From 63cf1e132abbf214afecbd803e407a1ed440dd73 Mon Sep 17 00:00:00 2001 From: Alchav Date: Tue, 5 Dec 2023 08:49:34 -0500 Subject: [PATCH 001/113] Super Mario Land 2! --- worlds/marioland2/__init__.py | 237 ++++++++++++++++++++++++++++ worlds/marioland2/basepatch.bsdiff4 | Bin 0 -> 392 bytes worlds/marioland2/client.py | 97 ++++++++++++ worlds/marioland2/options.py | 13 ++ worlds/marioland2/rom.py | 188 ++++++++++++++++++++++ worlds/marioland2/rom_addresses.py | 17 ++ 6 files changed, 552 insertions(+) create mode 100644 worlds/marioland2/__init__.py create mode 100644 worlds/marioland2/basepatch.bsdiff4 create mode 100644 worlds/marioland2/client.py create mode 100644 worlds/marioland2/options.py create mode 100644 worlds/marioland2/rom.py create mode 100644 worlds/marioland2/rom_addresses.py diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py new file mode 100644 index 000000000000..17ec56f11166 --- /dev/null +++ b/worlds/marioland2/__init__.py @@ -0,0 +1,237 @@ +import base64 +from typing import Dict, Any + +import Utils +from worlds.AutoWorld import World, WebWorld +from BaseClasses import Region, Location, Item, ItemClassification + +from . import client +from .rom import generate_output +from .options import sml2options + +locations = [ + "Mushroom Zone", #A848 + "Scenic Course", #A870 + "Tree Zone 1 - Invincibility!", #A849 + "Tree Zone 2 - In the Trees", #A84A + "Tree Zone 3 - The Exit", #A84C + "Tree Zone 4 - Honeybees", #A84B + "Tree Zone 5 - The Big Bird", #A84D + "Tree Zone - Secret Course", #A84B + "Hippo Zone", #A867 + "Space Zone 1 - Moon Stage", #A858 + "Space Zone 2 - Star Stage", #A859 + "Space Zone - Secret Course", #A871 + "Macro Zone 1 - The Ant Monsters", #A853 + "Macro Zone 2 - In the Syrup Sea", #A854 + "Macro Zone 3 - Fiery Mario-Special Agent", #A855 + "Macro Zone 4 - One Mighty Mouse", #A856 + "Macro Zone - Secret Course", #A86B + "Pumpkin Zone 1 - Bat Course", #A84E + "Pumpkin Zone 2 - Cyclops Course", #A84F + "Pumpkin Zone 3 - Ghost House", #A850 + "Pumpkin Zone 4 - Witch's Mansion", #A851 + "Pumpkin Zone - Secret Course 1", #A86E + "Pumpkin Zone - Secret Course 2", #A86F + "Mario Zone 1 - Fiery Blocks", #A862 + "Mario Zone 2 - Mario the Circus Star!", #A863 + "Mario Zone 3 - Beware: Jagged Spikes", #A864 + "Mario Zone 4 - Three Mean Pigs!", #A865 + "Turtle Zone 1 - Cheep Cheep Course", #A85D + "Turtle Zone 2 - Turtle Zone", #A85E + "Turtle Zone 3 - Whale Course", #A85F + "Turtle Zone - Secret Course", #A86D +] +locations = { + "Mushroom Zone": 0, + "Scenic Course": 40, + "Tree Zone 1 - Invincibility!": 1, + "Tree Zone 2 - In the Trees": 2, + "Tree Zone 3 - The Exit": 4, + "Tree Zone 4 - Honeybees": 3, + "Tree Zone 5 - The Big Bird": 5, + "Tree Zone - Secret Course": 3, + "Hippo Zone": 31, + "Space Zone 1 - Moon Stage": 16, + "Space Zone 2 - Star Stage": 17, + "Space Zone - Secret Course": 41, + "Macro Zone 1 - The Ant Monsters": 11, + "Macro Zone 2 - In the Syrup Sea": 12, + "Macro Zone 3 - Fiery Mario-Special Agent": 13, + "Macro Zone 4 - One Mighty Mouse": 14, + "Macro Zone - Secret Course": 35, + "Pumpkin Zone 1 - Bat Course": 6, + "Pumpkin Zone 2 - Cyclops Course": 7, + "Pumpkin Zone 3 - Ghost House": 8, + "Pumpkin Zone 4 - Witch's Mansion": 9, + "Pumpkin Zone - Secret Course 1": 38, + "Pumpkin Zone - Secret Course 2": 39, + "Mario Zone 1 - Fiery Blocks": 26, + "Mario Zone 2 - Mario the Circus Star!": 27, + "Mario Zone 3 - Beware: Jagged Spikes": 28, + "Mario Zone 4 - Three Mean Pigs!": 29, + "Turtle Zone 1 - Cheep Cheep Course": 21, + "Turtle Zone 2 - Turtle Zone": 22, + "Turtle Zone 3 - Whale Course": 23, + "Turtle Zone - Secret Course": 37, +} +locations = { + 'Mushroom Zone': {'ram_index': 0}, + 'Scenic Course': {'ram_index': 40}, + 'Tree Zone 1 - Invincibility!': {'ram_index': 1, 'clear_condition': ("Progressive Tree Zone", 1)}, + 'Tree Zone 2 - In the Trees': {'ram_index': 2, 'clear_condition': ("Progressive Tree Zone", 2)}, + 'Tree Zone 3 - The Exit': {'ram_index': 4, 'clear_condition': ("Progressive Tree Zone", 3)}, + 'Tree Zone 4 - Honeybees': {'ram_index': 3, 'clear_condition': ("Progressive Tree Zone", 3)}, + 'Tree Zone 5 - The Big Bird': {'ram_index': 5, 'clear_condition': ("Tree Coin", 1)}, + 'Tree Zone - Secret Course': {'ram_index': 3}, + 'Hippo Zone': {'ram_index': 31}, + 'Space Zone 1 - Moon Stage': {'ram_index': 16, 'clear_condition': ("Progressive Space Zone", 2)}, + 'Space Zone 2 - Star Stage': {'ram_index': 17, 'clear_condition': ("Space Coin", 1)}, + 'Space Zone - Secret Course': {'ram_index': 41}, + 'Macro Zone 1 - The Ant Monsters': {'ram_index': 11, 'clear_condition': ("Progressive Macro Zone", 1)}, + 'Macro Zone 2 - In the Syrup Sea': {'ram_index': 12, 'clear_condition': ("Progressive Macro Zone", 2)}, + 'Macro Zone 3 - Fiery Mario-Special Agent': {'ram_index': 13, 'clear_condition': ("Progressive Macro Zone", 3)}, + 'Macro Zone 4 - One Mighty Mouse': {'ram_index': 14, 'clear_condition': ("Macro Coin", 1)}, + 'Macro Zone - Secret Course': {'ram_index': 35, 'clear_condition': ("Macro Zone Secret", 1)}, + 'Pumpkin Zone 1 - Bat Course': {'ram_index': 6, 'clear_condition': ("Progressive Pumpkin Zone", 1)}, + 'Pumpkin Zone 2 - Cyclops Course': {'ram_index': 7, 'clear_condition': ("Progressive Pumpkin Zone", 2)}, + 'Pumpkin Zone 3 - Ghost House': {'ram_index': 8, 'clear_condition': ("Progressive Pumpkin Zone", 3)}, + "Pumpkin Zone 4 - Witch's Mansion": {'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1)}, + 'Pumpkin Zone - Secret Course 1': {'ram_index': 38}, + 'Pumpkin Zone - Secret Course 2': {'ram_index': 39}, + 'Mario Zone 1 - Fiery Blocks': {'ram_index': 26, 'clear_condition': ("Progressive Mario Zone", 1)}, + 'Mario Zone 2 - Mario the Circus Star!': {'ram_index': 27, 'clear_condition': ("Progressive Mario Zone", 2)}, + 'Mario Zone 3 - Beware: Jagged Spikes': {'ram_index': 28, 'clear_condition': ("Progressive Mario Zone", 3)}, + 'Mario Zone 4 - Three Mean Pigs!': {'ram_index': 29, 'clear_condition': ("Mario Coin", 1)}, + 'Turtle Zone 1 - Cheep Cheep Course': {'ram_index': 21, 'clear_condition': ("Progressive Turtle Zone", 1)}, + 'Turtle Zone 2 - Turtle Zone': {'ram_index': 22, 'clear_condition': ("Progressive Turtle Zone", 2)}, + 'Turtle Zone 3 - Whale Course': {'ram_index': 23, 'clear_condition': ("Turtle Coin", 1)}, + 'Turtle Zone - Secret Course': {'ram_index': 37}} + +items = [ + "Progressive Space Zone", + "Progressive Tree Zone", + "Progressive Macro Zone", + "Macro Zone Secret", + "Progressive Pumpkin Zone", + "Progressive Mario Zone", + "Progressive Turtle Zone", + "Tree Coin", + "Space Coin", + "Macro Coin", + "Pumpkin Coin", + "Mario Coin", + "Turtle Coin", + "Mushroom", + "Fire Flower", + "Carrot", + "Progressive Invincibility Star", + "Space Physics", + "Easy Mode", + "Normal Mode", +] +START_IDS = 7770000 +class MarioLand2World(World): + game = "Super Mario Land 2" + + location_name_to_id = {location_name: ID for ID, location_name in enumerate(locations, START_IDS)} + item_name_to_id = {item_name: ID for ID, item_name in enumerate(items, START_IDS)} + + option_definitions = sml2options + + generate_output = generate_output + + def create_regions(self): + menu_region = Region("Menu", self.player, self.multiworld) + self.multiworld.regions.append(menu_region) + for location_name in locations: + menu_region.locations.append(Location(self.player, location_name, self.location_name_to_id[location_name], menu_region)) + + def set_rules(self): + rules = { + "Space Zone 1 - Moon Stage": lambda state: state.has("Progressive Space Zone", self.player), # this is really hard though + "Space Zone - Secret Course": lambda state: state.has("Progressive Space Zone", self.player, 2) and state.has_any(["Space Physics", "Carrot"], self.player), + "Space Zone 2 - Star Stage": lambda state: state.has("Progressive Space Zone", self.player, 2) and state.has("Space Physics", self.player), + "Tree Zone 2 - In the Trees": lambda state: state.has("Progressive Tree Zone", self.player), + "Tree Zone - Secret Course": lambda state: state.has_all(["Progressive Tree Zone", "Carrot"], self.player), + "Tree Zone 3 - The Exit": lambda state: state.has("Progressive Tree Zone", self.player, 2), + "Tree Zone 4 - Honeybees": lambda state: state.has("Progressive Tree Zone", self.player, 2), + "Tree Zone 5 - The Big Bird": lambda state: state.has("Progressive Tree Zone", self.player, 3), + # You can use a Fire Flower to get here from Macro Zone 1, or if you have every Progressive Macro Zone and the Macro Zone Secret paths, you can get here from the boss levl + "Macro Zone - Secret Course": lambda state: state.has("Fire Flower", self.player) or (state.has("Macro Zone Secret", self.player) and state.has("Progressive Macro Zone", self.player, 3)), + "Macro Zone 2 - In the Syrup Sea": lambda state: state.has("Progressive Macro Zone", self.player), + "Macro Zone 3 - Fiery Mario-Special Agent": lambda state: state.has("Progressive Macro Zone", self.player, 2), + "Macro Zone 4 - One Mighty Mouse": lambda state: state.has("Progressive Macro Zone", self.player, 3) or (state.has("Fire Flower", self.player) and state.has("Macro Zone Secret", self.player)), + "Pumpkin Zone 2 - Cyclops Course": lambda state: state.has("Progressive Pumpkin Zone", self.player), + # You can only spin jump as Big Mario or Fire Mario + "Pumpkin Zone - Secret Course 1": lambda state: state.has("Progressive Pumpkin Zone", self.player) and state.has_any(["Mushroom", "Fire Flower"], self.player), + "Pumpkin Zone 3 - Ghost House": lambda state: state.has("Progressive Pumpkin Zone", self.player, 2), + "Pumpkin Zone - Secret Course 2": lambda state: state.has("Progressive Pumpkin Zone", self.player, 2) and state.has("Carrot", self.player), + "Pumpkin Zone 4 - Witch's Mansion": lambda state: state.has("Progressive Pumpkin Zone", self.player, 3), + "Mario Zone 2 - Mario the Circus Star!": lambda state: state.has("Progressive Mario Zone", self.player), + "Mario Zone 3 - Beware: Jagged Spikes": lambda state: state.has("Progressive Mario Zone", self.player, 2), + "Mario Zone 4 - Three Mean Pigs!": lambda state: state.has("Progressive Mario Zone", self.player, 3), + "Turtle Zone 2 - Turtle Zone": lambda state: state.has("Progressive Turtle Zone", self.player), + # The powerups are needed not for the secret exit in Turtle Zone 2, but to fly over or take damage in the spikes in the secret course + "Turtle Zone - Secret Course": lambda state: state.has("Progressive Turtle Zone", self.player) and state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player), + "Turtle Zone 3 - Whale Course": lambda state: state.has("Progressive Turtle Zone", self.player, 2), + } + + for level, rule in rules.items(): + self.multiworld.get_location(level, self.player).access_rule = rule + + self.multiworld.completion_condition[self.player] = lambda state: state.has_all(["Tree Coin", "Space Coin", + "Macro Coin", "Pumpkin Coin", "Mario Coin", "Turtle Coin",], self.player) + + def create_items(self): + item_counts = { + "Progressive Space Zone": 2, + "Progressive Tree Zone": 3, + "Progressive Macro Zone": 3, + "Macro Zone Secret": 1, + "Progressive Pumpkin Zone": 3, + "Progressive Mario Zone": 3, + "Progressive Turtle Zone": 2, + "Tree Coin": 1, + "Space Coin": 1, + "Macro Coin": 1, + "Pumpkin Coin": 1, + "Mario Coin": 1, + "Turtle Coin": 1, + "Mushroom": 1, + "Fire Flower": 1, + "Carrot": 1, + "Progressive Invincibility Star": 3, + "Space Physics": 1, + } + + if self.multiworld.difficulty_mode[self.player] == "easy_to_normal": + item_counts["Normal Mode"] = 1 + else: + item_counts["Easy Mode"] = 1 + + for item_name, count in item_counts.items(): + for _ in range(count): + classification = (ItemClassification.trap if item_name == "Normal Mode" + else ItemClassification.useful if item_name == "Easy Mode" + else ItemClassification.filler if item_name == "Progressive Invincibility Star" + else ItemClassification.progression) + self.multiworld.itempool.append(MarioLand2Item(item_name, classification, self.item_name_to_id[item_name], self.player)) + + def fill_slot_data(self): + return { + "mode": self.multiworld.difficulty_mode[self.player].value + } + + def get_filler_item_name(self): + return "Progressive Invincibility Star" + + 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.multiworld.player_name[self.player]] + +class MarioLand2Item(Item): + game = "Super Mario Land 2" \ No newline at end of file diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 new file mode 100644 index 0000000000000000000000000000000000000000..556d80f7c4828d0426d656ccddfc1166b0bd36c6 GIT binary patch literal 392 zcmZ%yNp#urzdxR11y_-~Zqc1A}JGxt|sd3XBX)3=9nn z0v-$uObS4S6B!g3LIO-KoH*HGa^3Nmba>lmtlrkS*vkD z$X{)F@gi4?fC(T25yo3S+SbXyz*oQgcafk$g94L-00#>r3kQReXoDjQheJmML*4?0 zE>2OPvraHBn`JB_7}VB~VRGbT6SISf^3&rJo~cPMD%kiixu7sf<(%W_V>4UJvtH8=umUDZie~Z9REk} OJeG3_N%}1{UH|}$KZ8sF literal 0 HcmV?d00001 diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py new file mode 100644 index 000000000000..117a81b60a76 --- /dev/null +++ b/worlds/marioland2/client.py @@ -0,0 +1,97 @@ +import base64 +import logging + +from NetUtils import ClientStatus +from worlds._bizhawk.client import BizHawkClient, BizHawkClientContext +from worlds._bizhawk import read, write, guarded_write + +logger = logging.getLogger("Client") + +from .rom_addresses import rom_addresses + +class MarioLand2Client(BizHawkClient): + system = ("GB", "SGB") + patch_suffix = ".apsml2" + game = "Super Mario Land 2" + + def __init__(self): + super().__init__() + self.locations_array = [] + + async def validate_rom(self, ctx): + game_name = await read(ctx.bizhawk_ctx, [(0x134, 10, "ROM")]) + game_name = game_name[0].decode("ascii") + if game_name == "MARIOLAND2": + ctx.game = self.game + ctx.items_handling = 0b111 + return True + return False + + async def set_auth(self, ctx): + auth_name = await read(ctx.bizhawk_ctx, [(0x77777, 21, "ROM")]) + auth_name = base64.b64encode(auth_name[0]).decode() + ctx.auth = auth_name + + async def game_watcher(self, ctx: BizHawkClientContext): + from . import locations, items, START_IDS + game_loaded_check, level_data, music = await read(ctx.bizhawk_ctx, [(0x0046, 10, "CartRAM"), (0x0848, 42, "CartRAM"), (0x0469, 1, "CartRAM")]) + if game_loaded_check != b'\x124Vx\xff\xff\xff\xff\xff\xff': + return + + level_data = list(level_data) + + items_received = [items[item.item - START_IDS] for item in ctx.items_received] + locations_checked = [] + modified_level_data = level_data.copy() + for ID, (location, data) in enumerate(locations.items(), START_IDS): + if "clear_condition" in data: + if items_received.count(data["clear_condition"][0]) >= data["clear_condition"][1]: + modified_level_data[data["ram_index"]] |= 0x80 + if level_data[data["ram_index"]] & 0x41: + locations_checked.append(ID) + + invincibility_length = items_received.count("Progressive Invincibility Star") + if invincibility_length > 0: + invincibility_length = min(invincibility_length + 1, 255) + + if "Normal Mode" in items_received: + difficulty_mode = 0 + elif "Easy Mode" in items_received: + difficulty_mode = 1 + elif ctx.slot_data: + difficulty_mode = ctx.slot_data["mode"] + else: + difficulty_mode = 0 + + data_writes = [ + (rom_addresses["Space_Physics"], [0xea, 0x87, 0xa2] if "Space Physics" in items_received else [0, 0, 0], "ROM"), + (rom_addresses["Get_Hurt_To_Big_Mario"], [1] if "Mushroom" in items_received else [0], "ROM"), + (rom_addresses["Get_Mushroom_A"], [0xea, 0x16, 0xa2] if "Mushroom" in items_received else [0, 0, 0], "ROM"), + (rom_addresses["Get_Mushroom_B"], [0xea, 0x16, 0xa2] if "Mushroom" in items_received else [0, 0, 0], "ROM"), + (rom_addresses["Get_Mushroom_C"], [00] if "Mushroom" in items_received else [0xd8], "ROM"), + (rom_addresses["Get_Carrot_A"], [0xea, 0x16, 0xa2] if "Carrot" in items_received else [0, 0, 0], "ROM"), + (rom_addresses["Get_Carrot_B"], [0xea, 0x16, 0xa2] if "Carrot" in items_received else [0, 0, 0], "ROM"), + (rom_addresses["Get_Carrot_C"], [00] if "Mushroom" in items_received else [0xc8], "ROM"), + (rom_addresses["Get_Fire_Flower_A"], [0xea, 0x16, 0xa2] if "Fire Flower" in items_received else [0, 0, 0], "ROM"), + (rom_addresses["Get_Fire_Flower_B"], [0xea, 0x16, 0xa2] if "Fire Flower" in items_received else [0, 0, 0], "ROM"), + (rom_addresses["Get_Fire_Flower_C"], [00] if "Mushroom" in items_received else [0xc8], "ROM"), + (rom_addresses["Invincibility_Star_A"], [invincibility_length], "ROM"), + (rom_addresses["Invincibility_Star_B"], [64] if "Progressive Invincibility Star" in items_received else [0], "ROM"), + (rom_addresses["Invincibility_Star_C"], [4] if "Progressive Invincibility Star" in items_received else [0], "ROM"), + (rom_addresses["Enable_Bubble"], [0xcb, 0xd7] if "Progressive Space Zone" in items_received else [0, 0], "ROM"), + (0x02E4, [difficulty_mode], "CartRAM"), + (0x0848, modified_level_data, "CartRAM") + ] + + success = await guarded_write(ctx.bizhawk_ctx, data_writes, [(0x0848, level_data, "CartRAM")]) + + if not ctx.server or not ctx.server.socket.open or ctx.server.socket.closed: + return + + if locations_checked and locations_checked != self.locations_array: + self.locations_array = locations_checked + await ctx.send_msgs([{"cmd": "LocationChecks", "locations": locations_checked}]) + + if music == 0x18: + await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) + ctx.finished_game = True \ No newline at end of file diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py new file mode 100644 index 000000000000..cb045ccf45b0 --- /dev/null +++ b/worlds/marioland2/options.py @@ -0,0 +1,13 @@ +from Options import Toggle, Choice, Range, NamedRange, TextChoice, DeathLink + +class DifficultyMode(Choice): + """Start in Normal Mode with an "upgrade" to Easy Mode in the item pool, + or start in Easy Mode with a Normal Mode "Trap" in the item pool""" + display_name = "Difficulty Mode" + option_normal_to_easy = 0 + option_easy_to_normal = 1 + default = 0 + +sml2options = { + "difficulty_mode": DifficultyMode +} \ No newline at end of file diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py new file mode 100644 index 000000000000..aa0059e2c225 --- /dev/null +++ b/worlds/marioland2/rom.py @@ -0,0 +1,188 @@ +import hashlib +import os +import pkgutil +import bsdiff4 + +import Utils + +from Patch import APDeltaPatch +from settings import get_settings + +# Enemy randomizer ported directly from SML2R +# https://github.com/slashinfty/sml2r-node/blob/862128c73d336d6cbfbf6290c09f3eff103688e8/src/index.ts#L284 + +def sprite_extract(a, b): + x = ((0b00010000 & a) << 2) + y = ((0b11100000 & a) >> 2) + z = ((0b11100000 & b) >> 5) + return x | y | z + +def sprite_insert(a, b, s): + x = ((s & 0b01000000) >> 2) + y = ((s & 0b00111000) << 2) + z = ((s & 0b00000111) << 5) + return [(a & 0b00001111) | x | y, (b & 0b00011111) | z] + +def randomize_enemies(data, random): + + def copy_sprite(arr, pos): + for i in range(2): + data[pos + i] = arr[i] + + def randomize_sprite(arr, i): + selected_sprite = sprite_insert(data[i], data[i + 1], random.choice(arr)) + copy_sprite(selected_sprite, i) + + level_list = [ + {"enemies": [0x01, 0x08, 0x09, 0x3A], "start": 0xE077, "end": 0xE0BC}, # lv00 + {"enemies": [0x01, 0x08, 0x09, 0x3A], "start": 0xE955, "end": 0xE99D}, # lv17 + {"enemies": [0x08, 0x09, 0x3A], "start": 0xEA2F, "end": 0xEA7D}, # lv19 + {"enemies": [0x08, 0x09, 0x3A], "start": 0xEAA3, "end": 0xEACD}, # lv1B + {"enemies": [0x1F, 0x20, 0x21, 0x22], "start": 0xE0BD, "end": 0xE123}, # lv01 + {"enemies": [0x44, 0x58], "start": 0xE124, "end": 0xE181}, # lv02 + {"enemies": [0x35, 0x3E, 0x40, 0x41, 0x42], "start": 0xE182, "end": 0xE1EE}, # lv03 + {"enemies": [0x33, 0x34, 0x5D], "start": 0xE1EF, "end": 0xE249}, # lv04 + {"enemies": [0x08, 0x39, 0x3A], "start": 0xE24A, "end": 0xE2A1}, # lv05 + {"enemies": [0x4D, 0x54, 0x55, 0x56, 0x5E, 0x5F], "start": 0xE30C, "end": 0xE384}, # lv07 + {"enemies": [0x4D, 0x57], "start": 0xE385, "end": 0xE3D3}, # lv08 + {"enemies": [0x01, 0x40, 0x4B], "start": 0xE432, "end": 0xE49B}, # lv0A + {"enemies": [0x08, 0x09, 0x3A, 0x44, 0x4D], "start": 0xE49C, "end": 0xE4F9}, # lv0B + {"enemies": [0x05, 0x06, 0x07, 0x08, 0x09, 0x0B, 0x3A, 0x3D], "start": 0xE5C2, "end": 0xE62B}, # lv0E + {"enemies": [0x05, 0x39, 0x57, 0x5B], "start": 0xE706, "end": 0xE77B}, # lv11 + {"enemies": [0x5C, 0x5E, 0x5F], "start": 0xE7C8, "end": 0xE822}, # lv13 + {"enemies": [0x22, 0x23, 0x25, 0x27], "start": 0xE823, "end": 0xE88F}, # lv14 + {"enemies": [0x07, 0x33, 0x34, 0x3D, 0x5D], "start": 0xE890, "end": 0xE8F6}, # lv15 + {"enemies": [0x01, 0x08, 0x09, 0x34, 0x3A, 0x55], "start": 0xE8F7, "end": 0xE954}, # lv16 + {"enemies": [0x68, 0x69], "start": 0xE99E, "end": 0xEA2E}, # lv18a + {"enemies": [0x6E, 0x6F], "start": 0xE99E, "end": 0xEA2E}, # lv18b + {"enemies": [0x01, 0x09], "start": 0xEB55, "end": 0xEBB5} # lv1F + ] + for level in level_list: + i = level["start"] + while i < level["end"]: + sprite = sprite_extract(data[i], data[i+1]) + if data[i] == 0xFF: + i -= 2 + elif sprite in level["enemies"]: + randomize_sprite(level["enemies"], i) + i += 3 + for i in range(0xE2A2, 0xE30B, 3): # lvl06 + sprite = sprite_extract(data[i], data[i+1]) + if sprite == 0x4E: + randomize_sprite([0x4D, 0x4E, 0x51, 0x53], i) + elif sprite == 0x4F: + randomize_sprite([0x4D, 0x4F, 0x51, 0x53], i) + elif sprite in (0x4D, 0x51, 0x53): + randomize_sprite([0x4D, 0x51, 0x53], i) + for i in range(0xE3D4, 0xE431, 3): # lvl09 + sprite = sprite_extract(data[i], data[i + 1]) + if sprite == 0x4F: + randomize_sprite([0x4D, 0x4F, 0x53, 0x5A, 0x5C], i) + elif sprite in (0x4D, 0x53, 0x5a, 0x5C): + randomize_sprite([0x4D, 0x53, 0x5A, 0x5C], i) + for i in range(0xE4FA, 0xE560, 3): # lvl0c + sprite = sprite_extract(data[i], data[i + 1]) + if sprite == 0x49: + randomize_sprite([0x01, 0x47, 0x48, 0x49, 0x53], i) + elif sprite in (0x01, 0x47, 0x48): + randomize_sprite([0x01, 0x47, 0x48, 0x53], i) + for i in range(0xE561, 0xE5C1, 3): # lvl0D + sprite = sprite_extract(data[i], data[i + 1]) + if sprite == 0x43: + randomize_sprite([0x09, 0x43, 0x4D, 0x53], i) + elif sprite == 0x4C: + randomize_sprite([0x09, 0x4C, 0x4D, 0x53], i) + elif sprite in (0x09, 0x4D): + randomize_sprite([0x09, 0x4D, 0x53], i) + for i in range(0xE6C0, 0xE705, 3): # lvl10 + sprite = sprite_extract(data[i], data[i + 1]) + if sprite == 0x21: + randomize_sprite([0x01, 0x08, 0x20, 0x21, 0x3A, 0x55], i) + elif sprite in (0x01, 0x08, 0x20, 0x3A, 0x55): + randomize_sprite([0x01, 0x08, 0x20, 0x3A, 0x55], i) + for i in range(0xE77C, 0xE7C7, 3): # lvl12 + sprite = sprite_extract(data[i], data[i + 1]) + if sprite == 0x4D: + randomize_sprite([0x4D, 0x58], i) + elif sprite in (0x58, 0x5A): + randomize_sprite([0x4D, 0x58, 0x5A], i) + # Thwomps in Wario's Castle + for i in [0xE9D6, 0xE9D9, 0xE9DF, 0xE9E2, 0xE9E5]: + data[i] = 0x34 if random.randint(0, 9) else 0x35 + # Piranha plants + i = 0xE077 + while i < 0xEBB5: + if i < 0xE30C or 0xE384 < i < 0xE3D4 or 0xE431 < i < 0xE8F7 or i > 0xE954: + sprite = sprite_extract(data[i], data[i + 1]) + if sprite == 0xFF: + i -= 2 + elif sprite in (0x0C,0x0D): + copy_sprite(sprite_insert(data[i], data[i+1], random.choice([0x0C, 0x0D])), i) + i += 3 + + + + + + + + + +def generate_output(self, output_directory: str): + data = get_base_rom_bytes() + base_patch = pkgutil.get_data(__name__, f'basepatch.bsdiff4') + + data = bytearray(bsdiff4.patch(data, base_patch)) + + randomize_enemies(data, self.multiworld.per_slot_randoms[self.player]) + + 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))) + write_bytes(data, rom_name, 0x77777) + + outfilepname = f'_P{self.player}' + outfilepname += f"_{self.multiworld.get_file_safe_player_name(self.player).replace(' ', '_')}" \ + if self.multiworld.player_name[self.player] != 'Player%d' % self.player else '' + rompath = os.path.join(output_directory, f'AP_{self.multiworld.seed_name}{outfilepname}.gb') + with open(rompath, 'wb') as outfile: + outfile.write(data) + patch = SuperMarioLand2DeltaPatch(os.path.splitext(rompath)[0] + SuperMarioLand2DeltaPatch.patch_file_ending, + player=self.player, player_name=self.multiworld.player_name[self.player], + patched_path=rompath) + patch.write() + os.unlink(rompath) + +class SuperMarioLand2DeltaPatch(APDeltaPatch): + hash = "a8413347d5df8c9d14f97f0330d67bce" + patch_file_ending = ".apsml2" + game = "Super Mario Land 2" + result_file_ending = ".gb" + @classmethod + def get_source_data(cls) -> bytes: + return get_base_rom_bytes() + +def get_base_rom_bytes(): + file_name = get_base_rom_path() + with open(file_name, "rb") as file: + base_rom_bytes = bytes(file.read()) + + basemd5 = hashlib.md5() + basemd5.update(base_rom_bytes) + if SuperMarioLand2DeltaPatch.hash != basemd5.hexdigest(): + raise Exception("Supplied Base Rom does not match known MD5 for Super Mario Land 1.0. " + "Get the correct game and version, then dump it") + return base_rom_bytes + +def get_base_rom_path(): + file_name = "C:\\src\\marioland2\\baserom.gb" + if not file_name: + file_name = get_settings()["sml2_options"]["rom_file"] + if not os.path.exists(file_name): + file_name = Utils.user_path(file_name) + return file_name + +def write_bytes(data, byte_array, address): + for byte in byte_array: + data[address] = byte + address += 1 \ No newline at end of file diff --git a/worlds/marioland2/rom_addresses.py b/worlds/marioland2/rom_addresses.py new file mode 100644 index 000000000000..a8ddaf188288 --- /dev/null +++ b/worlds/marioland2/rom_addresses.py @@ -0,0 +1,17 @@ +rom_addresses = { + "Space_Physics": 0x4e8, + "Get_Hurt_To_Big_Mario": 0x31c7, + "Get_Mushroom_A": 0x345c, + "Get_Fire_Flower_A": 0x346d, + "Get_Carrot_A": 0x347e, + "Invincibility_Star_A": 0x349e, + "Invincibility_Star_B": 0x34a3, + "Invincibility_Star_C": 0x34a8, + "Enable_Bubble": 0x34e5, + "Get_Carrot_C": 0x6092f, + "Get_Mushroom_C": 0x60930, + "Get_Fire_Flower_C": 0x60933, + "Get_Mushroom_B": 0x60ddb, + "Get_Carrot_B": 0x60de7, + "Get_Fire_Flower_B": 0x60df3, +} From 08b27a4dc1474dd8c2f71e35bc9df69abdf2f565 Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 9 Dec 2023 10:08:45 -0500 Subject: [PATCH 002/113] Bring in features from SML2R --- worlds/marioland2/__init__.py | 44 ++++++++-------- worlds/marioland2/basepatch.bsdiff4 | Bin 392 -> 404 bytes worlds/marioland2/options.py | 30 ++++++++++- worlds/marioland2/rom.py | 79 ++++++++++++++++++---------- 4 files changed, 100 insertions(+), 53 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 17ec56f11166..3ff9b889fc8f 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -108,28 +108,28 @@ 'Turtle Zone 3 - Whale Course': {'ram_index': 23, 'clear_condition': ("Turtle Coin", 1)}, 'Turtle Zone - Secret Course': {'ram_index': 37}} -items = [ - "Progressive Space Zone", - "Progressive Tree Zone", - "Progressive Macro Zone", - "Macro Zone Secret", - "Progressive Pumpkin Zone", - "Progressive Mario Zone", - "Progressive Turtle Zone", - "Tree Coin", - "Space Coin", - "Macro Coin", - "Pumpkin Coin", - "Mario Coin", - "Turtle Coin", - "Mushroom", - "Fire Flower", - "Carrot", - "Progressive Invincibility Star", - "Space Physics", - "Easy Mode", - "Normal Mode", -] +items = { + "Progressive Space Zone": ItemClassification.progression, + "Progressive Tree Zone": ItemClassification.progression, + "Progressive Macro Zone": ItemClassification.progression, + "Macro Zone Secret": ItemClassification.progression_skip_balancing, + "Progressive Pumpkin Zone": ItemClassification.progression, + "Progressive Mario Zone": ItemClassification.progression, + "Progressive Turtle Zone": ItemClassification.progression, + "Tree Coin": ItemClassification.progression_skip_balancing, + "Space Coin": ItemClassification.progression_skip_balancing, + "Macro Coin": ItemClassification.progression_skip_balancing, + "Pumpkin Coin": ItemClassification.progression_skip_balancing, + "Mario Coin": ItemClassification.progression_skip_balancing, + "Turtle Coin": ItemClassification.progression_skip_balancing, + "Mushroom": ItemClassification.progression, + "Fire Flower": ItemClassification.progression, + "Carrot": ItemClassification.progression, + "Progressive Invincibility Star": ItemClassification.filler, + "Space Physics": ItemClassification.progression, + "Easy Mode": ItemClassification.useful, + "Normal Mode": ItemClassification.trap, +} START_IDS = 7770000 class MarioLand2World(World): game = "Super Mario Land 2" diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index 556d80f7c4828d0426d656ccddfc1166b0bd36c6..0e9869612fc37d3071dfb93d665b5675859f398d 100644 GIT binary patch literal 404 zcmZ%yNp#urzdxR11zgn2V+%P1KHJdn46VOzpf|DZ~T3|Z9E|f?>au zl5$JfkHk;C>Y{QYbpHCMRW+)muRbi>%*fg}Bj&4o00RTtvTwGz2e z@ZmM?|C|gA%=OoAC@?fAFgP$WumDMh21X_Z76u^0fkA+QQGvnGXU0r6HxY&lHnSQX UXRH0WRm8!t3mRk_;(vGm0Q=E`*8l(j literal 392 zcmZ%yNp#urzdxR11y_-~Zqc1A}JGxt|sd3XBX)3=9nn z0v-$uObS4S6B!g3LIO-KoH*HGa^3Nmba>lmtlrkS*vkD z$X{)F@gi4?fC(T25yo3S+SbXyz*oQgcafk$g94L-00#>r3kQReXoDjQheJmML*4?0 zE>2OPvraHBn`JB_7}VB~VRGbT6SISf^3&rJo~cPMD%kiixu7sf<(%W_V>4UJvtH8=umUDZie~Z9REk} OJeG3_N%}1{UH|}$KZ8sF diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index cb045ccf45b0..a0ee893123f1 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -1,13 +1,39 @@ from Options import Toggle, Choice, Range, NamedRange, TextChoice, DeathLink + class DifficultyMode(Choice): """Start in Normal Mode with an "upgrade" to Easy Mode in the item pool, - or start in Easy Mode with a Normal Mode "Trap" in the item pool""" + or start in Easy Mode with a Normal Mode "Trap" in the item pool.""" display_name = "Difficulty Mode" option_normal_to_easy = 0 option_easy_to_normal = 1 default = 0 +class ShuffleSpacePhysics(Toggle): + """Oh, no! There is Earth gravity on the moon and in space! Find the missing Space Physics item to restore + proper order to the universe.""" + display_name = "Shuffle Space Physics" + + +class RandomizeEnemies(Toggle): + """Randomize enemies throughout levels.""" + display_name = "Randomize Enemies" + + +class RandomizePlatforms(Toggle): + """Randomize platforms throughout levels.""" + display_name = "Randomize Platforms" + + +class RandomizeAutoScrollLevels(Toggle): + """Randomize which three levels have auto-scrolling.""" + display_name = "Randomize Auto Scroll Levels" + + sml2options = { - "difficulty_mode": DifficultyMode + "difficulty_mode": DifficultyMode, + "shuffle_space_physics": ShuffleSpacePhysics, + "randomize_enemies": RandomizeEnemies, + "randomize_platforms": RandomizePlatforms, + "randomize_auto_scroll_levels": RandomizeAutoScrollLevels, } \ No newline at end of file diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index aa0059e2c225..84940aad5416 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -23,15 +23,15 @@ def sprite_insert(a, b, s): z = ((s & 0b00000111) << 5) return [(a & 0b00001111) | x | y, (b & 0b00011111) | z] -def randomize_enemies(data, random): +def copy_sprite(data, arr, pos): + for i in range(2): + data[pos + i] = arr[i] - def copy_sprite(arr, pos): - for i in range(2): - data[pos + i] = arr[i] +def randomize_sprite(data, random, arr, i): + selected_sprite = sprite_insert(data[i], data[i + 1], random.choice(arr)) + copy_sprite(data, selected_sprite, i) - def randomize_sprite(arr, i): - selected_sprite = sprite_insert(data[i], data[i + 1], random.choice(arr)) - copy_sprite(selected_sprite, i) +def randomize_enemies(data, random): level_list = [ {"enemies": [0x01, 0x08, 0x09, 0x3A], "start": 0xE077, "end": 0xE0BC}, # lv00 @@ -64,48 +64,48 @@ def randomize_sprite(arr, i): if data[i] == 0xFF: i -= 2 elif sprite in level["enemies"]: - randomize_sprite(level["enemies"], i) + randomize_sprite(data, random, level["enemies"], i) i += 3 for i in range(0xE2A2, 0xE30B, 3): # lvl06 sprite = sprite_extract(data[i], data[i+1]) if sprite == 0x4E: - randomize_sprite([0x4D, 0x4E, 0x51, 0x53], i) + randomize_sprite(data, random, [0x4D, 0x4E, 0x51, 0x53], i) elif sprite == 0x4F: - randomize_sprite([0x4D, 0x4F, 0x51, 0x53], i) + randomize_sprite(data, random, [0x4D, 0x4F, 0x51, 0x53], i) elif sprite in (0x4D, 0x51, 0x53): - randomize_sprite([0x4D, 0x51, 0x53], i) + randomize_sprite(data, random, [0x4D, 0x51, 0x53], i) for i in range(0xE3D4, 0xE431, 3): # lvl09 sprite = sprite_extract(data[i], data[i + 1]) if sprite == 0x4F: - randomize_sprite([0x4D, 0x4F, 0x53, 0x5A, 0x5C], i) + randomize_sprite(data, random, [0x4D, 0x4F, 0x53, 0x5A, 0x5C], i) elif sprite in (0x4D, 0x53, 0x5a, 0x5C): - randomize_sprite([0x4D, 0x53, 0x5A, 0x5C], i) + randomize_sprite(data, random, [0x4D, 0x53, 0x5A, 0x5C], i) for i in range(0xE4FA, 0xE560, 3): # lvl0c sprite = sprite_extract(data[i], data[i + 1]) if sprite == 0x49: - randomize_sprite([0x01, 0x47, 0x48, 0x49, 0x53], i) + randomize_sprite(data, random, [0x01, 0x47, 0x48, 0x49, 0x53], i) elif sprite in (0x01, 0x47, 0x48): - randomize_sprite([0x01, 0x47, 0x48, 0x53], i) + randomize_sprite(data, random, [0x01, 0x47, 0x48, 0x53], i) for i in range(0xE561, 0xE5C1, 3): # lvl0D sprite = sprite_extract(data[i], data[i + 1]) if sprite == 0x43: - randomize_sprite([0x09, 0x43, 0x4D, 0x53], i) + randomize_sprite(data, random, [0x09, 0x43, 0x4D, 0x53], i) elif sprite == 0x4C: - randomize_sprite([0x09, 0x4C, 0x4D, 0x53], i) + randomize_sprite(data, random, [0x09, 0x4C, 0x4D, 0x53], i) elif sprite in (0x09, 0x4D): - randomize_sprite([0x09, 0x4D, 0x53], i) + randomize_sprite(data, random, [0x09, 0x4D, 0x53], i) for i in range(0xE6C0, 0xE705, 3): # lvl10 sprite = sprite_extract(data[i], data[i + 1]) if sprite == 0x21: - randomize_sprite([0x01, 0x08, 0x20, 0x21, 0x3A, 0x55], i) + randomize_sprite(data, random, [0x01, 0x08, 0x20, 0x21, 0x3A, 0x55], i) elif sprite in (0x01, 0x08, 0x20, 0x3A, 0x55): - randomize_sprite([0x01, 0x08, 0x20, 0x3A, 0x55], i) + randomize_sprite(data, random, [0x01, 0x08, 0x20, 0x3A, 0x55], i) for i in range(0xE77C, 0xE7C7, 3): # lvl12 sprite = sprite_extract(data[i], data[i + 1]) if sprite == 0x4D: - randomize_sprite([0x4D, 0x58], i) + randomize_sprite(data, random, [0x4D, 0x58], i) elif sprite in (0x58, 0x5A): - randomize_sprite([0x4D, 0x58, 0x5A], i) + randomize_sprite(data, random, [0x4D, 0x58, 0x5A], i) # Thwomps in Wario's Castle for i in [0xE9D6, 0xE9D9, 0xE9DF, 0xE9E2, 0xE9E5]: data[i] = 0x34 if random.randint(0, 9) else 0x35 @@ -117,16 +117,30 @@ def randomize_sprite(arr, i): if sprite == 0xFF: i -= 2 elif sprite in (0x0C,0x0D): - copy_sprite(sprite_insert(data[i], data[i+1], random.choice([0x0C, 0x0D])), i) + copy_sprite(data, sprite_insert(data[i], data[i+1], random.choice([0x0C, 0x0D])), i) i += 3 +def randomize_auto_scroll_levels(data, random): + for i in random.sample([0, 1, 2, 3, 5, 8, 9, 10, 11, 13, 14, 16, 17, 18, 19, 20, 23, 25, 30, 31], 3): + data[0x1F71 + i] = 1 #int(random.randint(0, 99) < (8 if data[0x1F71 +1] == 0 else 25)) - - - - - +def randomize_platforms(data, random): + level_list = [ + {"platforms": [0x28, 0x29, 0x2A, 0x2B, 0x2D, 0x2E], "start": 0xE1EF, "end": 0xE249}, + {"platforms": [0x38, 0x3D], "start": 0xE24A, "end": 0xE2A1}, + {"platforms": [0x60, 0x61, 0x67], "start": 0xE99E, "end": 0xEA2E} + ] + for level in level_list: + i = level["start"] + while i < level["end"]: + sprite = sprite_extract(data[i], data[i + 1]) + if sprite == 0xFF: + i -= 2 + elif sprite in level["platforms"]: + randomize_sprite(data, random, level["platforms"], i) + for i in range(0xE9A3, 0xE9CE, 3): + data[i] = (0x57 if data[i] == 0x5E else 0x38) + random.randint(0, 7) def generate_output(self, output_directory: str): data = get_base_rom_bytes() @@ -134,7 +148,14 @@ def generate_output(self, output_directory: str): data = bytearray(bsdiff4.patch(data, base_patch)) - randomize_enemies(data, self.multiworld.per_slot_randoms[self.player]) + random = self.multiworld.per_slot_randoms[self.player] + + if self.multiworld.randomize_enemies[self.player]: + randomize_enemies(data, random) + if self.multiworld.randomize_platforms[self.player]: + randomize_platforms(data, random) + if self.multiworld.randomize_auto_scroll_levels[self.player]: + randomize_auto_scroll_levels(data, random) rom_name = bytearray(f'AP{Utils.__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed:11}\0', 'utf8')[:21] From df5b3e140a7f6694739aecef257e255c4ff66e2e Mon Sep 17 00:00:00 2001 From: Alchav Date: Sun, 10 Dec 2023 11:32:23 -0500 Subject: [PATCH 003/113] Many things --- worlds/marioland2/__init__.py | 142 +++++++++++----------------- worlds/marioland2/basepatch.bsdiff4 | Bin 404 -> 399 bytes worlds/marioland2/client.py | 46 ++++++--- worlds/marioland2/options.py | 41 ++++++-- worlds/marioland2/rom.py | 32 +++++-- 5 files changed, 147 insertions(+), 114 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 3ff9b889fc8f..1ff1025929eb 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -1,5 +1,4 @@ import base64 -from typing import Dict, Any import Utils from worlds.AutoWorld import World, WebWorld @@ -9,72 +8,6 @@ from .rom import generate_output from .options import sml2options -locations = [ - "Mushroom Zone", #A848 - "Scenic Course", #A870 - "Tree Zone 1 - Invincibility!", #A849 - "Tree Zone 2 - In the Trees", #A84A - "Tree Zone 3 - The Exit", #A84C - "Tree Zone 4 - Honeybees", #A84B - "Tree Zone 5 - The Big Bird", #A84D - "Tree Zone - Secret Course", #A84B - "Hippo Zone", #A867 - "Space Zone 1 - Moon Stage", #A858 - "Space Zone 2 - Star Stage", #A859 - "Space Zone - Secret Course", #A871 - "Macro Zone 1 - The Ant Monsters", #A853 - "Macro Zone 2 - In the Syrup Sea", #A854 - "Macro Zone 3 - Fiery Mario-Special Agent", #A855 - "Macro Zone 4 - One Mighty Mouse", #A856 - "Macro Zone - Secret Course", #A86B - "Pumpkin Zone 1 - Bat Course", #A84E - "Pumpkin Zone 2 - Cyclops Course", #A84F - "Pumpkin Zone 3 - Ghost House", #A850 - "Pumpkin Zone 4 - Witch's Mansion", #A851 - "Pumpkin Zone - Secret Course 1", #A86E - "Pumpkin Zone - Secret Course 2", #A86F - "Mario Zone 1 - Fiery Blocks", #A862 - "Mario Zone 2 - Mario the Circus Star!", #A863 - "Mario Zone 3 - Beware: Jagged Spikes", #A864 - "Mario Zone 4 - Three Mean Pigs!", #A865 - "Turtle Zone 1 - Cheep Cheep Course", #A85D - "Turtle Zone 2 - Turtle Zone", #A85E - "Turtle Zone 3 - Whale Course", #A85F - "Turtle Zone - Secret Course", #A86D -] -locations = { - "Mushroom Zone": 0, - "Scenic Course": 40, - "Tree Zone 1 - Invincibility!": 1, - "Tree Zone 2 - In the Trees": 2, - "Tree Zone 3 - The Exit": 4, - "Tree Zone 4 - Honeybees": 3, - "Tree Zone 5 - The Big Bird": 5, - "Tree Zone - Secret Course": 3, - "Hippo Zone": 31, - "Space Zone 1 - Moon Stage": 16, - "Space Zone 2 - Star Stage": 17, - "Space Zone - Secret Course": 41, - "Macro Zone 1 - The Ant Monsters": 11, - "Macro Zone 2 - In the Syrup Sea": 12, - "Macro Zone 3 - Fiery Mario-Special Agent": 13, - "Macro Zone 4 - One Mighty Mouse": 14, - "Macro Zone - Secret Course": 35, - "Pumpkin Zone 1 - Bat Course": 6, - "Pumpkin Zone 2 - Cyclops Course": 7, - "Pumpkin Zone 3 - Ghost House": 8, - "Pumpkin Zone 4 - Witch's Mansion": 9, - "Pumpkin Zone - Secret Course 1": 38, - "Pumpkin Zone - Secret Course 2": 39, - "Mario Zone 1 - Fiery Blocks": 26, - "Mario Zone 2 - Mario the Circus Star!": 27, - "Mario Zone 3 - Beware: Jagged Spikes": 28, - "Mario Zone 4 - Three Mean Pigs!": 29, - "Turtle Zone 1 - Cheep Cheep Course": 21, - "Turtle Zone 2 - Turtle Zone": 22, - "Turtle Zone 3 - Whale Course": 23, - "Turtle Zone - Secret Course": 37, -} locations = { 'Mushroom Zone': {'ram_index': 0}, 'Scenic Course': {'ram_index': 40}, @@ -83,7 +16,7 @@ 'Tree Zone 3 - The Exit': {'ram_index': 4, 'clear_condition': ("Progressive Tree Zone", 3)}, 'Tree Zone 4 - Honeybees': {'ram_index': 3, 'clear_condition': ("Progressive Tree Zone", 3)}, 'Tree Zone 5 - The Big Bird': {'ram_index': 5, 'clear_condition': ("Tree Coin", 1)}, - 'Tree Zone - Secret Course': {'ram_index': 3}, + 'Tree Zone - Secret Course': {'ram_index': 36}, 'Hippo Zone': {'ram_index': 31}, 'Space Zone 1 - Moon Stage': {'ram_index': 16, 'clear_condition': ("Progressive Space Zone", 2)}, 'Space Zone 2 - Star Stage': {'ram_index': 17, 'clear_condition': ("Space Coin", 1)}, @@ -137,6 +70,12 @@ class MarioLand2World(World): location_name_to_id = {location_name: ID for ID, location_name in enumerate(locations, START_IDS)} item_name_to_id = {item_name: ID for ID, item_name in enumerate(items, START_IDS)} + item_name_groups = { + "Coins": {item_name for item_name in items if "Coin" in item_name}, + "Powerups": {"Mushroom", "Fire Flower", "Carrot"}, + "Difficulties": {"Easy Mode", "Normal Mode"} + } + option_definitions = sml2options generate_output = generate_output @@ -157,7 +96,8 @@ def set_rules(self): "Tree Zone 3 - The Exit": lambda state: state.has("Progressive Tree Zone", self.player, 2), "Tree Zone 4 - Honeybees": lambda state: state.has("Progressive Tree Zone", self.player, 2), "Tree Zone 5 - The Big Bird": lambda state: state.has("Progressive Tree Zone", self.player, 3), - # You can use a Fire Flower to get here from Macro Zone 1, or if you have every Progressive Macro Zone and the Macro Zone Secret paths, you can get here from the boss levl + # You can use a Fire Flower to get here from Macro Zone 1, or if you have every Progressive Macro Zone and + # the Macro Zone Secret paths, you can get here from the boss level "Macro Zone - Secret Course": lambda state: state.has("Fire Flower", self.player) or (state.has("Macro Zone Secret", self.player) and state.has("Progressive Macro Zone", self.player, 3)), "Macro Zone 2 - In the Syrup Sea": lambda state: state.has("Progressive Macro Zone", self.player), "Macro Zone 3 - Fiery Mario-Special Agent": lambda state: state.has("Progressive Macro Zone", self.player, 2), @@ -180,8 +120,14 @@ def set_rules(self): for level, rule in rules.items(): self.multiworld.get_location(level, self.player).access_rule = rule - self.multiworld.completion_condition[self.player] = lambda state: state.has_all(["Tree Coin", "Space Coin", - "Macro Coin", "Pumpkin Coin", "Mario Coin", "Turtle Coin",], self.player) + if self.multiworld.golden_coins[self.player] == "progressive": + self.multiworld.completion_condition[self.player] = lambda state: ( + state.has("Progressive Space Zone", self.player, 3) and state.has("Progressive Tree Zone", self.player, 4) + and state.has("Progressive Macro Zone", self.player, 4) and state.has("Progressive Pumpkin Zone", self.player, 4) + and state.has("Progressive Mario Zone", self.player, 4) and state.has("Progressive Turtle Zone", self.player, 3)) + else: + self.multiworld.completion_condition[self.player] = lambda state: state.has_all(["Tree Coin", "Space Coin", + "Macro Coin", "Pumpkin Coin", "Mario Coin", "Turtle Coin"], self.player) def create_items(self): item_counts = { @@ -192,37 +138,59 @@ def create_items(self): "Progressive Pumpkin Zone": 3, "Progressive Mario Zone": 3, "Progressive Turtle Zone": 2, - "Tree Coin": 1, - "Space Coin": 1, - "Macro Coin": 1, - "Pumpkin Coin": 1, - "Mario Coin": 1, - "Turtle Coin": 1, "Mushroom": 1, "Fire Flower": 1, "Carrot": 1, - "Progressive Invincibility Star": 3, - "Space Physics": 1, + "Progressive Invincibility Star": 4, } + if self.multiworld.golden_coins[self.player] == "vanilla": + for item, location_name in ( + ("Macro Coin", "Mario Zone 4 - Three Mean Pigs!"), + ("Mario Coin", "Tree Zone 5 - The Big Bird"), + ("Space Coin", "Space Zone 2 - Star Stage"), + ("Pumpkin Coin", "Macro Zone 4 - One Mighty Mouse"), + ("Turtle Coin", "Pumpkin Zone 4 - Witch's Mansion"), + ("Tree Coin", "Turtle Zone 3 - Whale Course") + ): + location = self.multiworld.get_location(location_name, self.player) + location.place_locked_item(self.create_item(item)) + location.address = None + location.item.code = None + elif self.multiworld.golden_coins[self.player] == "shuffled": + for item in self.item_name_groups["Coins"]: + item_counts[item] = 1 + elif self.multiworld.golden_coins[self.player] == "progressive": + for item in [item for item in items if "Progressive" in item and "Zone" in item]: + item_counts[item] += 1 + if self.multiworld.difficulty_mode[self.player] == "easy_to_normal": item_counts["Normal Mode"] = 1 - else: + elif self.multiworld.difficulty_mode[self.player] == "normal_to_easy": item_counts["Easy Mode"] = 1 + else: + item_counts["Progressive Invincibility Star"] += 1 + + if self.multiworld.shuffle_space_physics[self.player]: + item_counts["Progressive Invincibility Star"] -= 1 + item_counts["Space Physics"] = 1 + else: + self.multiworld.push_precollected(self.create_item("Space Physics")) for item_name, count in item_counts.items(): - for _ in range(count): - classification = (ItemClassification.trap if item_name == "Normal Mode" - else ItemClassification.useful if item_name == "Easy Mode" - else ItemClassification.filler if item_name == "Progressive Invincibility Star" - else ItemClassification.progression) - self.multiworld.itempool.append(MarioLand2Item(item_name, classification, self.item_name_to_id[item_name], self.player)) + self.multiworld.itempool += [self.create_item(item_name) for _ in range(count)] def fill_slot_data(self): return { - "mode": self.multiworld.difficulty_mode[self.player].value + "mode": self.multiworld.difficulty_mode[self.player].value, + "vanilla_coins": self.multiworld.golden_coins[self.player] == "vanilla", + "stars": max(len([loc for loc in self.multiworld.get_filled_locations() if loc.item.player == self.player + and loc.item.name == "Progressive Invincibility Star"]), 1) } + 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 "Progressive Invincibility Star" diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index 0e9869612fc37d3071dfb93d665b5675859f398d..d125ad39238c63381452af71937bb2d74ac0c1fc 100644 GIT binary patch delta 242 zcmVz>%00000-H{Pme=(Npn*ab1fB*Y(8884K6F?9M z1OWsn0HA^Z6_S7u1b{&V2mlHh#BQ(vxT2@10WcE@jY9!20MOA4G&BGJ27mwn01+uE zpwmV`&>9T@41jMHq)$ delta 247 zcmeBYp295X6zt;Z=4N8x$N&M4CJIK?OL`xcVqoC=zyEiUqydA1u!8^x3nPmLgTmBF z4FW8!jw~Du9g+v~HZW{UnCc%?>5#!|BE`UKl99P=o`|v*%f*Qcm(5UCxVXT;S2~ze zRY8hvmI+XW6i?eMUXz1yUQ3-e>9lOvv8Js{xG_*@iu?)D2^|f*De93NETT6Uoctn< z1iIHKcwBnLAix;R=&ZuPke<7J*&5B8MfY#bNV{Q~C8@FBHsn@PZVCI5_^DT2R8EA> vU;nhKMz!?Shh>`?SsQ1>e3cJiU|?JJ%{I5({l9pTt3|*Bkf$cwF$Mwvn?qE( diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 117a81b60a76..46bcb228aca3 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -40,26 +40,46 @@ async def game_watcher(self, ctx: BizHawkClientContext): level_data = list(level_data) - items_received = [items[item.item - START_IDS] for item in ctx.items_received] + items_received = [list(items.keys())[item.item - START_IDS] for item in ctx.items_received] + + progressive_coins = { + "Progressive Space Zone": 3, + "Progressive Tree Zone": 4, + "Progressive Macro Zone": 4, + "Progressive Pumpkin Zone": 4, + "Progressive Mario Zone": 4, + "Progressive Turtle Zone": 3 + } + for level_item, count in progressive_coins.items(): + if items_received.count(level_item) >= count: + items_received.append(level_item.split(" ")[1] + " Coin") + locations_checked = [] modified_level_data = level_data.copy() for ID, (location, data) in enumerate(locations.items(), START_IDS): if "clear_condition" in data: if items_received.count(data["clear_condition"][0]) >= data["clear_condition"][1]: modified_level_data[data["ram_index"]] |= 0x80 + elif (ctx.slot_data and ctx.slot_data["vanilla_coins"] and "Coin" in data["clear_condition"][0] + and modified_level_data[data["ram_index"]] & 0x40): + modified_level_data[data["ram_index"]] |= 0x80 + if level_data[data["ram_index"]] & 0x41: locations_checked.append(ID) - invincibility_length = items_received.count("Progressive Invincibility Star") - if invincibility_length > 0: - invincibility_length = min(invincibility_length + 1, 255) + if ctx.slot_data: + total_stars = ctx.slot_data["stars"] + else: + total_stars = 5 - if "Normal Mode" in items_received: - difficulty_mode = 0 - elif "Easy Mode" in items_received: + invincibility_length = int((832.0 / total_stars) * items_received.count("Progressive Invincibility Star")) + + if "Easy Mode" in items_received: difficulty_mode = 1 + elif "Normal Mode" in items_received: + difficulty_mode = 0 elif ctx.slot_data: - difficulty_mode = ctx.slot_data["mode"] + difficulty_mode = ctx.slot_data["mode"] & 1 else: difficulty_mode = 0 @@ -71,12 +91,12 @@ async def game_watcher(self, ctx: BizHawkClientContext): (rom_addresses["Get_Mushroom_C"], [00] if "Mushroom" in items_received else [0xd8], "ROM"), (rom_addresses["Get_Carrot_A"], [0xea, 0x16, 0xa2] if "Carrot" in items_received else [0, 0, 0], "ROM"), (rom_addresses["Get_Carrot_B"], [0xea, 0x16, 0xa2] if "Carrot" in items_received else [0, 0, 0], "ROM"), - (rom_addresses["Get_Carrot_C"], [00] if "Mushroom" in items_received else [0xc8], "ROM"), + (rom_addresses["Get_Carrot_C"], [00] if "Carrot" in items_received else [0xc8], "ROM"), (rom_addresses["Get_Fire_Flower_A"], [0xea, 0x16, 0xa2] if "Fire Flower" in items_received else [0, 0, 0], "ROM"), (rom_addresses["Get_Fire_Flower_B"], [0xea, 0x16, 0xa2] if "Fire Flower" in items_received else [0, 0, 0], "ROM"), - (rom_addresses["Get_Fire_Flower_C"], [00] if "Mushroom" in items_received else [0xc8], "ROM"), - (rom_addresses["Invincibility_Star_A"], [invincibility_length], "ROM"), - (rom_addresses["Invincibility_Star_B"], [64] if "Progressive Invincibility Star" in items_received else [0], "ROM"), + (rom_addresses["Get_Fire_Flower_C"], [00] if "Fire Flower" in items_received else [0xc8], "ROM"), + (rom_addresses["Invincibility_Star_A"], [(invincibility_length >> 8) + 1], "ROM"), + (rom_addresses["Invincibility_Star_B"], [invincibility_length & 0xFF], "ROM"), (rom_addresses["Invincibility_Star_C"], [4] if "Progressive Invincibility Star" in items_received else [0], "ROM"), (rom_addresses["Enable_Bubble"], [0xcb, 0xd7] if "Progressive Space Zone" in items_received else [0, 0], "ROM"), (0x02E4, [difficulty_mode], "CartRAM"), @@ -92,6 +112,6 @@ async def game_watcher(self, ctx: BizHawkClientContext): self.locations_array = locations_checked await ctx.send_msgs([{"cmd": "LocationChecks", "locations": locations_checked}]) - if music == 0x18: + if music == b'\x18': await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) ctx.finished_game = True \ No newline at end of file diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index a0ee893123f1..fa2da89c7657 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -1,12 +1,28 @@ from Options import Toggle, Choice, Range, NamedRange, TextChoice, DeathLink +class GoldenCoins(Choice): + """Vanilla: The coins are found in their original locations. + Shuffled: The coins are shuffled into the item pool. + Progressive: The coins are at the end of the Progressive Level item chains. For example, there will be a third + Progressive Space Zone item, and the final one received will grant the Space Coin. + + You will see a Golden Coin being received when defeating bosses regardless of whether you are actually getting a coin.""" + display_name = "Golden Coins" + option_vanilla = 0 + option_shuffled = 1 + option_progressive = 2 + default = 0 + + class DifficultyMode(Choice): - """Start in Normal Mode with an "upgrade" to Easy Mode in the item pool, + """Play in normal or easy mode. You can also start in Normal Mode with an "upgrade" to Easy Mode in the item pool, or start in Easy Mode with a Normal Mode "Trap" in the item pool.""" display_name = "Difficulty Mode" - option_normal_to_easy = 0 - option_easy_to_normal = 1 + option_normal = 0 + option_easy = 1 + option_normal_to_easy = 2 + option_easy_to_normal = 3 default = 0 class ShuffleSpacePhysics(Toggle): @@ -25,15 +41,26 @@ class RandomizePlatforms(Toggle): display_name = "Randomize Platforms" -class RandomizeAutoScrollLevels(Toggle): - """Randomize which three levels have auto-scrolling.""" - display_name = "Randomize Auto Scroll Levels" +class AutoScrollLevels(Toggle): + """Keep auto scroll levels vanilla or choose a number of levels to be randomly selected to have auto-scrolling. + Certain levels are excluded.""" + display_name = "Auto Scroll Levels" + range_start = -1 + range_end = 20 + special_range_names = {"vanilla": -1} + + +class RandomizeMusic(Toggle): + """Randomize the music that plays in levels and overworld areas.""" + display_name = "Randomize Music" sml2options = { + "golden_coins": GoldenCoins, "difficulty_mode": DifficultyMode, "shuffle_space_physics": ShuffleSpacePhysics, "randomize_enemies": RandomizeEnemies, "randomize_platforms": RandomizePlatforms, - "randomize_auto_scroll_levels": RandomizeAutoScrollLevels, + "auto_scroll_levels": AutoScrollLevels, + "randomize_music": RandomizeMusic } \ No newline at end of file diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 84940aad5416..f0fe38bde78f 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -120,9 +120,12 @@ def randomize_enemies(data, random): copy_sprite(data, sprite_insert(data[i], data[i+1], random.choice([0x0C, 0x0D])), i) i += 3 -def randomize_auto_scroll_levels(data, random): - for i in random.sample([0, 1, 2, 3, 5, 8, 9, 10, 11, 13, 14, 16, 17, 18, 19, 20, 23, 25, 30, 31], 3): - data[0x1F71 + i] = 1 #int(random.randint(0, 99) < (8 if data[0x1F71 +1] == 0 else 25)) + +def randomize_auto_scroll_levels(data, random, n): + eligible_levels = [0, 1, 2, 3, 5, 8, 9, 10, 11, 13, 14, 16, 17, 18, 19, 20, 23, 25, 30, 31] + auto_scroll_levels = random.sample(eligible_levels, n) + for i in eligible_levels: + data[0x1F71 + i] = 1 if i in auto_scroll_levels else 0 def randomize_platforms(data, random): @@ -139,8 +142,21 @@ def randomize_platforms(data, random): i -= 2 elif sprite in level["platforms"]: randomize_sprite(data, random, level["platforms"], i) - for i in range(0xE9A3, 0xE9CE, 3): - data[i] = (0x57 if data[i] == 0x5E else 0x38) + random.randint(0, 7) + i += 3 + for i in range(0xE9A3, 0xE9CE, 3): + data[i] = (0x57 if data[i] == 0x5E else 0x38) + random.randint(0, 7) + +def randomize_music(data, random): + # overworld + overworld_music_tracks = [0x05, 0x06, 0x0D, 0x0E, 0x10, 0x12, 0x1B, 0x1C, 0x1E] + random.shuffle(overworld_music_tracks) + for i, track in zip([0x3004F, 0x3EA9B, 0x3D186, 0x3D52B, 0x3D401, 0x3D297, 0x3D840, 0x3D694, 0x3D758], + overworld_music_tracks): + data[i] = track + # levels + for i in range(0x5619, 0x5899, 0x14): + data[i] = random.choice([0x01, 0x0B, 0x11, 0x13, 0x14, 0x17, 0x1D, 0x1F, 0x28]) + def generate_output(self, output_directory: str): data = get_base_rom_bytes() @@ -154,8 +170,10 @@ def generate_output(self, output_directory: str): randomize_enemies(data, random) if self.multiworld.randomize_platforms[self.player]: randomize_platforms(data, random) - if self.multiworld.randomize_auto_scroll_levels[self.player]: - randomize_auto_scroll_levels(data, random) + if self.multiworld.randomize_auto_scroll_levels[self.player] > -1: + randomize_auto_scroll_levels(data, random, self.multiworld.randomize_auto_scroll_levels[self.player].value) + if self.multiworld.randomize_music[self.player]: + randomize_music(data, random) rom_name = bytearray(f'AP{Utils.__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed:11}\0', 'utf8')[:21] From f76215e708a4d345e83dce1d77580adc41412e72 Mon Sep 17 00:00:00 2001 From: Alchav Date: Sun, 10 Dec 2023 19:37:33 -0500 Subject: [PATCH 004/113] Auto scroll option fixes --- worlds/marioland2/options.py | 4 ++-- worlds/marioland2/rom.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index fa2da89c7657..fe9e23248ca1 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -41,11 +41,11 @@ class RandomizePlatforms(Toggle): display_name = "Randomize Platforms" -class AutoScrollLevels(Toggle): +class AutoScrollLevels(NamedRange): """Keep auto scroll levels vanilla or choose a number of levels to be randomly selected to have auto-scrolling. Certain levels are excluded.""" display_name = "Auto Scroll Levels" - range_start = -1 + range_start = 0 range_end = 20 special_range_names = {"vanilla": -1} diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index f0fe38bde78f..df0b1519dd9d 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -170,8 +170,8 @@ def generate_output(self, output_directory: str): randomize_enemies(data, random) if self.multiworld.randomize_platforms[self.player]: randomize_platforms(data, random) - if self.multiworld.randomize_auto_scroll_levels[self.player] > -1: - randomize_auto_scroll_levels(data, random, self.multiworld.randomize_auto_scroll_levels[self.player].value) + if self.multiworld.auto_scroll_levels[self.player] > -1: + randomize_auto_scroll_levels(data, random, self.multiworld.auto_scroll_levels[self.player].value) if self.multiworld.randomize_music[self.player]: randomize_music(data, random) From 0453a15b2b00b547bce258a605c455a49aac91e6 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 11 Dec 2023 05:39:55 -0500 Subject: [PATCH 005/113] Vanilla coins built into rom --- worlds/marioland2/__init__.py | 3 +-- worlds/marioland2/basepatch.bsdiff4 | Bin 399 -> 402 bytes worlds/marioland2/client.py | 3 --- worlds/marioland2/options.py | 3 ++- worlds/marioland2/rom.py | 5 +++++ worlds/marioland2/rom_addresses.py | 1 + 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 1ff1025929eb..64f54bec7064 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -96,7 +96,7 @@ def set_rules(self): "Tree Zone 3 - The Exit": lambda state: state.has("Progressive Tree Zone", self.player, 2), "Tree Zone 4 - Honeybees": lambda state: state.has("Progressive Tree Zone", self.player, 2), "Tree Zone 5 - The Big Bird": lambda state: state.has("Progressive Tree Zone", self.player, 3), - # You can use a Fire Flower to get here from Macro Zone 1, or if you have every Progressive Macro Zone and + # You can use a Fire Flower to get the Secret Course from Macro Zone 1, or if you have every Progressive Macro Zone and # the Macro Zone Secret paths, you can get here from the boss level "Macro Zone - Secret Course": lambda state: state.has("Fire Flower", self.player) or (state.has("Macro Zone Secret", self.player) and state.has("Progressive Macro Zone", self.player, 3)), "Macro Zone 2 - In the Syrup Sea": lambda state: state.has("Progressive Macro Zone", self.player), @@ -183,7 +183,6 @@ def create_items(self): def fill_slot_data(self): return { "mode": self.multiworld.difficulty_mode[self.player].value, - "vanilla_coins": self.multiworld.golden_coins[self.player] == "vanilla", "stars": max(len([loc for loc in self.multiworld.get_filled_locations() if loc.item.player == self.player and loc.item.name == "Progressive Invincibility Star"]), 1) } diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index d125ad39238c63381452af71937bb2d74ac0c1fc..394c596cc6877da5090c0376f4c19f4462c8f202 100644 GIT binary patch delta 245 zcmeBYp2RHZ6zt;Z=4N8x$N&KkCJIK?f6ZQeje&u`e*f3PuACS3#aj{#&1rz>Y|BI`5LfmFDPMl!tkiyou;N(^& z&7hP7AF$z~+e)snJSb32y!=pnqSuxy5uJY;L=-%gxi~g5tTYp3`XH=Kzp zW>y0S<2#0zTnr3RJ;C4W`1L2rM((KE>+@8&G~lJ}DpURx#u~GVn{V^$9%3N%uXi!$L>j*hg#ZK$I5M(Y{wV~05*J8IRF3v delta 242 zcmVz>%00000-H{Pme=(Npn*ab1fB*Y(8884K6F?9M z1OWsn0HA^Z6_S7u1b{&V2mlHh#BQ(vxT2@10WcE@jY9!20MOA4G&BGJ27mwn01+uE zpwmV`&>9T@41jM= data["clear_condition"][1]: modified_level_data[data["ram_index"]] |= 0x80 - elif (ctx.slot_data and ctx.slot_data["vanilla_coins"] and "Coin" in data["clear_condition"][0] - and modified_level_data[data["ram_index"]] & 0x40): - modified_level_data[data["ram_index"]] |= 0x80 if level_data[data["ram_index"]] & 0x41: locations_checked.append(ID) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index fe9e23248ca1..e2115f3f4e58 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -1,4 +1,4 @@ -from Options import Toggle, Choice, Range, NamedRange, TextChoice, DeathLink +from Options import Toggle, Choice, NamedRange class GoldenCoins(Choice): @@ -25,6 +25,7 @@ class DifficultyMode(Choice): option_easy_to_normal = 3 default = 0 + class ShuffleSpacePhysics(Toggle): """Oh, no! There is Earth gravity on the moon and in space! Find the missing Space Physics item to restore proper order to the universe.""" diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index df0b1519dd9d..1b11e78b98b4 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -8,6 +8,8 @@ from Patch import APDeltaPatch from settings import get_settings +from .rom_addresses import rom_addresses + # Enemy randomizer ported directly from SML2R # https://github.com/slashinfty/sml2r-node/blob/862128c73d336d6cbfbf6290c09f3eff103688e8/src/index.ts#L284 @@ -175,6 +177,9 @@ def generate_output(self, output_directory: str): if self.multiworld.randomize_music[self.player]: randomize_music(data, random) + if self.multiworld.golden_coins[self.player] != "vanilla": + data[rom_addresses["Coin_Shuffle"]] = 0x40 + 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))) diff --git a/worlds/marioland2/rom_addresses.py b/worlds/marioland2/rom_addresses.py index a8ddaf188288..0bd804129693 100644 --- a/worlds/marioland2/rom_addresses.py +++ b/worlds/marioland2/rom_addresses.py @@ -8,6 +8,7 @@ "Invincibility_Star_B": 0x34a3, "Invincibility_Star_C": 0x34a8, "Enable_Bubble": 0x34e5, + "Coin_Shuffle": 0x304ce, "Get_Carrot_C": 0x6092f, "Get_Mushroom_C": 0x60930, "Get_Fire_Flower_C": 0x60933, From d3d651e3639a576fd06262f3f5e94ad3c04ebc63 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 11 Dec 2023 12:33:33 -0500 Subject: [PATCH 006/113] Bosses location name group --- worlds/marioland2/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 64f54bec7064..8e7a98227ec4 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -19,8 +19,8 @@ 'Tree Zone - Secret Course': {'ram_index': 36}, 'Hippo Zone': {'ram_index': 31}, 'Space Zone 1 - Moon Stage': {'ram_index': 16, 'clear_condition': ("Progressive Space Zone", 2)}, - 'Space Zone 2 - Star Stage': {'ram_index': 17, 'clear_condition': ("Space Coin", 1)}, 'Space Zone - Secret Course': {'ram_index': 41}, + 'Space Zone 2 - Star Stage': {'ram_index': 17, 'clear_condition': ("Space Coin", 1)}, 'Macro Zone 1 - The Ant Monsters': {'ram_index': 11, 'clear_condition': ("Progressive Macro Zone", 1)}, 'Macro Zone 2 - In the Syrup Sea': {'ram_index': 12, 'clear_condition': ("Progressive Macro Zone", 2)}, 'Macro Zone 3 - Fiery Mario-Special Agent': {'ram_index': 13, 'clear_condition': ("Progressive Macro Zone", 3)}, @@ -76,6 +76,13 @@ class MarioLand2World(World): "Difficulties": {"Easy Mode", "Normal Mode"} } + location_name_groups = { + "Bosses": { + "Tree Zone 5 - The Big Bird", "Space Zone 2 - Star Stage", "Macro Zone 4 - One Mighty Mouse", + "Pumpkin Zone 4 - Witch's Mansion", "Mario Zone 4 - Three Mean Pigs!", "Turtle Zone 3 - Whale Course" + } + } + option_definitions = sml2options generate_output = generate_output From 42b4150b946ded3b46969b15851e8b10ef3a5781 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 11 Dec 2023 12:53:35 -0500 Subject: [PATCH 007/113] Add host.yaml settings for rom name --- worlds/marioland2/__init__.py | 20 ++++++++++++++++++-- worlds/marioland2/rom.py | 4 +--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 8e7a98227ec4..d02a3cba0e3d 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -1,13 +1,16 @@ import base64 import Utils +import settings from worlds.AutoWorld import World, WebWorld from BaseClasses import Region, Location, Item, ItemClassification from . import client -from .rom import generate_output +from .rom import generate_output, SuperMarioLand2DeltaPatch from .options import sml2options +START_IDS = 7770000 + locations = { 'Mushroom Zone': {'ram_index': 0}, 'Scenic Course': {'ram_index': 40}, @@ -63,10 +66,23 @@ "Easy Mode": ItemClassification.useful, "Normal Mode": ItemClassification.trap, } -START_IDS = 7770000 + +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 = [SuperMarioLand2DeltaPatch.hash] + + rom_file: SML2RomFile = SML2RomFile(SML2RomFile.copy_to) + + class MarioLand2World(World): game = "Super Mario Land 2" + settings_key = "sml2_options" + settings: MarioLand2Settings + location_name_to_id = {location_name: ID for ID, location_name in enumerate(locations, START_IDS)} item_name_to_id = {item_name: ID for ID, item_name in enumerate(items, START_IDS)} diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 1b11e78b98b4..147bc9dc5bb5 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -219,9 +219,7 @@ def get_base_rom_bytes(): return base_rom_bytes def get_base_rom_path(): - file_name = "C:\\src\\marioland2\\baserom.gb" - if not file_name: - file_name = get_settings()["sml2_options"]["rom_file"] + file_name = get_settings()["sml2_options"]["rom_file"] if not os.path.exists(file_name): file_name = Utils.user_path(file_name) return file_name From e97633c6072bd262fb945a60e3d92981a9513963 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 11 Dec 2023 17:40:27 -0500 Subject: [PATCH 008/113] Auto Scroll Trap --- worlds/marioland2/__init__.py | 5 +++++ worlds/marioland2/client.py | 16 ++++++++++++++-- worlds/marioland2/options.py | 6 ++++++ worlds/marioland2/rom.py | 4 +++- worlds/marioland2/rom_addresses.py | 2 ++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index d02a3cba0e3d..045132e3c080 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -65,6 +65,7 @@ "Space Physics": ItemClassification.progression, "Easy Mode": ItemClassification.useful, "Normal Mode": ItemClassification.trap, + "Auto Scroll": ItemClassification.trap } class MarioLand2Settings(settings.Group): @@ -200,6 +201,10 @@ def create_items(self): else: self.multiworld.push_precollected(self.create_item("Space Physics")) + if self.multiworld.auto_scroll_trap[self.player]: + item_counts["Progressive Invincibility Star"] -= 1 + item_counts["Auto Scroll"] = 1 + for item_name, count in item_counts.items(): self.multiworld.itempool += [self.create_item(item_name) for _ in range(count)] diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 967af927ec56..c8b43ed60b7b 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -34,7 +34,11 @@ async def set_auth(self, ctx): async def game_watcher(self, ctx: BizHawkClientContext): from . import locations, items, START_IDS - game_loaded_check, level_data, music = await read(ctx.bizhawk_ctx, [(0x0046, 10, "CartRAM"), (0x0848, 42, "CartRAM"), (0x0469, 1, "CartRAM")]) + game_loaded_check, level_data, music, auto_scroll_enabled, auto_scroll_levels, current_level = \ + await read(ctx.bizhawk_ctx, [(0x0046, 10, "CartRAM"), (0x0848, 42, "CartRAM"), (0x0469, 1, "CartRAM"), + (rom_addresses["Auto_Scroll_Disable"], 1, "ROM"), + (rom_addresses["Auto_Scroll_Levels"], 32, "ROM"), + (0x0269, 1, "CartRAM")]) if game_loaded_check != b'\x124Vx\xff\xff\xff\xff\xff\xff': return @@ -100,7 +104,15 @@ async def game_watcher(self, ctx: BizHawkClientContext): (0x0848, modified_level_data, "CartRAM") ] - success = await guarded_write(ctx.bizhawk_ctx, data_writes, [(0x0848, level_data, "CartRAM")]) + if "Auto Scroll" in items_received: + if auto_scroll_enabled == 0xaf: + # auto scroll has not yet been enabled + data_writes.append((rom_addresses["Auto_Scroll_Disable"], [0x7e], "ROM")) + # if the current level is an auto scroll level, turn on auto scrolling now + if auto_scroll_levels[current_level] == 1: + data_writes.append((0x02C8, [0x01], "CartRAM")) + + await guarded_write(ctx.bizhawk_ctx, data_writes, [(0x0848, level_data, "CartRAM")]) if not ctx.server or not ctx.server.socket.open or ctx.server.socket.closed: return diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index e2115f3f4e58..ea27c93a5a94 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -51,6 +51,11 @@ class AutoScrollLevels(NamedRange): special_range_names = {"vanilla": -1} +class AutoScrollTrap(Toggle): + """If on, auto scroll levels will not auto scroll until you've received the Auto Scroll trap item.""" + display_name = "Auto Scroll Trap" + + class RandomizeMusic(Toggle): """Randomize the music that plays in levels and overworld areas.""" display_name = "Randomize Music" @@ -63,5 +68,6 @@ class RandomizeMusic(Toggle): "randomize_enemies": RandomizeEnemies, "randomize_platforms": RandomizePlatforms, "auto_scroll_levels": AutoScrollLevels, + "auto_scroll_trap": AutoScrollTrap, "randomize_music": RandomizeMusic } \ No newline at end of file diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 147bc9dc5bb5..e89d3f16e2fb 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -127,7 +127,7 @@ def randomize_auto_scroll_levels(data, random, n): eligible_levels = [0, 1, 2, 3, 5, 8, 9, 10, 11, 13, 14, 16, 17, 18, 19, 20, 23, 25, 30, 31] auto_scroll_levels = random.sample(eligible_levels, n) for i in eligible_levels: - data[0x1F71 + i] = 1 if i in auto_scroll_levels else 0 + data[rom_addresses["Auto_Scroll_Levels"] + i] = 1 if i in auto_scroll_levels else 0 def randomize_platforms(data, random): @@ -177,6 +177,8 @@ def generate_output(self, output_directory: str): if self.multiworld.randomize_music[self.player]: randomize_music(data, random) + if self.multiworld.auto_scroll_trap[self.player]: + data[rom_addresses["Auto_Scroll_Disable"]] = 0xAF if self.multiworld.golden_coins[self.player] != "vanilla": data[rom_addresses["Coin_Shuffle"]] = 0x40 diff --git a/worlds/marioland2/rom_addresses.py b/worlds/marioland2/rom_addresses.py index 0bd804129693..e9b92b8c9fe7 100644 --- a/worlds/marioland2/rom_addresses.py +++ b/worlds/marioland2/rom_addresses.py @@ -1,5 +1,7 @@ rom_addresses = { + "Auto_Scroll_Disable": 0x4c0, "Space_Physics": 0x4e8, + "Auto_Scroll_Levels": 0x1f71, "Get_Hurt_To_Big_Mario": 0x31c7, "Get_Mushroom_A": 0x345c, "Get_Fire_Flower_A": 0x346d, From bb2622a070e3c676f93267833013807f1b66ecc7 Mon Sep 17 00:00:00 2001 From: Alchav Date: Tue, 12 Dec 2023 08:05:17 -0500 Subject: [PATCH 009/113] Exclude Level 10 from auto scroll --- worlds/marioland2/options.py | 2 +- worlds/marioland2/rom.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index ea27c93a5a94..64b9cdd158b6 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -47,7 +47,7 @@ class AutoScrollLevels(NamedRange): Certain levels are excluded.""" display_name = "Auto Scroll Levels" range_start = 0 - range_end = 20 + range_end = 19 special_range_names = {"vanilla": -1} diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index e89d3f16e2fb..b24ed3c93713 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -124,7 +124,7 @@ def randomize_enemies(data, random): def randomize_auto_scroll_levels(data, random, n): - eligible_levels = [0, 1, 2, 3, 5, 8, 9, 10, 11, 13, 14, 16, 17, 18, 19, 20, 23, 25, 30, 31] + eligible_levels = [0, 1, 2, 3, 5, 8, 9, 11, 13, 14, 16, 17, 18, 19, 20, 23, 25, 30, 31] auto_scroll_levels = random.sample(eligible_levels, n) for i in eligible_levels: data[rom_addresses["Auto_Scroll_Levels"] + i] = 1 if i in auto_scroll_levels else 0 From d280a3fe2620b74a8a234e67ae90a975c0856fde Mon Sep 17 00:00:00 2001 From: Alchav Date: Fri, 15 Dec 2023 09:44:13 -0500 Subject: [PATCH 010/113] Mario Land 2 second release --- worlds/marioland2/__init__.py | 130 ++++++++++++++++++++-------- worlds/marioland2/basepatch.bsdiff4 | Bin 402 -> 398 bytes worlds/marioland2/client.py | 24 +++-- worlds/marioland2/options.py | 19 +++- worlds/marioland2/rom.py | 76 +++++++++------- worlds/marioland2/rom_addresses.py | 3 +- 6 files changed, 176 insertions(+), 76 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 045132e3c080..3835fd76c946 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -12,37 +12,60 @@ START_IDS = 7770000 locations = { - 'Mushroom Zone': {'ram_index': 0}, - 'Scenic Course': {'ram_index': 40}, - 'Tree Zone 1 - Invincibility!': {'ram_index': 1, 'clear_condition': ("Progressive Tree Zone", 1)}, - 'Tree Zone 2 - In the Trees': {'ram_index': 2, 'clear_condition': ("Progressive Tree Zone", 2)}, - 'Tree Zone 3 - The Exit': {'ram_index': 4, 'clear_condition': ("Progressive Tree Zone", 3)}, - 'Tree Zone 4 - Honeybees': {'ram_index': 3, 'clear_condition': ("Progressive Tree Zone", 3)}, - 'Tree Zone 5 - The Big Bird': {'ram_index': 5, 'clear_condition': ("Tree Coin", 1)}, - 'Tree Zone - Secret Course': {'ram_index': 36}, - 'Hippo Zone': {'ram_index': 31}, - 'Space Zone 1 - Moon Stage': {'ram_index': 16, 'clear_condition': ("Progressive Space Zone", 2)}, - 'Space Zone - Secret Course': {'ram_index': 41}, - 'Space Zone 2 - Star Stage': {'ram_index': 17, 'clear_condition': ("Space Coin", 1)}, - 'Macro Zone 1 - The Ant Monsters': {'ram_index': 11, 'clear_condition': ("Progressive Macro Zone", 1)}, - 'Macro Zone 2 - In the Syrup Sea': {'ram_index': 12, 'clear_condition': ("Progressive Macro Zone", 2)}, - 'Macro Zone 3 - Fiery Mario-Special Agent': {'ram_index': 13, 'clear_condition': ("Progressive Macro Zone", 3)}, - 'Macro Zone 4 - One Mighty Mouse': {'ram_index': 14, 'clear_condition': ("Macro Coin", 1)}, - 'Macro Zone - Secret Course': {'ram_index': 35, 'clear_condition': ("Macro Zone Secret", 1)}, - 'Pumpkin Zone 1 - Bat Course': {'ram_index': 6, 'clear_condition': ("Progressive Pumpkin Zone", 1)}, - 'Pumpkin Zone 2 - Cyclops Course': {'ram_index': 7, 'clear_condition': ("Progressive Pumpkin Zone", 2)}, - 'Pumpkin Zone 3 - Ghost House': {'ram_index': 8, 'clear_condition': ("Progressive Pumpkin Zone", 3)}, - "Pumpkin Zone 4 - Witch's Mansion": {'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1)}, - 'Pumpkin Zone - Secret Course 1': {'ram_index': 38}, - 'Pumpkin Zone - Secret Course 2': {'ram_index': 39}, - 'Mario Zone 1 - Fiery Blocks': {'ram_index': 26, 'clear_condition': ("Progressive Mario Zone", 1)}, - 'Mario Zone 2 - Mario the Circus Star!': {'ram_index': 27, 'clear_condition': ("Progressive Mario Zone", 2)}, - 'Mario Zone 3 - Beware: Jagged Spikes': {'ram_index': 28, 'clear_condition': ("Progressive Mario Zone", 3)}, - 'Mario Zone 4 - Three Mean Pigs!': {'ram_index': 29, 'clear_condition': ("Mario Coin", 1)}, - 'Turtle Zone 1 - Cheep Cheep Course': {'ram_index': 21, 'clear_condition': ("Progressive Turtle Zone", 1)}, - 'Turtle Zone 2 - Turtle Zone': {'ram_index': 22, 'clear_condition': ("Progressive Turtle Zone", 2)}, - 'Turtle Zone 3 - Whale Course': {'ram_index': 23, 'clear_condition': ("Turtle Coin", 1)}, - 'Turtle Zone - Secret Course': {'ram_index': 37}} + 'Mushroom Zone': {'id': 0x00, 'ram_index': 0, 'type': 'level'}, + 'Mushroom Zone Midway Bell': {'id': 0x00, 'ram_index': 0, 'clear_condition': ("Mushroom Zone Midway Bell", 1), 'type': 'bell'}, + 'Scenic Course': {'id': 0x19, 'ram_index': 40, 'type': 'level'}, + 'Tree Zone 1 - Invincibility!': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Progressive Tree Zone", 1), 'type': 'level'}, + 'Tree Zone 1 - Invincibility! Midway Bell': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone 1 - Invincibility! Midway Bell", 1), 'type': 'bell'}, + 'Tree Zone 2 - In the Trees': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Progressive Tree Zone", 2), 'type': 'level'}, + 'Tree Zone 2 - In the Trees Midway Bell': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Progressive Tree Zone", 2), 'type': 'bell'}, + 'Tree Zone 3 - The Exit': {'id': 0x04, 'ram_index': 4, 'clear_condition': ("Progressive Tree Zone", 3), 'type': 'level'}, + 'Tree Zone 4 - Honeybees': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Progressive Tree Zone", 3), 'type': 'level'}, + 'Tree Zone 4 - Honeybees Midway Bell': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone 4 - Honeybees Midway Bell", 1), 'type': 'bell'}, + 'Tree Zone 5 - The Big Bird': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Coin", 1), 'type': 'level'}, + 'Tree Zone 5 - The Big Bird Midway Bell': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Zone 5 - The Big Bird Midway Bell", 1), 'type': 'bell'}, + 'Tree Zone - Secret Course': {'id': 0x1D, 'ram_index': 36, 'type': 'level'}, + 'Hippo Zone': {'id': 0x11, 'ram_index': 31, 'type': 'level'}, + 'Space Zone 1 - Moon Stage': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Progressive Space Zone", 2), 'type': 'level'}, + 'Space Zone 1 - Moon Stage Midway Bell': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone 1 - Moon Stage Midway Bell", 1), 'type': 'bell'}, + 'Space Zone - Secret Course': {'id': 0x1C, 'ram_index': 41, 'type': 'level'}, + 'Space Zone 2 - Star Stage': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Coin", 1), 'type': 'level'}, + 'Space Zone 2 - Star Stage Midway Bell': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Zone 2 - Star Stage Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 1 - The Ant Monsters': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Progressive Macro Zone", 1), 'type': 'level'}, + 'Macro Zone 1 - The Ant Monsters Midway Bell': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone 1 - The Ant Monsters Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 2 - In the Syrup Sea': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Progressive Macro Zone", 2), 'type': 'level'}, + 'Macro Zone 2 - In the Syrup Sea Midway Bell': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone 2 - In the Syrup Sea Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 3 - Fiery Mario-Special Agent': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Progressive Macro Zone", 3), 'type': 'level'}, + 'Macro Zone 3 - Fiery Mario-Special Agent Midway Bell': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 4 - One Mighty Mouse': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Coin", 1), 'type': 'level'}, + 'Macro Zone 4 - One Mighty Mouse Midway Bell': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Zone 4 - One Mighty Mouse Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone - Secret Course': {'id': 0x1E, 'ram_index': 35, 'clear_condition': ("Macro Zone Secret", 1), 'type': 'level'}, + 'Pumpkin Zone 1 - Bat Course': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Progressive Pumpkin Zone", 1), 'type': 'level'}, + 'Pumpkin Zone 1 - Bat Course Midway Bell': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone 1 - Bat Course Midway Bell", 1), 'type': 'bell'}, + 'Pumpkin Zone 2 - Cyclops Course': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Progressive Pumpkin Zone", 2), 'type': 'level'}, + 'Pumpkin Zone 2 - Cyclops Course Midway Bell': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Progressive Pumpkin Zone", 2), 'type': 'bell'}, + 'Pumpkin Zone 3 - Ghost House': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Progressive Pumpkin Zone", 3), 'type': 'level'}, + 'Pumpkin Zone 3 - Ghost House Midway Bell': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Progressive Pumpkin Zone", 3), 'type': 'bell'}, + "Pumpkin Zone 4 - Witch's Mansion": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'level'}, + "Pumpkin Zone 4 - Witch's Mansion Midway Bell": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'bell'}, + 'Pumpkin Zone - Secret Course 1': {'id': 0x1B, 'ram_index': 38, 'type': 'level'}, + 'Pumpkin Zone - Secret Course 2': {'id': 0x1F, 'ram_index': 39, 'type': 'level'}, + 'Mario Zone 1 - Fiery Blocks': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Progressive Mario Zone", 1), 'type': 'level'}, + 'Mario Zone 1 - Fiery Blocks Midway Bell': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone 1 - Fiery Blocks Midway Bell", 1), 'type': 'bell'}, + 'Mario Zone 2 - Mario the Circus Star!': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Progressive Mario Zone", 2), 'type': 'level'}, + 'Mario Zone 2 - Mario the Circus Star! Midway Bell': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone 2 - Mario the Circus Star! Midway Bell", 1), 'type': 'bell'}, + 'Mario Zone 3 - Beware: Jagged Spikes': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Progressive Mario Zone", 3), 'type': 'level'}, + 'Mario Zone 3 - Beware: Jagged Spikes Midway Bell': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone 3 - Beware: Jagged Spikes Midway Bell", 1), 'type': 'bell'}, + 'Mario Zone 4 - Three Mean Pigs!': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Coin", 1), 'type': 'level'}, + 'Mario Zone 4 - Three Mean Pigs! Midway Bell': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Zone 4 - Three Mean Pigs! Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone 1 - Cheep Cheep Course': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Progressive Turtle Zone", 1), 'type': 'level'}, + 'Turtle Zone 1 - Cheep Cheep Course Midway Bell': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone 1 - Cheep Cheep Course Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone 2 - Turtle Zone': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Progressive Turtle Zone", 2), 'type': 'level'}, + 'Turtle Zone 2 - Turtle Zone Midway Bell': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone 2 - Turtle Zone Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone 3 - Whale Course': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Coin", 1), 'type': 'level'}, + 'Turtle Zone 3 - Whale Course Midway Bell': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Zone 3 - Whale Course Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone - Secret Course': {'id': 0x1A, 'ram_index': 37, 'type': 'level'} +} items = { "Progressive Space Zone": ItemClassification.progression, @@ -65,9 +88,32 @@ "Space Physics": ItemClassification.progression, "Easy Mode": ItemClassification.useful, "Normal Mode": ItemClassification.trap, - "Auto Scroll": ItemClassification.trap + "Auto Scroll": ItemClassification.trap, + "Mushroom Zone Midway Bell": ItemClassification.filler, + "Tree Zone 1 - Invincibility! Midway Bell": ItemClassification.filler, + "Tree Zone 3 - The Exit Midway Bell": ItemClassification.filler, + "Tree Zone 4 - Honeybees Midway Bell": ItemClassification.filler, + "Tree Zone 5 - The Big Bird Midway Bell": ItemClassification.filler, + "Space Zone 1 - Moon Stage Midway Bell": ItemClassification.filler, + "Space Zone 2 - Star Stage Midway Bell": ItemClassification.filler, + "Macro Zone 1 - The Ant Monsters Midway Bell": ItemClassification.filler, + "Macro Zone 2 - In the Syrup Sea Midway Bell": ItemClassification.filler, + "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": ItemClassification.filler, + "Macro Zone 4 - One Mighty Mouse Midway Bell": ItemClassification.filler, + "Pumpkin Zone 1 - Bat Course Midway Bell": ItemClassification.filler, + "Pumpkin Zone 2 - Cyclops Course Midway Bell": ItemClassification.filler, + "Pumpkin Zone 3 - Ghost House Midway Bell": ItemClassification.filler, + "Pumpkin Zone 4 - Witch's Mansion Midway Bell": ItemClassification.filler, + "Mario Zone 1 - Fiery Blocks Midway Bell": ItemClassification.filler, + "Mario Zone 2 - Mario the Circus Star! Midway Bell": ItemClassification.filler, + "Mario Zone 3 - Beware: Jagged Spikes Midway Bell": ItemClassification.filler, + "Mario Zone 4 - Three Mean Pigs! Midway Bell": ItemClassification.filler, + "Turtle Zone 1 - Cheep Cheep Course Midway Bell": ItemClassification.filler, + "Turtle Zone 2 - Turtle Zone Midway Bell": ItemClassification.filler, + "Turtle Zone 3 - Whale Course Midway Bell": ItemClassification.filler, } + class MarioLand2Settings(settings.Group): class SML2RomFile(settings.UserFilePath): """File name of the Super Mario Land 2 1.0 ROM""" @@ -108,6 +154,8 @@ def create_regions(self): menu_region = Region("Menu", self.player, self.multiworld) self.multiworld.regions.append(menu_region) for location_name in locations: + if "Midway Bell" in location_name and not self.multiworld.shuffle_midway_bells[self.player]: + continue menu_region.locations.append(Location(self.player, location_name, self.location_name_to_id[location_name], menu_region)) def set_rules(self): @@ -120,8 +168,8 @@ def set_rules(self): "Tree Zone 3 - The Exit": lambda state: state.has("Progressive Tree Zone", self.player, 2), "Tree Zone 4 - Honeybees": lambda state: state.has("Progressive Tree Zone", self.player, 2), "Tree Zone 5 - The Big Bird": lambda state: state.has("Progressive Tree Zone", self.player, 3), - # You can use a Fire Flower to get the Secret Course from Macro Zone 1, or if you have every Progressive Macro Zone and - # the Macro Zone Secret paths, you can get here from the boss level + # You can use a Fire Flower to get the Secret Course from Macro Zone 1, or if you have every Progressive + # Macro Zone and the Macro Zone Secret paths, you can get here from the boss level "Macro Zone - Secret Course": lambda state: state.has("Fire Flower", self.player) or (state.has("Macro Zone Secret", self.player) and state.has("Progressive Macro Zone", self.player, 3)), "Macro Zone 2 - In the Syrup Sea": lambda state: state.has("Progressive Macro Zone", self.player), "Macro Zone 3 - Fiery Mario-Special Agent": lambda state: state.has("Progressive Macro Zone", self.player, 2), @@ -136,11 +184,19 @@ def set_rules(self): "Mario Zone 3 - Beware: Jagged Spikes": lambda state: state.has("Progressive Mario Zone", self.player, 2), "Mario Zone 4 - Three Mean Pigs!": lambda state: state.has("Progressive Mario Zone", self.player, 3), "Turtle Zone 2 - Turtle Zone": lambda state: state.has("Progressive Turtle Zone", self.player), - # The powerups are needed not for the secret exit in Turtle Zone 2, but to fly over or take damage in the spikes in the secret course + # The powerups are needed not for the secret exit in Turtle Zone 2, but to fly over or take damage in the + # spikes in the secret course "Turtle Zone - Secret Course": lambda state: state.has("Progressive Turtle Zone", self.player) and state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player), "Turtle Zone 3 - Whale Course": lambda state: state.has("Progressive Turtle Zone", self.player, 2), } + if self.multiworld.shuffle_midway_bells[self.player]: + # copy level access rules onto midway point rules + for midway_loc in [loc for loc in locations if "Midway Bell" in loc]: + level_loc = midway_loc[:-12] + if level_loc in rules: + rules[midway_loc] = rules[level_loc] + for level, rule in rules.items(): self.multiworld.get_location(level, self.player).access_rule = rule @@ -188,6 +244,10 @@ def create_items(self): for item in [item for item in items if "Progressive" in item and "Zone" in item]: item_counts[item] += 1 + if self.multiworld.shuffle_midway_bells[self.player]: + for item in [item for item in items if "Midway Bell" in item]: + item_counts[item] = 1 + if self.multiworld.difficulty_mode[self.player] == "easy_to_normal": item_counts["Normal Mode"] = 1 elif self.multiworld.difficulty_mode[self.player] == "normal_to_easy": diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index 394c596cc6877da5090c0376f4c19f4462c8f202..3cceb38afe4fd460f6ef42094e2ee253a5cf1dd5 100644 GIT binary patch delta 241 zcmbQl+{Y~F6zt;Z=4N8x$N&L%CJIK?*PL7u%D}*1zyEiUqydA1v4a2y3nPmL1H)9I z1_72vM-~nSE{P|38yL1FN`+L-lrmym*wAos;wyg7UkaCl8W&tRk&!mRD1}W;eS*?d zf0qMbqnV|jY)y3Z;<6}U{L+)knV9K1#k+&?k)R->l8%E`e2asV2ZLbxA*F!EU{?L} z4lE4c8A@9j81#-sSwE4VYj@(^)ld1RV(iP-)^Gk(ltDSz}6ixDTjf!F)sHC?vz>{r}w0*V4=d#$sVgFJ2D0W0MF1=sQ>@~ delta 245 zcmeBUp2RHZ6zt;Z=4N8x$N&KkCJIK?f6ZQeje&u`e*f3PuACS3#aj{#&1rz>Y|BI`5LfmFDPMl!tkiyou;N(^& z&7hP7AF$z~+e)snJSb32y!=pnqSuxy5uJY;L=-%gxi~g5tTYp3`XH=Kzp zW>y0S<2#0zTnr3RJ;C4W`1L2rM((KE>+@8&G~lJ}DpURx#u~GVn{V^$9%3N%uXi!$L>j*hg#ZK$I5M(Y|9u305ybFHvj+t diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index c8b43ed60b7b..75deec6236e9 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -5,9 +5,10 @@ from worlds._bizhawk.client import BizHawkClient, BizHawkClientContext from worlds._bizhawk import read, write, guarded_write +from .rom_addresses import rom_addresses + logger = logging.getLogger("Client") -from .rom_addresses import rom_addresses class MarioLand2Client(BizHawkClient): system = ("GB", "SGB") @@ -34,14 +35,21 @@ async def set_auth(self, ctx): async def game_watcher(self, ctx: BizHawkClientContext): from . import locations, items, START_IDS - game_loaded_check, level_data, music, auto_scroll_enabled, auto_scroll_levels, current_level = \ + game_loaded_check, level_data, music, auto_scroll_enabled, auto_scroll_levels, current_level, midway_point = \ await read(ctx.bizhawk_ctx, [(0x0046, 10, "CartRAM"), (0x0848, 42, "CartRAM"), (0x0469, 1, "CartRAM"), (rom_addresses["Auto_Scroll_Disable"], 1, "ROM"), (rom_addresses["Auto_Scroll_Levels"], 32, "ROM"), - (0x0269, 1, "CartRAM")]) + (0x0269, 1, "CartRAM"), + (0x02A0, 1, "CartRAM")]) + if game_loaded_check != b'\x124Vx\xff\xff\xff\xff\xff\xff': return + current_level = int.from_bytes(current_level) + midway_point = int.from_bytes(midway_point) + music = int.from_bytes(music) + auto_scroll_enabled = int.from_bytes(auto_scroll_enabled) + level_data = list(level_data) items_received = [list(items.keys())[item.item - START_IDS] for item in ctx.items_received] @@ -63,9 +71,11 @@ async def game_watcher(self, ctx: BizHawkClientContext): for ID, (location, data) in enumerate(locations.items(), START_IDS): if "clear_condition" in data: if items_received.count(data["clear_condition"][0]) >= data["clear_condition"][1]: - modified_level_data[data["ram_index"]] |= 0x80 + modified_level_data[data["ram_index"]] |= 0x08 if data["type"] == "bell" else 0x80 - if level_data[data["ram_index"]] & 0x41: + if data["type"] == "level" and level_data[data["ram_index"]] & 0x41: + locations_checked.append(ID) + elif data["type"] == "bell" and data["id"] == current_level and midway_point == 0xFF: locations_checked.append(ID) if ctx.slot_data: @@ -85,7 +95,7 @@ async def game_watcher(self, ctx: BizHawkClientContext): difficulty_mode = 0 data_writes = [ - (rom_addresses["Space_Physics"], [0xea, 0x87, 0xa2] if "Space Physics" in items_received else [0, 0, 0], "ROM"), + (rom_addresses["Space_Physics"], [0x7e] if "Space Physics" in items_received else [0xaf], "ROM"), (rom_addresses["Get_Hurt_To_Big_Mario"], [1] if "Mushroom" in items_received else [0], "ROM"), (rom_addresses["Get_Mushroom_A"], [0xea, 0x16, 0xa2] if "Mushroom" in items_received else [0, 0, 0], "ROM"), (rom_addresses["Get_Mushroom_B"], [0xea, 0x16, 0xa2] if "Mushroom" in items_received else [0, 0, 0], "ROM"), @@ -121,6 +131,6 @@ async def game_watcher(self, ctx: BizHawkClientContext): self.locations_array = locations_checked await ctx.send_msgs([{"cmd": "LocationChecks", "locations": locations_checked}]) - if music == b'\x18': + if music == 0x18: await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) ctx.finished_game = True \ No newline at end of file diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 64b9cdd158b6..c2a2809c8159 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -1,4 +1,4 @@ -from Options import Toggle, Choice, NamedRange +from Options import Toggle, Choice, NamedRange, Range class GoldenCoins(Choice): @@ -15,6 +15,14 @@ class GoldenCoins(Choice): default = 0 +class GoldenCoinsRequired(Range): + """Number of Golden Coins required to enter Wario's Castle.""" + display_name = "Golden Coins Required" + range_start = 0 + range_end = 6 + default = 6 + + class DifficultyMode(Choice): """Play in normal or easy mode. You can also start in Normal Mode with an "upgrade" to Easy Mode in the item pool, or start in Easy Mode with a Normal Mode "Trap" in the item pool.""" @@ -26,6 +34,13 @@ class DifficultyMode(Choice): default = 0 +class ShuffleMidwayBells(Toggle): + """Shuffle the Midway Bells into the item pool. Ringing a bell will trigger a location check. + Obtaining a Midway Bell will be permanent, and some levels will require backtracking from the midway point to reach + secret exits.""" + display_name = "Shuffle Midway Bells" + + class ShuffleSpacePhysics(Toggle): """Oh, no! There is Earth gravity on the moon and in space! Find the missing Space Physics item to restore proper order to the universe.""" @@ -49,6 +64,7 @@ class AutoScrollLevels(NamedRange): range_start = 0 range_end = 19 special_range_names = {"vanilla": -1} + default = -1 class AutoScrollTrap(Toggle): @@ -64,6 +80,7 @@ class RandomizeMusic(Toggle): sml2options = { "golden_coins": GoldenCoins, "difficulty_mode": DifficultyMode, + "shuffle_midway_bells":ShuffleMidwayBells, "shuffle_space_physics": ShuffleSpacePhysics, "randomize_enemies": RandomizeEnemies, "randomize_platforms": RandomizePlatforms, diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index b24ed3c93713..efc06c879820 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -10,54 +10,58 @@ from .rom_addresses import rom_addresses -# Enemy randomizer ported directly from SML2R +# Enemy and Platform randomizer ported directly from SML2R # https://github.com/slashinfty/sml2r-node/blob/862128c73d336d6cbfbf6290c09f3eff103688e8/src/index.ts#L284 + def sprite_extract(a, b): x = ((0b00010000 & a) << 2) y = ((0b11100000 & a) >> 2) z = ((0b11100000 & b) >> 5) return x | y | z + def sprite_insert(a, b, s): x = ((s & 0b01000000) >> 2) y = ((s & 0b00111000) << 2) z = ((s & 0b00000111) << 5) return [(a & 0b00001111) | x | y, (b & 0b00011111) | z] + def copy_sprite(data, arr, pos): for i in range(2): data[pos + i] = arr[i] + def randomize_sprite(data, random, arr, i): selected_sprite = sprite_insert(data[i], data[i + 1], random.choice(arr)) copy_sprite(data, selected_sprite, i) -def randomize_enemies(data, random): +def randomize_enemies(data, random): level_list = [ - {"enemies": [0x01, 0x08, 0x09, 0x3A], "start": 0xE077, "end": 0xE0BC}, # lv00 - {"enemies": [0x01, 0x08, 0x09, 0x3A], "start": 0xE955, "end": 0xE99D}, # lv17 - {"enemies": [0x08, 0x09, 0x3A], "start": 0xEA2F, "end": 0xEA7D}, # lv19 - {"enemies": [0x08, 0x09, 0x3A], "start": 0xEAA3, "end": 0xEACD}, # lv1B - {"enemies": [0x1F, 0x20, 0x21, 0x22], "start": 0xE0BD, "end": 0xE123}, # lv01 - {"enemies": [0x44, 0x58], "start": 0xE124, "end": 0xE181}, # lv02 - {"enemies": [0x35, 0x3E, 0x40, 0x41, 0x42], "start": 0xE182, "end": 0xE1EE}, # lv03 - {"enemies": [0x33, 0x34, 0x5D], "start": 0xE1EF, "end": 0xE249}, # lv04 - {"enemies": [0x08, 0x39, 0x3A], "start": 0xE24A, "end": 0xE2A1}, # lv05 - {"enemies": [0x4D, 0x54, 0x55, 0x56, 0x5E, 0x5F], "start": 0xE30C, "end": 0xE384}, # lv07 - {"enemies": [0x4D, 0x57], "start": 0xE385, "end": 0xE3D3}, # lv08 - {"enemies": [0x01, 0x40, 0x4B], "start": 0xE432, "end": 0xE49B}, # lv0A - {"enemies": [0x08, 0x09, 0x3A, 0x44, 0x4D], "start": 0xE49C, "end": 0xE4F9}, # lv0B - {"enemies": [0x05, 0x06, 0x07, 0x08, 0x09, 0x0B, 0x3A, 0x3D], "start": 0xE5C2, "end": 0xE62B}, # lv0E - {"enemies": [0x05, 0x39, 0x57, 0x5B], "start": 0xE706, "end": 0xE77B}, # lv11 - {"enemies": [0x5C, 0x5E, 0x5F], "start": 0xE7C8, "end": 0xE822}, # lv13 - {"enemies": [0x22, 0x23, 0x25, 0x27], "start": 0xE823, "end": 0xE88F}, # lv14 - {"enemies": [0x07, 0x33, 0x34, 0x3D, 0x5D], "start": 0xE890, "end": 0xE8F6}, # lv15 - {"enemies": [0x01, 0x08, 0x09, 0x34, 0x3A, 0x55], "start": 0xE8F7, "end": 0xE954}, # lv16 - {"enemies": [0x68, 0x69], "start": 0xE99E, "end": 0xEA2E}, # lv18a - {"enemies": [0x6E, 0x6F], "start": 0xE99E, "end": 0xEA2E}, # lv18b - {"enemies": [0x01, 0x09], "start": 0xEB55, "end": 0xEBB5} # lv1F + {"enemies": [0x01, 0x08, 0x09, 0x3A], "start": 0xE077, "end": 0xE0BC}, # lv00 + {"enemies": [0x01, 0x08, 0x09, 0x3A], "start": 0xE955, "end": 0xE99D}, # lv17 + {"enemies": [0x08, 0x09, 0x3A], "start": 0xEA2F, "end": 0xEA7D}, # lv19 + {"enemies": [0x08, 0x09, 0x3A], "start": 0xEAA3, "end": 0xEACD}, # lv1B + {"enemies": [0x1F, 0x20, 0x21, 0x22], "start": 0xE0BD, "end": 0xE123}, # lv01 + {"enemies": [0x44, 0x58], "start": 0xE124, "end": 0xE181}, # lv02 + {"enemies": [0x35, 0x3E, 0x40, 0x41, 0x42], "start": 0xE182, "end": 0xE1EE}, # lv03 + {"enemies": [0x33, 0x34, 0x5D], "start": 0xE1EF, "end": 0xE249}, # lv04 + {"enemies": [0x08, 0x39, 0x3A], "start": 0xE24A, "end": 0xE2A1}, # lv05 + {"enemies": [0x4D, 0x54, 0x55, 0x56, 0x5E, 0x5F], "start": 0xE30C, "end": 0xE384}, # lv07 + {"enemies": [0x4D, 0x57], "start": 0xE385, "end": 0xE3D3}, # lv08 + {"enemies": [0x01, 0x40, 0x4B], "start": 0xE432, "end": 0xE49B}, # lv0A + {"enemies": [0x08, 0x09, 0x3A, 0x44, 0x4D], "start": 0xE49C, "end": 0xE4F9}, # lv0B + {"enemies": [0x05, 0x06, 0x07, 0x08, 0x09, 0x0B, 0x3A, 0x3D], "start": 0xE5C2, "end": 0xE62B}, # lv0E + {"enemies": [0x05, 0x39, 0x57, 0x5B], "start": 0xE706, "end": 0xE77B}, # lv11 + {"enemies": [0x5C, 0x5E, 0x5F], "start": 0xE7C8, "end": 0xE822}, # lv13 + {"enemies": [0x22, 0x23, 0x25, 0x27], "start": 0xE823, "end": 0xE88F}, # lv14 + {"enemies": [0x07, 0x33, 0x34, 0x3D, 0x5D], "start": 0xE890, "end": 0xE8F6}, # lv15 + {"enemies": [0x01, 0x08, 0x09, 0x34, 0x3A, 0x55], "start": 0xE8F7, "end": 0xE954}, # lv16 + {"enemies": [0x68, 0x69], "start": 0xE99E, "end": 0xEA2E}, # lv18a + {"enemies": [0x6E, 0x6F], "start": 0xE99E, "end": 0xEA2E}, # lv18b + {"enemies": [0x01, 0x09], "start": 0xEB55, "end": 0xEBB5} # lv1F ] for level in level_list: i = level["start"] @@ -68,7 +72,7 @@ def randomize_enemies(data, random): elif sprite in level["enemies"]: randomize_sprite(data, random, level["enemies"], i) i += 3 - for i in range(0xE2A2, 0xE30B, 3): # lvl06 + for i in range(0xE2A2, 0xE30B, 3): # lvl06 sprite = sprite_extract(data[i], data[i+1]) if sprite == 0x4E: randomize_sprite(data, random, [0x4D, 0x4E, 0x51, 0x53], i) @@ -76,19 +80,19 @@ def randomize_enemies(data, random): randomize_sprite(data, random, [0x4D, 0x4F, 0x51, 0x53], i) elif sprite in (0x4D, 0x51, 0x53): randomize_sprite(data, random, [0x4D, 0x51, 0x53], i) - for i in range(0xE3D4, 0xE431, 3): # lvl09 + for i in range(0xE3D4, 0xE431, 3): # lvl09 sprite = sprite_extract(data[i], data[i + 1]) if sprite == 0x4F: randomize_sprite(data, random, [0x4D, 0x4F, 0x53, 0x5A, 0x5C], i) elif sprite in (0x4D, 0x53, 0x5a, 0x5C): randomize_sprite(data, random, [0x4D, 0x53, 0x5A, 0x5C], i) - for i in range(0xE4FA, 0xE560, 3): # lvl0c + for i in range(0xE4FA, 0xE560, 3): # lvl0c sprite = sprite_extract(data[i], data[i + 1]) if sprite == 0x49: randomize_sprite(data, random, [0x01, 0x47, 0x48, 0x49, 0x53], i) elif sprite in (0x01, 0x47, 0x48): randomize_sprite(data, random, [0x01, 0x47, 0x48, 0x53], i) - for i in range(0xE561, 0xE5C1, 3): # lvl0D + for i in range(0xE561, 0xE5C1, 3): # lvl0D sprite = sprite_extract(data[i], data[i + 1]) if sprite == 0x43: randomize_sprite(data, random, [0x09, 0x43, 0x4D, 0x53], i) @@ -96,13 +100,13 @@ def randomize_enemies(data, random): randomize_sprite(data, random, [0x09, 0x4C, 0x4D, 0x53], i) elif sprite in (0x09, 0x4D): randomize_sprite(data, random, [0x09, 0x4D, 0x53], i) - for i in range(0xE6C0, 0xE705, 3): # lvl10 + for i in range(0xE6C0, 0xE705, 3): # lvl10 sprite = sprite_extract(data[i], data[i + 1]) if sprite == 0x21: randomize_sprite(data, random, [0x01, 0x08, 0x20, 0x21, 0x3A, 0x55], i) elif sprite in (0x01, 0x08, 0x20, 0x3A, 0x55): randomize_sprite(data, random, [0x01, 0x08, 0x20, 0x3A, 0x55], i) - for i in range(0xE77C, 0xE7C7, 3): # lvl12 + for i in range(0xE77C, 0xE7C7, 3): # lvl12 sprite = sprite_extract(data[i], data[i + 1]) if sprite == 0x4D: randomize_sprite(data, random, [0x4D, 0x58], i) @@ -118,7 +122,7 @@ def randomize_enemies(data, random): sprite = sprite_extract(data[i], data[i + 1]) if sprite == 0xFF: i -= 2 - elif sprite in (0x0C,0x0D): + elif sprite in (0x0C, 0x0D): copy_sprite(data, sprite_insert(data[i], data[i+1], random.choice([0x0C, 0x0D])), i) i += 3 @@ -148,6 +152,7 @@ def randomize_platforms(data, random): for i in range(0xE9A3, 0xE9CE, 3): data[i] = (0x57 if data[i] == 0x5E else 0x38) + random.randint(0, 7) + def randomize_music(data, random): # overworld overworld_music_tracks = [0x05, 0x06, 0x0D, 0x0E, 0x10, 0x12, 0x1B, 0x1C, 0x1E] @@ -181,6 +186,8 @@ def generate_output(self, output_directory: str): data[rom_addresses["Auto_Scroll_Disable"]] = 0xAF if self.multiworld.golden_coins[self.player] != "vanilla": data[rom_addresses["Coin_Shuffle"]] = 0x40 + if self.multiworld.shuffle_midway_bells[self.player]: + data[rom_addresses["Disable_Midway_Bell"]] = 0xC9 rom_name = bytearray(f'AP{Utils.__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed:11}\0', 'utf8')[:21] @@ -198,15 +205,18 @@ def generate_output(self, output_directory: str): patched_path=rompath) patch.write() os.unlink(rompath) + class SuperMarioLand2DeltaPatch(APDeltaPatch): hash = "a8413347d5df8c9d14f97f0330d67bce" patch_file_ending = ".apsml2" game = "Super Mario Land 2" result_file_ending = ".gb" + @classmethod def get_source_data(cls) -> bytes: return get_base_rom_bytes() + def get_base_rom_bytes(): file_name = get_base_rom_path() @@ -220,13 +230,15 @@ def get_base_rom_bytes(): "Get the correct game and version, then dump it") return base_rom_bytes + def get_base_rom_path(): file_name = get_settings()["sml2_options"]["rom_file"] if not os.path.exists(file_name): file_name = Utils.user_path(file_name) return file_name + def write_bytes(data, byte_array, address): for byte in byte_array: data[address] = byte - address += 1 \ No newline at end of file + address += 1 diff --git a/worlds/marioland2/rom_addresses.py b/worlds/marioland2/rom_addresses.py index e9b92b8c9fe7..63b14a81e1f5 100644 --- a/worlds/marioland2/rom_addresses.py +++ b/worlds/marioland2/rom_addresses.py @@ -1,6 +1,6 @@ rom_addresses = { "Auto_Scroll_Disable": 0x4c0, - "Space_Physics": 0x4e8, + "Space_Physics": 0x4e7, "Auto_Scroll_Levels": 0x1f71, "Get_Hurt_To_Big_Mario": 0x31c7, "Get_Mushroom_A": 0x345c, @@ -11,6 +11,7 @@ "Invincibility_Star_C": 0x34a8, "Enable_Bubble": 0x34e5, "Coin_Shuffle": 0x304ce, + "Disable_Midway_Bell": 0x3ef1e, "Get_Carrot_C": 0x6092f, "Get_Mushroom_C": 0x60930, "Get_Fire_Flower_C": 0x60933, From e7057d51e1778b89c1180f4075803e5ad5c3af9f Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 18 Dec 2023 00:59:13 -0500 Subject: [PATCH 011/113] Golden Coins Required option and midway bells protection --- worlds/marioland2/__init__.py | 22 +++++++++++++++------- worlds/marioland2/basepatch.bsdiff4 | Bin 398 -> 406 bytes worlds/marioland2/client.py | 8 ++++++-- worlds/marioland2/options.py | 1 + worlds/marioland2/rom.py | 2 ++ worlds/marioland2/rom_addresses.py | 1 + 6 files changed, 25 insertions(+), 9 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 3835fd76c946..e0dfcda9e319 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -201,13 +201,20 @@ def set_rules(self): self.multiworld.get_location(level, self.player).access_rule = rule if self.multiworld.golden_coins[self.player] == "progressive": - self.multiworld.completion_condition[self.player] = lambda state: ( - state.has("Progressive Space Zone", self.player, 3) and state.has("Progressive Tree Zone", self.player, 4) - and state.has("Progressive Macro Zone", self.player, 4) and state.has("Progressive Pumpkin Zone", self.player, 4) - and state.has("Progressive Mario Zone", self.player, 4) and state.has("Progressive Turtle Zone", self.player, 3)) + self.multiworld.completion_condition[self.player] = lambda state: [ + state.has("Progressive Space Zone", self.player, 3), + state.has("Progressive Tree Zone", self.player, 4), + state.has("Progressive Macro Zone", self.player, 4), + state.has("Progressive Pumpkin Zone", self.player, 4), + state.has("Progressive Mario Zone", self.player, 4), + state.has("Progressive Turtle Zone", self.player, 3) + ].count(True) >= self.multiworld.required_golden_coins[self.player] else: - self.multiworld.completion_condition[self.player] = lambda state: state.has_all(["Tree Coin", "Space Coin", - "Macro Coin", "Pumpkin Coin", "Mario Coin", "Turtle Coin"], self.player) + self.multiworld.completion_condition[self.player] = lambda state: [ + state.has("Tree Coin", self.player), state.has("Space Coin", self.player), + state.has("Macro Coin", self.player), state.has("Pumpkin Coin", self.player), + state.has("Mario Coin", self.player), state.has("Turtle Coin", self.player) + ].count(True) >= self.multiworld.required_golden_coins[self.player] def create_items(self): item_counts = { @@ -272,7 +279,8 @@ def fill_slot_data(self): return { "mode": self.multiworld.difficulty_mode[self.player].value, "stars": max(len([loc for loc in self.multiworld.get_filled_locations() if loc.item.player == self.player - and loc.item.name == "Progressive Invincibility Star"]), 1) + and loc.item.name == "Progressive Invincibility Star"]), 1), + "midway_bells": self.multiworld.shuffle_midway_bells[self.player].value } def create_item(self, name: str) -> Item: diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index 3cceb38afe4fd460f6ef42094e2ee253a5cf1dd5..75a7b1dc5e958d3e92d87d8621b0b61c00c609ce 100644 GIT binary patch delta 249 zcmVz>%00000@CJusr zrw~H%0zGk0*mxvabI)P}1*#~Lm^#9$s`HGq<*Pj&6!#Z$ML1B97*!f8lRW`Zbi-IQ delta 241 zcmbQn+{Y~F6zt;Z=4N8x$N&L%CJIK?*PL7u%D}*1zyEiUqydA1v4a2y3nPmL1H)9I z1_72vM-~nSE{P|38yL1FN`+L-lrmym*wAos;wyg7UkaCl8W&tRk&!mRD1}W;eS*?d zf0qMbqnV|jY)y3Z;<6}U{L+)knV9K1#k+&?k)R->l8%E`e2asV2ZLbxA*F!EU{?L} z4lE4c8A@9j81#-sSwE4VYj@(^)ld1RV(iP-)^Gk(ltDSz}6ixDTjf!F)sHC?vz>{r}w0*V4=d#$sVgFyD|m>0MnpUu>b%7 diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 75deec6236e9..cd463f3876a3 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -114,9 +114,13 @@ async def game_watcher(self, ctx: BizHawkClientContext): (0x0848, modified_level_data, "CartRAM") ] + # TODO: Remove check that midway_bells is present, just here for temporary backwards compatibility + if midway_point == 0xFF and ctx.slot_data and "midway_bells" in ctx.slot_data and ctx.slot_data["midway_bells"]: + # after registering the check for the midway bell, clear the value just for safety. + data_writes.append((0x02A0, 0, "CartRAM")) + if "Auto Scroll" in items_received: - if auto_scroll_enabled == 0xaf: - # auto scroll has not yet been enabled + if auto_scroll_enabled == 0xaf: # auto scroll has not yet been enabled data_writes.append((rom_addresses["Auto_Scroll_Disable"], [0x7e], "ROM")) # if the current level is an auto scroll level, turn on auto scrolling now if auto_scroll_levels[current_level] == 1: diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index c2a2809c8159..48c63b543b97 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -79,6 +79,7 @@ class RandomizeMusic(Toggle): sml2options = { "golden_coins": GoldenCoins, + "required_golden_coins": GoldenCoinsRequired, "difficulty_mode": DifficultyMode, "shuffle_midway_bells":ShuffleMidwayBells, "shuffle_space_physics": ShuffleSpacePhysics, diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index efc06c879820..88c70d4eee09 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -189,6 +189,8 @@ def generate_output(self, output_directory: str): if self.multiworld.shuffle_midway_bells[self.player]: data[rom_addresses["Disable_Midway_Bell"]] = 0xC9 + data[rom_addresses["Required_Golden_Coins"]] = self.multiworld.required_golden_coins[self.player].value + 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))) diff --git a/worlds/marioland2/rom_addresses.py b/worlds/marioland2/rom_addresses.py index 63b14a81e1f5..1c824b9a66e5 100644 --- a/worlds/marioland2/rom_addresses.py +++ b/worlds/marioland2/rom_addresses.py @@ -11,6 +11,7 @@ "Invincibility_Star_C": 0x34a8, "Enable_Bubble": 0x34e5, "Coin_Shuffle": 0x304ce, + "Required_Golden_Coins": 0x306e9, "Disable_Midway_Bell": 0x3ef1e, "Get_Carrot_C": 0x6092f, "Get_Mushroom_C": 0x60930, From 6361e9577eba37d194a84cfe9e856660fc603aa1 Mon Sep 17 00:00:00 2001 From: Alchav Date: Thu, 21 Dec 2023 10:25:36 -0500 Subject: [PATCH 012/113] Open World, third release --- worlds/marioland2/basepatch.bsdiff4 | Bin 406 -> 429 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index 75a7b1dc5e958d3e92d87d8621b0b61c00c609ce..771cb364e0e487775ac1afc1caedd0cb89725c41 100644 GIT binary patch delta 338 zcmV-Y0j>U)1FZuQLQ_OZMn*I+LjV8(00000`H>MBe`i6!)&Kw)&`Ieyg8)DP00ICY z0RSKXF#rGtQ_vvErg>FSBm;PX-v?OV2vaY0B8*w41f(b zp{9e;f9gS|l^O#fpcsLshJXOmBM1>nfQ(EgnK3X4rkEk7n3w{3I~pJg6+u$97^H#& z=+-D_RBWKmP!GfaPZEevfL5#su?i`65L63Lssj8%pwoc`F8Kz>%00000f5J%>PXGWJP)O-Gf&f4O00I#J zAOIneq{L((8USSy^<0tx&2Vp{$0~MP9_g0ZgwA4h1 z0r?V^bU`FuffgVYCV&+K1PF+-!XjFM1tygc5CV8#=uiLy41HVOAo8Z8t8RkLpO84o zPj=s!_+q3{^d=61ey0#Z@d7<@PuO@QS#!@~9RmfbD3X{u!m6tCjI-sdJsuSI7ji{7 NP>>i^8Y`1r0Z|mWZ-W2; From 8830077fe9dc916e7498209c8a29e499feddf039 Mon Sep 17 00:00:00 2001 From: Alchav Date: Tue, 26 Dec 2023 21:05:10 -0500 Subject: [PATCH 013/113] Super Mario Land 2 version 4 release --- worlds/marioland2/LICENSE | 21 +++ worlds/marioland2/__init__.py | 257 ++++++++++++++++++---------- worlds/marioland2/basepatch.bsdiff4 | Bin 429 -> 436 bytes worlds/marioland2/client.py | 24 +-- worlds/marioland2/options.py | 13 +- worlds/marioland2/rom.py | 6 +- worlds/marioland2/rom_addresses.py | 2 +- 7 files changed, 204 insertions(+), 119 deletions(-) create mode 100644 worlds/marioland2/LICENSE diff --git a/worlds/marioland2/LICENSE b/worlds/marioland2/LICENSE new file mode 100644 index 000000000000..7ac515f432f1 --- /dev/null +++ b/worlds/marioland2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022-2023 Alex "Alchav" Avery + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index e0dfcda9e319..aff774a8e34f 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -15,52 +15,52 @@ 'Mushroom Zone': {'id': 0x00, 'ram_index': 0, 'type': 'level'}, 'Mushroom Zone Midway Bell': {'id': 0x00, 'ram_index': 0, 'clear_condition': ("Mushroom Zone Midway Bell", 1), 'type': 'bell'}, 'Scenic Course': {'id': 0x19, 'ram_index': 40, 'type': 'level'}, - 'Tree Zone 1 - Invincibility!': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Progressive Tree Zone", 1), 'type': 'level'}, + 'Tree Zone 1 - Invincibility!': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone Progression", 1), 'type': 'level'}, 'Tree Zone 1 - Invincibility! Midway Bell': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone 1 - Invincibility! Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone 2 - In the Trees': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Progressive Tree Zone", 2), 'type': 'level'}, - 'Tree Zone 2 - In the Trees Midway Bell': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Progressive Tree Zone", 2), 'type': 'bell'}, - 'Tree Zone 3 - The Exit': {'id': 0x04, 'ram_index': 4, 'clear_condition': ("Progressive Tree Zone", 3), 'type': 'level'}, - 'Tree Zone 4 - Honeybees': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Progressive Tree Zone", 3), 'type': 'level'}, + 'Tree Zone 2 - In the Trees': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Progression", 2), 'type': 'level'}, + 'Tree Zone 2 - In the Trees Midway Bell': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Progression", 2), 'type': 'bell'}, + 'Tree Zone 3 - The Exit': {'id': 0x04, 'ram_index': 4, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, + 'Tree Zone 4 - Honeybees': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, 'Tree Zone 4 - Honeybees Midway Bell': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone 4 - Honeybees Midway Bell", 1), 'type': 'bell'}, 'Tree Zone 5 - The Big Bird': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Coin", 1), 'type': 'level'}, 'Tree Zone 5 - The Big Bird Midway Bell': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Zone 5 - The Big Bird Midway Bell", 1), 'type': 'bell'}, 'Tree Zone - Secret Course': {'id': 0x1D, 'ram_index': 36, 'type': 'level'}, 'Hippo Zone': {'id': 0x11, 'ram_index': 31, 'type': 'level'}, - 'Space Zone 1 - Moon Stage': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Progressive Space Zone", 2), 'type': 'level'}, + 'Space Zone 1 - Moon Stage': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Progression", 1), 'type': 'level'}, 'Space Zone 1 - Moon Stage Midway Bell': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone 1 - Moon Stage Midway Bell", 1), 'type': 'bell'}, 'Space Zone - Secret Course': {'id': 0x1C, 'ram_index': 41, 'type': 'level'}, 'Space Zone 2 - Star Stage': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Coin", 1), 'type': 'level'}, 'Space Zone 2 - Star Stage Midway Bell': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Zone 2 - Star Stage Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 1 - The Ant Monsters': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Progressive Macro Zone", 1), 'type': 'level'}, + 'Macro Zone 1 - The Ant Monsters': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Progression", 1), 'type': 'level'}, 'Macro Zone 1 - The Ant Monsters Midway Bell': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone 1 - The Ant Monsters Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 2 - In the Syrup Sea': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Progressive Macro Zone", 2), 'type': 'level'}, + 'Macro Zone 2 - In the Syrup Sea': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone Progression", 2), 'type': 'level'}, 'Macro Zone 2 - In the Syrup Sea Midway Bell': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone 2 - In the Syrup Sea Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 3 - Fiery Mario-Special Agent': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Progressive Macro Zone", 3), 'type': 'level'}, + 'Macro Zone 3 - Fiery Mario-Special Agent': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone Progression", 3), 'type': 'level'}, 'Macro Zone 3 - Fiery Mario-Special Agent Midway Bell': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", 1), 'type': 'bell'}, 'Macro Zone 4 - One Mighty Mouse': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Coin", 1), 'type': 'level'}, 'Macro Zone 4 - One Mighty Mouse Midway Bell': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Zone 4 - One Mighty Mouse Midway Bell", 1), 'type': 'bell'}, 'Macro Zone - Secret Course': {'id': 0x1E, 'ram_index': 35, 'clear_condition': ("Macro Zone Secret", 1), 'type': 'level'}, - 'Pumpkin Zone 1 - Bat Course': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Progressive Pumpkin Zone", 1), 'type': 'level'}, + 'Pumpkin Zone 1 - Bat Course': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone Progression", 1), 'type': 'level'}, 'Pumpkin Zone 1 - Bat Course Midway Bell': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone 1 - Bat Course Midway Bell", 1), 'type': 'bell'}, - 'Pumpkin Zone 2 - Cyclops Course': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Progressive Pumpkin Zone", 2), 'type': 'level'}, - 'Pumpkin Zone 2 - Cyclops Course Midway Bell': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Progressive Pumpkin Zone", 2), 'type': 'bell'}, - 'Pumpkin Zone 3 - Ghost House': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Progressive Pumpkin Zone", 3), 'type': 'level'}, - 'Pumpkin Zone 3 - Ghost House Midway Bell': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Progressive Pumpkin Zone", 3), 'type': 'bell'}, + 'Pumpkin Zone 2 - Cyclops Course': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'level'}, + 'Pumpkin Zone 2 - Cyclops Course Midway Bell': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'bell'}, + 'Pumpkin Zone 3 - Ghost House': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'level'}, + 'Pumpkin Zone 3 - Ghost House Midway Bell': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'bell'}, "Pumpkin Zone 4 - Witch's Mansion": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'level'}, "Pumpkin Zone 4 - Witch's Mansion Midway Bell": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'bell'}, 'Pumpkin Zone - Secret Course 1': {'id': 0x1B, 'ram_index': 38, 'type': 'level'}, 'Pumpkin Zone - Secret Course 2': {'id': 0x1F, 'ram_index': 39, 'type': 'level'}, - 'Mario Zone 1 - Fiery Blocks': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Progressive Mario Zone", 1), 'type': 'level'}, + 'Mario Zone 1 - Fiery Blocks': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone Progression", 1), 'type': 'level'}, 'Mario Zone 1 - Fiery Blocks Midway Bell': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone 1 - Fiery Blocks Midway Bell", 1), 'type': 'bell'}, - 'Mario Zone 2 - Mario the Circus Star!': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Progressive Mario Zone", 2), 'type': 'level'}, + 'Mario Zone 2 - Mario the Circus Star!': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone Progression", 2), 'type': 'level'}, 'Mario Zone 2 - Mario the Circus Star! Midway Bell': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone 2 - Mario the Circus Star! Midway Bell", 1), 'type': 'bell'}, - 'Mario Zone 3 - Beware: Jagged Spikes': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Progressive Mario Zone", 3), 'type': 'level'}, + 'Mario Zone 3 - Beware: Jagged Spikes': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone Progression", 3), 'type': 'level'}, 'Mario Zone 3 - Beware: Jagged Spikes Midway Bell': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone 3 - Beware: Jagged Spikes Midway Bell", 1), 'type': 'bell'}, 'Mario Zone 4 - Three Mean Pigs!': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Coin", 1), 'type': 'level'}, 'Mario Zone 4 - Three Mean Pigs! Midway Bell': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Zone 4 - Three Mean Pigs! Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone 1 - Cheep Cheep Course': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Progressive Turtle Zone", 1), 'type': 'level'}, + 'Turtle Zone 1 - Cheep Cheep Course': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone Progression", 1), 'type': 'level'}, 'Turtle Zone 1 - Cheep Cheep Course Midway Bell': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone 1 - Cheep Cheep Course Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone 2 - Turtle Zone': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Progressive Turtle Zone", 2), 'type': 'level'}, + 'Turtle Zone 2 - Turtle Zone': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Progression", 2), 'type': 'level'}, 'Turtle Zone 2 - Turtle Zone Midway Bell': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone 2 - Turtle Zone Midway Bell", 1), 'type': 'bell'}, 'Turtle Zone 3 - Whale Course': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Coin", 1), 'type': 'level'}, 'Turtle Zone 3 - Whale Course Midway Bell': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Zone 3 - Whale Course Midway Bell", 1), 'type': 'bell'}, @@ -68,13 +68,13 @@ } items = { - "Progressive Space Zone": ItemClassification.progression, - "Progressive Tree Zone": ItemClassification.progression, - "Progressive Macro Zone": ItemClassification.progression, + "Space Zone Progression": ItemClassification.progression, + "Tree Zone Progression": ItemClassification.progression, + "Macro Zone Progression": ItemClassification.progression, "Macro Zone Secret": ItemClassification.progression_skip_balancing, - "Progressive Pumpkin Zone": ItemClassification.progression, - "Progressive Mario Zone": ItemClassification.progression, - "Progressive Turtle Zone": ItemClassification.progression, + "Pumpkin Zone Progression": ItemClassification.progression, + "Mario Zone Progression": ItemClassification.progression, + "Turtle Zone Progression": ItemClassification.progression, "Tree Coin": ItemClassification.progression_skip_balancing, "Space Coin": ItemClassification.progression_skip_balancing, "Macro Coin": ItemClassification.progression_skip_balancing, @@ -84,9 +84,11 @@ "Mushroom": ItemClassification.progression, "Fire Flower": ItemClassification.progression, "Carrot": ItemClassification.progression, - "Progressive Invincibility Star": ItemClassification.filler, "Space Physics": ItemClassification.progression, + "Hippo Bubble": ItemClassification.progression, + "Swim": ItemClassification.progression, "Easy Mode": ItemClassification.useful, + "Super Star Duration Increase": ItemClassification.filler, "Normal Mode": ItemClassification.trap, "Auto Scroll": ItemClassification.trap, "Mushroom Zone Midway Bell": ItemClassification.filler, @@ -95,7 +97,7 @@ "Tree Zone 4 - Honeybees Midway Bell": ItemClassification.filler, "Tree Zone 5 - The Big Bird Midway Bell": ItemClassification.filler, "Space Zone 1 - Moon Stage Midway Bell": ItemClassification.filler, - "Space Zone 2 - Star Stage Midway Bell": ItemClassification.filler, + "Space Zone 2 - Star Stage Midway Bell": ItemClassification.progression, "Macro Zone 1 - The Ant Monsters Midway Bell": ItemClassification.filler, "Macro Zone 2 - In the Syrup Sea Midway Bell": ItemClassification.filler, "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": ItemClassification.filler, @@ -104,12 +106,12 @@ "Pumpkin Zone 2 - Cyclops Course Midway Bell": ItemClassification.filler, "Pumpkin Zone 3 - Ghost House Midway Bell": ItemClassification.filler, "Pumpkin Zone 4 - Witch's Mansion Midway Bell": ItemClassification.filler, - "Mario Zone 1 - Fiery Blocks Midway Bell": ItemClassification.filler, + "Mario Zone 1 - Fiery Blocks Midway Bell": ItemClassification.progression, "Mario Zone 2 - Mario the Circus Star! Midway Bell": ItemClassification.filler, "Mario Zone 3 - Beware: Jagged Spikes Midway Bell": ItemClassification.filler, "Mario Zone 4 - Three Mean Pigs! Midway Bell": ItemClassification.filler, "Turtle Zone 1 - Cheep Cheep Course Midway Bell": ItemClassification.filler, - "Turtle Zone 2 - Turtle Zone Midway Bell": ItemClassification.filler, + "Turtle Zone 2 - Turtle Zone Midway Bell": ItemClassification.progression, "Turtle Zone 3 - Whale Course Midway Bell": ItemClassification.filler, } @@ -153,61 +155,130 @@ class MarioLand2World(World): def create_regions(self): menu_region = Region("Menu", self.player, self.multiworld) self.multiworld.regions.append(menu_region) - for location_name in locations: + created_regions = [] + for location_name, data in locations.items(): + if "Secret Course" in location_name: + region_name = location_name + elif "Mushroom Zone" in location_name: + region_name = "Mushroom Zone" + else: + 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 location_name == "Tree Zone - Secret Course": + region_to_connect = self.multiworld.get_region("Tree Zone 2", self.player) + elif location_name == "Space Zone - Secret Course": + region_to_connect = self.multiworld.get_region("Space Zone 1", self.player) + elif location_name == "Macro Zone - Secret Course": + region_to_connect = self.multiworld.get_region("Macro Zone 1", self.player) + elif location_name == "Pumpkin Zone - Secret Course 1": + region_to_connect = self.multiworld.get_region("Pumpkin Zone 2", self.player) + elif location_name == "Pumpkin Zone - Secret Course 2": + region_to_connect = self.multiworld.get_region("Pumpkin Zone 3", self.player) + elif location_name == "Turtle Zone - Secret Course": + region_to_connect = self.multiworld.get_region("Turtle Zone 2", self.player) + elif "-" in location_name and int(location_name.split(" ")[2]) > 1: + region_to_connect = self.multiworld.get_region(" ".join(location_name.split(" ")[:2]) + + f" {int(location_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 "Midway Bell" in location_name and not self.multiworld.shuffle_midway_bells[self.player]: continue - menu_region.locations.append(Location(self.player, location_name, self.location_name_to_id[location_name], menu_region)) + region.locations.append(Location(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)) def set_rules(self): - rules = { - "Space Zone 1 - Moon Stage": lambda state: state.has("Progressive Space Zone", self.player), # this is really hard though - "Space Zone - Secret Course": lambda state: state.has("Progressive Space Zone", self.player, 2) and state.has_any(["Space Physics", "Carrot"], self.player), - "Space Zone 2 - Star Stage": lambda state: state.has("Progressive Space Zone", self.player, 2) and state.has("Space Physics", self.player), - "Tree Zone 2 - In the Trees": lambda state: state.has("Progressive Tree Zone", self.player), - "Tree Zone - Secret Course": lambda state: state.has_all(["Progressive Tree Zone", "Carrot"], self.player), - "Tree Zone 3 - The Exit": lambda state: state.has("Progressive Tree Zone", self.player, 2), - "Tree Zone 4 - Honeybees": lambda state: state.has("Progressive Tree Zone", self.player, 2), - "Tree Zone 5 - The Big Bird": lambda state: state.has("Progressive Tree Zone", self.player, 3), - # You can use a Fire Flower to get the Secret Course from Macro Zone 1, or if you have every Progressive - # Macro Zone and the Macro Zone Secret paths, you can get here from the boss level - "Macro Zone - Secret Course": lambda state: state.has("Fire Flower", self.player) or (state.has("Macro Zone Secret", self.player) and state.has("Progressive Macro Zone", self.player, 3)), - "Macro Zone 2 - In the Syrup Sea": lambda state: state.has("Progressive Macro Zone", self.player), - "Macro Zone 3 - Fiery Mario-Special Agent": lambda state: state.has("Progressive Macro Zone", self.player, 2), - "Macro Zone 4 - One Mighty Mouse": lambda state: state.has("Progressive Macro Zone", self.player, 3) or (state.has("Fire Flower", self.player) and state.has("Macro Zone Secret", self.player)), - "Pumpkin Zone 2 - Cyclops Course": lambda state: state.has("Progressive Pumpkin Zone", self.player), + entrance_rules = { + "Menu -> Space Zone 1": lambda state: state.has_any(["Hippo Bubble", "Carrot"], self.player), + "Space Zone 1 -> Space Zone - Secret Course": lambda state: state.has_any(["Space Physics", "Carrot"], self.player), + "Space Zone 1 -> Space Zone 2": lambda state: state.has("Space Zone Progression", self.player), + "Tree Zone 1 -> Tree Zone 2": lambda state: state.has("Tree Zone Progression", self.player), + "Tree Zone 2 -> Tree Zone - Secret Course": lambda state: state.has("Carrot", self.player), + "Tree Zone 2 -> Tree Zone 3": lambda state: state.has("Tree Zone Progression", self.player, 2), + "Tree Zone 4 -> Tree Zone 5": lambda state: state.has("Tree Zone Progression", self.player, 3), + "Macro Zone 1 -> Macro Zone - Secret Course": lambda state: state.has("Fire Flower", self.player), + "Macro Zone - Secret Course -> Macro Zone 4": lambda state: state.has("Macro Zone Secret", self.player), + "Macro Zone 1 -> Macro Zone 2": lambda state: state.has("Macro Zone Progression", self.player), + "Macro Zone 2 -> Macro Zone 3": lambda state: state.has("Macro Zone Progression", self.player, 2), + "Macro Zone 3 -> Macro Zone 4": lambda state: state.has("Macro Zone Progression", self.player, 3), + "Macro Zone 4 -> Macro Zone - Secret Course": lambda state: state.has("Macro Zone Secret", self.player), + "Pumpkin Zone 1 -> Pumpkin Zone 2": lambda state: state.has("Pumpkin Zone Progression", self.player), # You can only spin jump as Big Mario or Fire Mario - "Pumpkin Zone - Secret Course 1": lambda state: state.has("Progressive Pumpkin Zone", self.player) and state.has_any(["Mushroom", "Fire Flower"], self.player), - "Pumpkin Zone 3 - Ghost House": lambda state: state.has("Progressive Pumpkin Zone", self.player, 2), - "Pumpkin Zone - Secret Course 2": lambda state: state.has("Progressive Pumpkin Zone", self.player, 2) and state.has("Carrot", self.player), - "Pumpkin Zone 4 - Witch's Mansion": lambda state: state.has("Progressive Pumpkin Zone", self.player, 3), - "Mario Zone 2 - Mario the Circus Star!": lambda state: state.has("Progressive Mario Zone", self.player), - "Mario Zone 3 - Beware: Jagged Spikes": lambda state: state.has("Progressive Mario Zone", self.player, 2), - "Mario Zone 4 - Three Mean Pigs!": lambda state: state.has("Progressive Mario Zone", self.player, 3), - "Turtle Zone 2 - Turtle Zone": lambda state: state.has("Progressive Turtle Zone", self.player), - # The powerups are needed not for the secret exit in Turtle Zone 2, but to fly over or take damage in the - # spikes in the secret course - "Turtle Zone - Secret Course": lambda state: state.has("Progressive Turtle Zone", self.player) and state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player), - "Turtle Zone 3 - Whale Course": lambda state: state.has("Progressive Turtle Zone", self.player, 2), + "Pumpkin Zone 2 -> Pumpkin Zone - Secret Course 1": lambda state: state.has("Swim", self.player) + and state.has_any(["Mushroom", "Fire Flower"], self.player), + "Pumpkin Zone 2 -> Pumpkin Zone 3": lambda state: state.has("Pumpkin Zone Progression", self.player, 2), + "Pumpkin Zone 3 -> Pumpkin Zone - Secret Course 2": lambda state: state.has("Carrot", self.player), + "Pumpkin Zone 3 -> Pumpkin Zone 4": lambda state: state.has("Pumpkin Zone Progression", self.player, 3), + "Mario Zone 1 -> Mario Zone 2": lambda state: state.has("Mario Zone Progression", self.player), + "Mario Zone 2 -> Mario Zone 3": lambda state: state.has("Mario Zone Progression", self.player, 2), + "Mario Zone 3 -> Mario Zone 4": lambda state: state.has("Mario Zone Progression", self.player, 3), + "Turtle Zone 1 -> Turtle Zone 2": lambda state: state.has("Turtle Zone Progression", self.player), + "Turtle Zone 2 -> Turtle Zone - Secret Course": lambda state: state.has("Swim", self.player), + "Turtle Zone 2 -> Turtle Zone 3": lambda state: state.has("Turtle Zone Progression", self.player, 2), + } + rules = { + "Hippo Zone": lambda state: state.has_any(["Hippo Bubble", "Carrot", "Swim"], self.player), + # It is possible, however quite difficulty, normally to beat the Moon Stage without Carrot or Space Physics. + # However, it requires somewhat precisely jumping off enemies. Enemy shuffle may make this impossible. + # I have not done any testing there. Instead, I will just always make one or the other required, since + # it is difficult without them anyway. + "Space Zone 1 - Moon Stage": lambda state: state.has_any(["Space Physics", "Carrot"], self.player), + # Without Space Physics, you must be able to take damage once to reach the bell, and again after the bell. + # If bells are not shuffled, then any one powerup will do, as you can get the bell and come back. + # Otherwise, you need the bell item from the item pool, or you need to be able to take damage twice in one + # visit. + "Space Zone 2 - Star Stage": lambda state: state.has("Space Physics", self.player) + or ((not state.multiworld.shuffle_midway_bells[self.player]) + and state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player)) + or (state.has("Mushroom", self.player) + and state.has_any(["Fire Flower", "Carrot"], self.player)) + or (state.has("Space Zone 2 - Star Stage Midway Bell", self.player) and state.has_any( + ["Mushroom", "Fire Flower", "Carrot"], self.player)), + "Space Zone 2 - Star Stage Midway Bell": lambda state: state.has_any( + ["Space Physics", "Space Zone 2 - Star Stage Midway Bell", "Mushroom", "Fire Flower", "Carrot"], + self.player), + "Space Zone - Secret Course": lambda state: state.has_any(["Space Physics", "Carrot"], self.player), + "Macro Zone 2 - In the Syrup Sea": lambda state: state.has("Swim", self.player), + "Macro Zone 2 - In the Syrup Sea Midway Bell": lambda state: state.has("Swim", self.player), + "Pumpkin Zone 2 - Cyclops Course": lambda state: state.has("Swim", self.player), + # It is possible to get as small mario, but it is a very precise jump and you will die afterward. + "Mario Zone 1 - Fiery Blocks Midway Bell": lambda state: state.has_any(["Mushroom", "Fire Flower", "Carrot", + "Mario Zone 1 - Fiery Blocks Midway Bell"], + self.player), + "Turtle Zone 1 - Cheep Cheep Course": lambda state: state.has_any(["Swim", "Carrot"], self.player), + "Turtle Zone 2 - Turtle Zone": lambda state: state.has("Swim", self.player), + "Turtle Zone 2 - Turtle Zone Midway Bell": lambda state: state.has_any( + ["Swim", "Turtle Zone 2 - Turtle Zone Midway Bell"], self.player), + "Turtle Zone - Secret Course": lambda state: state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player), } - if self.multiworld.shuffle_midway_bells[self.player]: - # copy level access rules onto midway point rules - for midway_loc in [loc for loc in locations if "Midway Bell" in loc]: - level_loc = midway_loc[:-12] - if level_loc in rules: - rules[midway_loc] = rules[level_loc] + for entrance, rule in entrance_rules.items(): + self.multiworld.get_entrance(entrance, self.player).access_rule = rule for level, rule in rules.items(): - self.multiworld.get_location(level, self.player).access_rule = rule + if ("Midway Bell" not in level) or self.multiworld.shuffle_midway_bells[self.player]: + self.multiworld.get_location(level, self.player).access_rule = rule if self.multiworld.golden_coins[self.player] == "progressive": self.multiworld.completion_condition[self.player] = lambda state: [ - state.has("Progressive Space Zone", self.player, 3), - state.has("Progressive Tree Zone", self.player, 4), - state.has("Progressive Macro Zone", self.player, 4), - state.has("Progressive Pumpkin Zone", self.player, 4), - state.has("Progressive Mario Zone", self.player, 4), - state.has("Progressive Turtle Zone", self.player, 3) + state.has("Space Zone Progression", self.player, 3), + state.has("Tree Zone Progression", self.player, 4), + state.has("Macro Zone Progression", self.player, 4), + state.has("Pumpkin Zone Progression", self.player, 4), + state.has("Mario Zone Progression", self.player, 4), + state.has("Turtle Zone Progression", self.player, 3) ].count(True) >= self.multiworld.required_golden_coins[self.player] else: self.multiworld.completion_condition[self.player] = lambda state: [ @@ -218,27 +289,30 @@ def set_rules(self): def create_items(self): item_counts = { - "Progressive Space Zone": 2, - "Progressive Tree Zone": 3, - "Progressive Macro Zone": 3, + "Space Zone Progression": 1, + "Tree Zone Progression": 3, + "Macro Zone Progression": 3, "Macro Zone Secret": 1, - "Progressive Pumpkin Zone": 3, - "Progressive Mario Zone": 3, - "Progressive Turtle Zone": 2, + "Pumpkin Zone Progression": 3, + "Mario Zone Progression": 3, + "Turtle Zone Progression": 2, "Mushroom": 1, "Fire Flower": 1, "Carrot": 1, - "Progressive Invincibility Star": 4, + "Space Physics": 1, + "Hippo Bubble": 1, + "Swim": 1, + "Super Star Duration Increase": 2, } if self.multiworld.golden_coins[self.player] == "vanilla": for item, location_name in ( - ("Macro Coin", "Mario Zone 4 - Three Mean Pigs!"), - ("Mario Coin", "Tree Zone 5 - The Big Bird"), + ("Mario Coin", "Mario Zone 4 - Three Mean Pigs!"), + ("Tree Coin", "Tree Zone 5 - The Big Bird"), ("Space Coin", "Space Zone 2 - Star Stage"), - ("Pumpkin Coin", "Macro Zone 4 - One Mighty Mouse"), - ("Turtle Coin", "Pumpkin Zone 4 - Witch's Mansion"), - ("Tree Coin", "Turtle Zone 3 - Whale Course") + ("Macro Coin", "Macro Zone 4 - One Mighty Mouse"), + ("Pumpkin Coin", "Pumpkin Zone 4 - Witch's Mansion"), + ("Turtle Coin", "Turtle Zone 3 - Whale Course") ): location = self.multiworld.get_location(location_name, self.player) location.place_locked_item(self.create_item(item)) @@ -260,16 +334,10 @@ def create_items(self): elif self.multiworld.difficulty_mode[self.player] == "normal_to_easy": item_counts["Easy Mode"] = 1 else: - item_counts["Progressive Invincibility Star"] += 1 - - if self.multiworld.shuffle_space_physics[self.player]: - item_counts["Progressive Invincibility Star"] -= 1 - item_counts["Space Physics"] = 1 - else: - self.multiworld.push_precollected(self.create_item("Space Physics")) + item_counts["Super Star Duration Increase"] += 1 if self.multiworld.auto_scroll_trap[self.player]: - item_counts["Progressive Invincibility Star"] -= 1 + item_counts["Super Star Duration Increase"] -= 1 item_counts["Auto Scroll"] = 1 for item_name, count in item_counts.items(): @@ -279,7 +347,7 @@ def fill_slot_data(self): return { "mode": self.multiworld.difficulty_mode[self.player].value, "stars": max(len([loc for loc in self.multiworld.get_filled_locations() if loc.item.player == self.player - and loc.item.name == "Progressive Invincibility Star"]), 1), + and loc.item.name == "Super Star Duration Increase"]), 1), "midway_bells": self.multiworld.shuffle_midway_bells[self.player].value } @@ -287,7 +355,7 @@ 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 "Progressive Invincibility Star" + return "Super Star Duration Increase" def modify_multidata(self, multidata: dict): rom_name = bytearray(f'AP{Utils.__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed:11}\0', @@ -296,5 +364,6 @@ def modify_multidata(self, multidata: dict): new_name = base64.b64encode(bytes(rom_name)).decode() multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]] + class MarioLand2Item(Item): game = "Super Mario Land 2" \ No newline at end of file diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index 771cb364e0e487775ac1afc1caedd0cb89725c41..ed84d25bc5fac4bffc5e95e12f358ef0f82383f1 100644 GIT binary patch delta 286 zcmV+(0pb3w1GEDYLQ_OZMn*I+LjV8(0000000EH_TYuc!bxr^P6Mw(^a#?TyAoBtk zCWHlO4&;S4c^$j!t8UPJEhM?0*fHVU^pa1{> z0MG#S0x3#p82~gHFiil&Xuu(+MwrrlU}dnNg;KAXx#|IQ@`R{ZsVJc8P$%LL6O@5X z=mgJNM1NDB6iFZ%AV37`g-{mEDi0(GDOZ6(5CT|VxBvhGrVGe(kr+0_pu>)MQ_-+a z?XMz+zep~{^M4O99z3C7va@0bwgV7>gz>;zq&!_|OHX!{3)q(8j|6yMP%;DX6Ar_L kRaUQ@zE!2qH(&U>k}1N3hUVLo5`X{ua#?TyAag(v zCge2!F8(DRvN43s9;8{6e79fdwx43W5N1fHy#Z02OaQ*4xr|Y=@+UpiAXMK6etA z9%|1uTcBqt&erhr0PXgM)KSte6i)(ig$0F!b7LcK(0&Hw-a diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index cd463f3876a3..3fcd6d5de2b0 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -55,12 +55,12 @@ async def game_watcher(self, ctx: BizHawkClientContext): items_received = [list(items.keys())[item.item - START_IDS] for item in ctx.items_received] progressive_coins = { - "Progressive Space Zone": 3, - "Progressive Tree Zone": 4, - "Progressive Macro Zone": 4, - "Progressive Pumpkin Zone": 4, - "Progressive Mario Zone": 4, - "Progressive Turtle Zone": 3 + "Space Zone Progression": 3, + "Tree Zone Progression": 4, + "Macro Zone Progression": 4, + "Pumpkin Zone Progression": 4, + "Mario Zone Progression": 4, + "Turtle Zone Progression": 3 } for level_item, count in progressive_coins.items(): if items_received.count(level_item) >= count: @@ -83,7 +83,8 @@ async def game_watcher(self, ctx: BizHawkClientContext): else: total_stars = 5 - invincibility_length = int((832.0 / total_stars) * items_received.count("Progressive Invincibility Star")) + invincibility_length = int((832.0 / (total_stars + 1)) + * (items_received.count("Super Star Duration Increase") + 1)) if "Easy Mode" in items_received: difficulty_mode = 1 @@ -108,16 +109,15 @@ async def game_watcher(self, ctx: BizHawkClientContext): (rom_addresses["Get_Fire_Flower_C"], [00] if "Fire Flower" in items_received else [0xc8], "ROM"), (rom_addresses["Invincibility_Star_A"], [(invincibility_length >> 8) + 1], "ROM"), (rom_addresses["Invincibility_Star_B"], [invincibility_length & 0xFF], "ROM"), - (rom_addresses["Invincibility_Star_C"], [4] if "Progressive Invincibility Star" in items_received else [0], "ROM"), - (rom_addresses["Enable_Bubble"], [0xcb, 0xd7] if "Progressive Space Zone" in items_received else [0, 0], "ROM"), + (rom_addresses["Enable_Bubble"], [0xcb, 0xd7] if "Hippo Bubble" in items_received else [0, 0], "ROM"), + (rom_addresses["Enable_Swim"], [0xcb, 0xcf] if "Swim" in items_received else [0, 0], "ROM"), (0x02E4, [difficulty_mode], "CartRAM"), (0x0848, modified_level_data, "CartRAM") ] - # TODO: Remove check that midway_bells is present, just here for temporary backwards compatibility - if midway_point == 0xFF and ctx.slot_data and "midway_bells" in ctx.slot_data and ctx.slot_data["midway_bells"]: + if midway_point == 0xFF and ctx.slot_data and ctx.slot_data["midway_bells"]: # after registering the check for the midway bell, clear the value just for safety. - data_writes.append((0x02A0, 0, "CartRAM")) + data_writes.append((0x02A0, [0], "CartRAM")) if "Auto Scroll" in items_received: if auto_scroll_enabled == 0xaf: # auto scroll has not yet been enabled diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 48c63b543b97..95c83c8e7314 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -4,8 +4,8 @@ class GoldenCoins(Choice): """Vanilla: The coins are found in their original locations. Shuffled: The coins are shuffled into the item pool. - Progressive: The coins are at the end of the Progressive Level item chains. For example, there will be a third - Progressive Space Zone item, and the final one received will grant the Space Coin. + Progressive: The coins are at the end of the Level Progression chains. For example, there will be a third + Space Zone Progression item, and the final one received will grant the Space Coin. You will see a Golden Coin being received when defeating bosses regardless of whether you are actually getting a coin.""" display_name = "Golden Coins" @@ -41,12 +41,6 @@ class ShuffleMidwayBells(Toggle): display_name = "Shuffle Midway Bells" -class ShuffleSpacePhysics(Toggle): - """Oh, no! There is Earth gravity on the moon and in space! Find the missing Space Physics item to restore - proper order to the universe.""" - display_name = "Shuffle Space Physics" - - class RandomizeEnemies(Toggle): """Randomize enemies throughout levels.""" display_name = "Randomize Enemies" @@ -81,8 +75,7 @@ class RandomizeMusic(Toggle): "golden_coins": GoldenCoins, "required_golden_coins": GoldenCoinsRequired, "difficulty_mode": DifficultyMode, - "shuffle_midway_bells":ShuffleMidwayBells, - "shuffle_space_physics": ShuffleSpacePhysics, + "shuffle_midway_bells": ShuffleMidwayBells, "randomize_enemies": RandomizeEnemies, "randomize_platforms": RandomizePlatforms, "auto_scroll_levels": AutoScrollLevels, diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 88c70d4eee09..562f09340edc 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -207,7 +207,8 @@ def generate_output(self, output_directory: str): patched_path=rompath) patch.write() os.unlink(rompath) - + + class SuperMarioLand2DeltaPatch(APDeltaPatch): hash = "a8413347d5df8c9d14f97f0330d67bce" @@ -218,7 +219,8 @@ class SuperMarioLand2DeltaPatch(APDeltaPatch): @classmethod def get_source_data(cls) -> bytes: return get_base_rom_bytes() - + + def get_base_rom_bytes(): file_name = get_base_rom_path() diff --git a/worlds/marioland2/rom_addresses.py b/worlds/marioland2/rom_addresses.py index 1c824b9a66e5..da3d17a1248c 100644 --- a/worlds/marioland2/rom_addresses.py +++ b/worlds/marioland2/rom_addresses.py @@ -1,6 +1,7 @@ rom_addresses = { "Auto_Scroll_Disable": 0x4c0, "Space_Physics": 0x4e7, + "Enable_Swim": 0x1d17, "Auto_Scroll_Levels": 0x1f71, "Get_Hurt_To_Big_Mario": 0x31c7, "Get_Mushroom_A": 0x345c, @@ -8,7 +9,6 @@ "Get_Carrot_A": 0x347e, "Invincibility_Star_A": 0x349e, "Invincibility_Star_B": 0x34a3, - "Invincibility_Star_C": 0x34a8, "Enable_Bubble": 0x34e5, "Coin_Shuffle": 0x304ce, "Required_Golden_Coins": 0x306e9, From 32a3ea216bf61dd52a066c48e452c2e982f47402 Mon Sep 17 00:00:00 2001 From: Alchav Date: Wed, 27 Dec 2023 17:05:44 -0500 Subject: [PATCH 014/113] Super Mario Land 2 version 5 release --- worlds/marioland2/__init__.py | 2 +- worlds/marioland2/options.py | 2 +- worlds/marioland2/rom.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index aff774a8e34f..c09e4ee09039 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -261,7 +261,7 @@ def set_rules(self): "Turtle Zone 2 - Turtle Zone": lambda state: state.has("Swim", self.player), "Turtle Zone 2 - Turtle Zone Midway Bell": lambda state: state.has_any( ["Swim", "Turtle Zone 2 - Turtle Zone Midway Bell"], self.player), - "Turtle Zone - Secret Course": lambda state: state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player), + "Turtle Zone - Secret Course": lambda state: state.has_any(["Fire Flower", "Carrot"], self.player), } for entrance, rule in entrance_rules.items(): diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 95c83c8e7314..e70a9d4c85cf 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -56,7 +56,7 @@ class AutoScrollLevels(NamedRange): Certain levels are excluded.""" display_name = "Auto Scroll Levels" range_start = 0 - range_end = 19 + range_end = 18 special_range_names = {"vanilla": -1} default = -1 diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 562f09340edc..3c5a89bcba34 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -128,7 +128,7 @@ def randomize_enemies(data, random): def randomize_auto_scroll_levels(data, random, n): - eligible_levels = [0, 1, 2, 3, 5, 8, 9, 11, 13, 14, 16, 17, 18, 19, 20, 23, 25, 30, 31] + eligible_levels = [0, 1, 2, 3, 5, 8, 9, 11, 13, 14, 16, 17, 19, 20, 23, 25, 30, 31] auto_scroll_levels = random.sample(eligible_levels, n) for i in eligible_levels: data[rom_addresses["Auto_Scroll_Levels"] + i] = 1 if i in auto_scroll_levels else 0 From c0206761c3fa0803ca9b5e4bfd139e08209d3273 Mon Sep 17 00:00:00 2001 From: Alchav Date: Wed, 27 Dec 2023 19:30:34 -0500 Subject: [PATCH 015/113] Docs and finishing touches --- worlds/marioland2/__init__.py | 41 +++++++--- .../marioland2/docs/en_Super Mario Land 2.md | 41 ++++++++++ worlds/marioland2/docs/setup_en.md | 75 +++++++++++++++++++ 3 files changed, 145 insertions(+), 12 deletions(-) create mode 100644 worlds/marioland2/docs/en_Super Mario Land 2.md create mode 100644 worlds/marioland2/docs/setup_en.md diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index c09e4ee09039..74cf87240955 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -2,8 +2,9 @@ import Utils import settings + from worlds.AutoWorld import World, WebWorld -from BaseClasses import Region, Location, Item, ItemClassification +from BaseClasses import Region, Location, Item, ItemClassification, Tutorial from . import client from .rom import generate_output, SuperMarioLand2DeltaPatch @@ -84,8 +85,8 @@ "Mushroom": ItemClassification.progression, "Fire Flower": ItemClassification.progression, "Carrot": ItemClassification.progression, - "Space Physics": ItemClassification.progression, - "Hippo Bubble": ItemClassification.progression, + "Space Physics": ItemClassification.progression_skip_balancing, + "Hippo Bubble": ItemClassification.progression_skip_balancing, "Swim": ItemClassification.progression, "Easy Mode": ItemClassification.useful, "Super Star Duration Increase": ItemClassification.filler, @@ -97,7 +98,7 @@ "Tree Zone 4 - Honeybees Midway Bell": ItemClassification.filler, "Tree Zone 5 - The Big Bird Midway Bell": ItemClassification.filler, "Space Zone 1 - Moon Stage Midway Bell": ItemClassification.filler, - "Space Zone 2 - Star Stage Midway Bell": ItemClassification.progression, + "Space Zone 2 - Star Stage Midway Bell": ItemClassification.progression_skip_balancing, "Macro Zone 1 - The Ant Monsters Midway Bell": ItemClassification.filler, "Macro Zone 2 - In the Syrup Sea Midway Bell": ItemClassification.filler, "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": ItemClassification.filler, @@ -106,12 +107,12 @@ "Pumpkin Zone 2 - Cyclops Course Midway Bell": ItemClassification.filler, "Pumpkin Zone 3 - Ghost House Midway Bell": ItemClassification.filler, "Pumpkin Zone 4 - Witch's Mansion Midway Bell": ItemClassification.filler, - "Mario Zone 1 - Fiery Blocks Midway Bell": ItemClassification.progression, + "Mario Zone 1 - Fiery Blocks Midway Bell": ItemClassification.progression_skip_balancing, "Mario Zone 2 - Mario the Circus Star! Midway Bell": ItemClassification.filler, "Mario Zone 3 - Beware: Jagged Spikes Midway Bell": ItemClassification.filler, "Mario Zone 4 - Three Mean Pigs! Midway Bell": ItemClassification.filler, "Turtle Zone 1 - Cheep Cheep Course Midway Bell": ItemClassification.filler, - "Turtle Zone 2 - Turtle Zone Midway Bell": ItemClassification.progression, + "Turtle Zone 2 - Turtle Zone Midway Bell": ItemClassification.progression_skip_balancing, "Turtle Zone 3 - Whale Course Midway Bell": ItemClassification.filler, } @@ -126,6 +127,19 @@ class SML2RomFile(settings.UserFilePath): 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): game = "Super Mario Land 2" @@ -135,7 +149,12 @@ class MarioLand2World(World): location_name_to_id = {location_name: ID for ID, location_name in enumerate(locations, START_IDS)} 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") + or item_name.endswith("Secret")}, + "Bells": {item_name for item_name in items if "Bell" in item_name}, "Coins": {item_name for item_name in items if "Coin" in item_name}, "Powerups": {"Mushroom", "Fire Flower", "Carrot"}, "Difficulties": {"Easy Mode", "Normal Mode"} @@ -202,7 +221,8 @@ def create_regions(self): def set_rules(self): entrance_rules = { "Menu -> Space Zone 1": lambda state: state.has_any(["Hippo Bubble", "Carrot"], self.player), - "Space Zone 1 -> Space Zone - Secret Course": lambda state: state.has_any(["Space Physics", "Carrot"], self.player), + "Space Zone 1 -> Space Zone - Secret Course": lambda state: state.has_any( + ["Space Physics", "Carrot"], self.player), "Space Zone 1 -> Space Zone 2": lambda state: state.has("Space Zone Progression", self.player), "Tree Zone 1 -> Tree Zone 2": lambda state: state.has("Tree Zone Progression", self.player), "Tree Zone 2 -> Tree Zone - Secret Course": lambda state: state.has("Carrot", self.player), @@ -249,15 +269,12 @@ def set_rules(self): "Space Zone 2 - Star Stage Midway Bell": lambda state: state.has_any( ["Space Physics", "Space Zone 2 - Star Stage Midway Bell", "Mushroom", "Fire Flower", "Carrot"], self.player), - "Space Zone - Secret Course": lambda state: state.has_any(["Space Physics", "Carrot"], self.player), "Macro Zone 2 - In the Syrup Sea": lambda state: state.has("Swim", self.player), "Macro Zone 2 - In the Syrup Sea Midway Bell": lambda state: state.has("Swim", self.player), "Pumpkin Zone 2 - Cyclops Course": lambda state: state.has("Swim", self.player), # It is possible to get as small mario, but it is a very precise jump and you will die afterward. - "Mario Zone 1 - Fiery Blocks Midway Bell": lambda state: state.has_any(["Mushroom", "Fire Flower", "Carrot", - "Mario Zone 1 - Fiery Blocks Midway Bell"], - self.player), - "Turtle Zone 1 - Cheep Cheep Course": lambda state: state.has_any(["Swim", "Carrot"], self.player), + "Mario Zone 1 - Fiery Blocks Midway Bell": lambda state: state.has_any( + ["Mushroom", "Fire Flower", "Carrot", "Mario Zone 1 - Fiery Blocks Midway Bell"], self.player), "Turtle Zone 2 - Turtle Zone": lambda state: state.has("Swim", self.player), "Turtle Zone 2 - Turtle Zone Midway Bell": lambda state: state.has_any( ["Swim", "Turtle Zone 2 - Turtle Zone Midway Bell"], self.player), diff --git a/worlds/marioland2/docs/en_Super Mario Land 2.md b/worlds/marioland2/docs/en_Super Mario Land 2.md new file mode 100644 index 000000000000..fc5f155116e2 --- /dev/null +++ b/worlds/marioland2/docs/en_Super Mario Land 2.md @@ -0,0 +1,41 @@ +# Super Mario Land 2: 6 Golden Coins + +## Where is the settings page? + +The [player settings page for this game](../player-settings) contains all the options you need to configure and export a +config file. + +## What items and locations get shuffled? + +Completing a level's normal exit OR secret exit results in a location check for that level. Completing a normal exit +does not automatically unlock the next level, but completing a secret exit does always bring you to the secret course. +This means that if you can complete the secret exit, you do not need to return to complete the normal exit, as the check +for the level will have been registered already. + +Unlocking "normal exit" paths requires shuffled zone progression items. So, for example, finding or receiving a "Tree +Zone Progression" will unlock the path from Tree Zone 1 to Tree Zone 2. + +Besides the zone progression unlocks, the following items are always shuffled: +- Mushroom: required to become Big Mario. If you are Fire or Bunny Mario and take damage, and have not obtained the +Mushroom, you will drop straight down to Small Mario. +- Fire Flower: required to become Fire Mario. +- Carrot: required to become Bunny Mario. +- Swim: Mario will fall through water as though it is air until this is obtained. +- Hippo Bubble: required to use the bubbles in Hippo Zone to fly. +- Space Physics: the Space Zone levels will have normal gravity until this is obtained. +- Super Star Duration Increase: you begin with a drastically lowered invincibility star duration, and these items will +increase it. + +Additionally, the following items can be shuffled depending on your YAML settings: +- The 6 Golden Coins: note that the game will still show you the coin being sent to the castle when defeating a boss +regardless of whether the coin is actually obtained in that location. +- Midway Bells: ringing bells results in a location check, and the midway check points are shuffled as items. +Note that you may have to backtrack from the midway point to reach some secret exits! +- Normal Mode/Easy Mode: you can start the game in Normal Mode with an Easy Mode "upgrade" in the item pool, or start in +Easy Mode with a Normal Mode "trap" item, swapping the difficulty. +- Auto Scroll: auto-scrolling levels can be set to not auto scroll until this trap item is received. + +## When the player receives an item, what happens? + +There is no in-game indication that an item has been received. You will need to watch the client to be sure you're aware +of the items you've received. \ No newline at end of file diff --git a/worlds/marioland2/docs/setup_en.md b/worlds/marioland2/docs/setup_en.md new file mode 100644 index 000000000000..31210d04cd22 --- /dev/null +++ b/worlds/marioland2/docs/setup_en.md @@ -0,0 +1,75 @@ +# Setup Guide for Super Mario Land 2: 6 Golden Coins + +## Important + +As we are using BizHawk, this guide is only applicable to Windows and Linux systems. + +## Required Software + +- BizHawk: [BizHawk Releases from TASVideos](https://tasvideos.org/BizHawk/ReleaseHistory) + - Version 2.9.1 is recommended. + - Detailed installation instructions for BizHawk can be found at the above link. + - Windows users must run the prereq installer first, which can also be found at the above link. +- The built-in Archipelago client, which can be installed [here](https://github.com/ArchipelagoMW/Archipelago/releases) +- A Super Mario Land 2: 6 Golden Coins version 1.0 ROM file. The Archipelago community cannot provide these. + +## Configuring BizHawk + +Once BizHawk has been installed, open EmuHawk and change the following settings: + +- Under Config > Customize > Advanced, make sure the box for AutoSaveRAM is checked, and click the 5s button. + This reduces the possibility of losing save data in emulator crashes. +- Under Config > Customize, check the "Run in background" box. This will prevent disconnecting from the client while +EmuHawk is running in the background. + +It is strongly recommended to associate GB rom extensions (\*.gb) to the EmuHawk we've just installed. +To do so, we simply have to search any Gameboy rom we happened to own, right click and select "Open with...", unfold +the list that appears and select the bottom option "Look for another application", then browse to the BizHawk folder +and select EmuHawk.exe. + +## Configuring your YAML file + +### What is a YAML file and why do I need one? + +Your YAML file contains a set of configuration options which provide the generator with information about how it should +generate your game. Each player of a multiworld will provide their own YAML file. This setup allows each player to enjoy +an experience customized for their taste, and different players in the same multiworld can all have different options. + +### Where do I get a YAML file? + +You can generate a yaml or download a template by visiting the [Super Mario Land 2 Player Settings Page](/games/Super%20Mario%20Land%202/player-settings) + +## Joining a MultiWorld Game + +### Generating and Patching a Game + +1. Create your settings file (YAML). +2. Follow the general Archipelago instructions for [generating a game](../../Archipelago/setup/en#generating-a-game). +This will generate an output file for you. Your patch file will have a `.apsml2` file extension. +3. Open `ArchipelagoLauncher.exe` +4. Select "Open Patch" on the left side and select your patch file. +5. If this is your first time patching, you will be prompted to locate your vanilla ROM. +6. A patched `.gb` file will be created in the same place as the patch file. +7. On your first time opening a patch with BizHawk Client, you will also be asked to locate `EmuHawk.exe` in your +BizHawk install. + +You must connect Super Mario Land 2 to a server, even for a single player game, or progress cannot be made. + +### Connect to the Multiserver + +By default, opening a patch file will do steps 1-5 below for you automatically. Even so, keep them in your memory just +in case you have to close and reopen a window mid-game for some reason. + +1. Super Mario Land 2 uses Archipelago's BizHawk Client. If the client isn't still open from when you patched your +game, you can re-open it from the launcher. +2. Ensure EmuHawk is running the patched ROM. +3. In EmuHawk, go to `Tools > Lua Console`. This window must stay open while playing. +4. In the Lua Console window, go to `Script > Open Script…`. +5. Navigate to your Archipelago install folder and open `data/lua/connector_bizhawk_generic.lua`. +6. The emulator may freeze every few seconds until it manages to connect to the client. This is expected. The BizHawk +Client window should indicate that it connected and recognized Super Mario Land 2. +7. To connect the client to the server, enter your room's address and port (e.g. `archipelago.gg:38281`) into the +top text field of the client and click Connect. + +To connect the client to the multiserver simply put `
:` on the textfield on top and press enter (if the +server uses password, type in the bottom textfield `/connect
: [password]`) \ No newline at end of file From 5b5d7a06d08b840cbd5f18dc383589c3381e4292 Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 30 Dec 2023 23:03:51 -0500 Subject: [PATCH 016/113] Fix Hippo Zone --- worlds/marioland2/basepatch.bsdiff4 | Bin 436 -> 465 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index ed84d25bc5fac4bffc5e95e12f358ef0f82383f1..0ad024705350a541cfe80ca4520cef41465a0f93 100644 GIT binary patch delta 307 zcmV-30nGli1JMH!LQ_OZMn*I+LjV8(000009gz`Se^3+QIsgC{f4}>7U2p(k^AZp! z1*yac00NZ~fDr|NWfVw2%UncmumH?~C`v|}JwN~&158GSfDHg?>Uy5kPkK~1$&*77 ziK76F1i}EoO*CndpcqLiOpFPDOpJ^G1ZZFhrT_q`@nC1=7*cpaPpZvf+PZ^8F@e=>-YWQJ_aAWzu{<4wW`Y?Fo%q_6^J5CDm+l0YSKl0c%cfg+cZKqLT1WD}?W z02;QqA<^c@UY8sM@>}Zmy>5+zyJEhI_F-oCY5adM)+x+3VRl_mCygphf6^MwI`dNGJ*LodA<# F0Z~Hoe3JkG delta 285 zcmV+&0pkAA1GEDXLQ_OZMn*I+LjV8(000000Fe<~f85)3P5=NCf4}>3S#SU#^8y$s z1*ydd00NZ~fDr_MK@4Kx5601Z5bpwmi#Gy_4P00000 z&;a!UDN1M=05lmeO#sAbz#*nan9_Yi0>H&1}gs51lD4^<4C*lwjlz~m? z1kYMTe^Z_mNgx>@Km_ZBP!`N84 Date: Sat, 30 Dec 2023 23:15:56 -0500 Subject: [PATCH 017/113] Pipe Traversal --- worlds/marioland2/__init__.py | 87 ++++++++++++++++++++++-------- worlds/marioland2/client.py | 8 +++ worlds/marioland2/options.py | 7 +++ worlds/marioland2/rom_addresses.py | 8 +++ 4 files changed, 89 insertions(+), 21 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 74cf87240955..5af811301556 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -88,22 +88,23 @@ "Space Physics": ItemClassification.progression_skip_balancing, "Hippo Bubble": ItemClassification.progression_skip_balancing, "Swim": ItemClassification.progression, - "Easy Mode": ItemClassification.useful, + "Pipe Traversal": ItemClassification.progression, "Super Star Duration Increase": ItemClassification.filler, + "Easy Mode": ItemClassification.useful, "Normal Mode": ItemClassification.trap, "Auto Scroll": ItemClassification.trap, "Mushroom Zone Midway Bell": ItemClassification.filler, "Tree Zone 1 - Invincibility! Midway Bell": ItemClassification.filler, - "Tree Zone 3 - The Exit Midway Bell": ItemClassification.filler, - "Tree Zone 4 - Honeybees Midway Bell": ItemClassification.filler, + "Tree Zone 2 - In the Trees Midway Bell": ItemClassification.progression_skip_balancing, + "Tree Zone 4 - Honeybees Midway Bell": ItemClassification.progression_skip_balancing, "Tree Zone 5 - The Big Bird Midway Bell": ItemClassification.filler, "Space Zone 1 - Moon Stage Midway Bell": ItemClassification.filler, "Space Zone 2 - Star Stage Midway Bell": ItemClassification.progression_skip_balancing, - "Macro Zone 1 - The Ant Monsters Midway Bell": ItemClassification.filler, - "Macro Zone 2 - In the Syrup Sea Midway Bell": ItemClassification.filler, - "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": ItemClassification.filler, + "Macro Zone 1 - The Ant Monsters Midway Bell": ItemClassification.progression_skip_balancing, + "Macro Zone 2 - In the Syrup Sea Midway Bell": ItemClassification.progression_skip_balancing, + "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": ItemClassification.progression_skip_balancing, "Macro Zone 4 - One Mighty Mouse Midway Bell": ItemClassification.filler, - "Pumpkin Zone 1 - Bat Course Midway Bell": ItemClassification.filler, + "Pumpkin Zone 1 - Bat Course Midway Bell": ItemClassification.progression_skip_balancing, "Pumpkin Zone 2 - Cyclops Course Midway Bell": ItemClassification.filler, "Pumpkin Zone 3 - Ghost House Midway Bell": ItemClassification.filler, "Pumpkin Zone 4 - Witch's Mansion Midway Bell": ItemClassification.filler, @@ -225,10 +226,12 @@ def set_rules(self): ["Space Physics", "Carrot"], self.player), "Space Zone 1 -> Space Zone 2": lambda state: state.has("Space Zone Progression", self.player), "Tree Zone 1 -> Tree Zone 2": lambda state: state.has("Tree Zone Progression", self.player), - "Tree Zone 2 -> Tree Zone - Secret Course": lambda state: state.has("Carrot", self.player), + "Tree Zone 2 -> Tree Zone - Secret Course": lambda state: state.has_all(["Carrot", "Pipe Traversal"], + self.player), "Tree Zone 2 -> Tree Zone 3": lambda state: state.has("Tree Zone Progression", self.player, 2), "Tree Zone 4 -> Tree Zone 5": lambda state: state.has("Tree Zone Progression", self.player, 3), - "Macro Zone 1 -> Macro Zone - Secret Course": lambda state: state.has("Fire Flower", self.player), + "Macro Zone 1 -> Macro Zone - Secret Course": lambda state: state.has_all(["Fire Flower", "Pipe Traversal"], + self.player), "Macro Zone - Secret Course -> Macro Zone 4": lambda state: state.has("Macro Zone Secret", self.player), "Macro Zone 1 -> Macro Zone 2": lambda state: state.has("Macro Zone Progression", self.player), "Macro Zone 2 -> Macro Zone 3": lambda state: state.has("Macro Zone Progression", self.player, 2), @@ -236,8 +239,8 @@ def set_rules(self): "Macro Zone 4 -> Macro Zone - Secret Course": lambda state: state.has("Macro Zone Secret", self.player), "Pumpkin Zone 1 -> Pumpkin Zone 2": lambda state: state.has("Pumpkin Zone Progression", self.player), # You can only spin jump as Big Mario or Fire Mario - "Pumpkin Zone 2 -> Pumpkin Zone - Secret Course 1": lambda state: state.has("Swim", self.player) - and state.has_any(["Mushroom", "Fire Flower"], self.player), + "Pumpkin Zone 2 -> Pumpkin Zone - Secret Course 1": lambda state: state.has_all( + ["Swim", "Pipe Traversal"], self.player) and state.has_any(["Mushroom", "Fire Flower"], self.player), "Pumpkin Zone 2 -> Pumpkin Zone 3": lambda state: state.has("Pumpkin Zone Progression", self.player, 2), "Pumpkin Zone 3 -> Pumpkin Zone - Secret Course 2": lambda state: state.has("Carrot", self.player), "Pumpkin Zone 3 -> Pumpkin Zone 4": lambda state: state.has("Pumpkin Zone Progression", self.player, 3), @@ -245,7 +248,8 @@ def set_rules(self): "Mario Zone 2 -> Mario Zone 3": lambda state: state.has("Mario Zone Progression", self.player, 2), "Mario Zone 3 -> Mario Zone 4": lambda state: state.has("Mario Zone Progression", self.player, 3), "Turtle Zone 1 -> Turtle Zone 2": lambda state: state.has("Turtle Zone Progression", self.player), - "Turtle Zone 2 -> Turtle Zone - Secret Course": lambda state: state.has("Swim", self.player), + "Turtle Zone 2 -> Turtle Zone - Secret Course": lambda state: state.has_all(["Swim", "Pipe Traversal"], + self.player), "Turtle Zone 2 -> Turtle Zone 3": lambda state: state.has("Turtle Zone Progression", self.player, 2), } rules = { @@ -269,16 +273,44 @@ def set_rules(self): "Space Zone 2 - Star Stage Midway Bell": lambda state: state.has_any( ["Space Physics", "Space Zone 2 - Star Stage Midway Bell", "Mushroom", "Fire Flower", "Carrot"], self.player), - "Macro Zone 2 - In the Syrup Sea": lambda state: state.has("Swim", self.player), - "Macro Zone 2 - In the Syrup Sea Midway Bell": lambda state: state.has("Swim", self.player), - "Pumpkin Zone 2 - Cyclops Course": lambda state: state.has("Swim", self.player), + "Tree Zone 2 - In the Trees": lambda state: state.has_any(["Pipe Traversal", + "Tree Zone 2 - In the Trees Midway Bell"], self.player), + "Tree Zone 2 - In the Trees Midway Bell": lambda state: state.has_any(["Pipe Traversal", + "Tree Zone 2 - In the Trees Midway Bell"], self.player), + "Tree Zone 4 - Honeybees": lambda state: state.has("Pipe Traversal", self.player), + "Tree Zone 4 - Honeybees Midway Bell": lambda state: state.has_any(["Pipe Traversal", + "Tree Zone 4 - Honeybees Midway Bell"], self.player), + "Tree Zone 5 - The Big Bird": lambda state: state.has("Pipe Traversal", self.player), + "Macro Zone 1 - The Ant Monsters": lambda state: state.has_any( + ["Pipe Traversal", "Macro Zone 1 - The Ant Monsters Midway Bell"], self.player), + "Macro Zone 1 - The Ant Monsters Midway Bell": lambda state: state.has_any( + ["Pipe Traversal", "Macro Zone 1 - The Ant Monsters Midway Bell"], self.player), + "Macro Zone 2 - In the Syrup Sea": lambda state: state.has_all(["Swim", "Pipe Traversal"], self.player), + "Macro Zone 2 - In the Syrup Sea Midway Bell": lambda state: state.has_all( + ["Swim", "Pipe Traversal"], self.player) or state.has("Macro Zone 2 - In the Syrup Sea Midway Bell", + self.player), + "Macro Zone 3 - Fiery Mario-Special Agent": lambda state: state.has_any( + ["Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", "Pipe Traversal"], self.player), + "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": lambda state: state.has_any( + ["Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", "Pipe Traversal"], self.player), + "Macro Zone 4 - One Mighty Mouse": lambda state: state.has("Pipe Traversal", self.player), + "Pumpkin Zone 1 - Bat Course": lambda state: state.has_any( + ["Pipe Traversal", "Pumpkin Zone 1 - Bat Course Midway Bell"], self.player), + "Pumpkin Zone 1 - Bat Course Midway Bell": lambda state: state.has_any( + ["Pipe Traversal", "Pumpkin Zone 1 - Bat Course Midway Bell"], self.player), + "Pumpkin Zone 2 - Cyclops Course": lambda state: state.has_all(["Swim", "Pipe Traversal"], self.player), + "Pumpkin Zone 4 - Witch's Mansion": lambda state: state.has("Pipe Traversal", self.player), + "Mario Zone 1 - Fiery Blocks": lambda state: state.has("Pipe Traversal", self.player), # It is possible to get as small mario, but it is a very precise jump and you will die afterward. - "Mario Zone 1 - Fiery Blocks Midway Bell": lambda state: state.has_any( - ["Mushroom", "Fire Flower", "Carrot", "Mario Zone 1 - Fiery Blocks Midway Bell"], self.player), - "Turtle Zone 2 - Turtle Zone": lambda state: state.has("Swim", self.player), + "Mario Zone 1 - Fiery Blocks Midway Bell": lambda state: (state.has_any( + ["Mushroom", "Fire Flower", "Carrot"], self.player) and state.has("Pipe Traversal", self.player)) + or state.has("Mario Zone 1 - Fiery Blocks Midway Bell", self.player), + "Mario Zone 4 - Three Mean Pigs!": lambda state: state.has("Pipe Traversal", self.player), + "Turtle Zone 2 - Turtle Zone": lambda state: state.has_all(["Swim", "Pipe Traversal"], self.player), "Turtle Zone 2 - Turtle Zone Midway Bell": lambda state: state.has_any( ["Swim", "Turtle Zone 2 - Turtle Zone Midway Bell"], self.player), "Turtle Zone - Secret Course": lambda state: state.has_any(["Fire Flower", "Carrot"], self.player), + "Turtle Zone 3 - Whale Course": lambda state: state.has("Pipe Traversal", self.player), } for entrance, rule in entrance_rules.items(): @@ -289,7 +321,7 @@ def set_rules(self): self.multiworld.get_location(level, self.player).access_rule = rule if self.multiworld.golden_coins[self.player] == "progressive": - self.multiworld.completion_condition[self.player] = lambda state: [ + self.multiworld.completion_condition[self.player] = lambda state: ([ state.has("Space Zone Progression", self.player, 3), state.has("Tree Zone Progression", self.player, 4), state.has("Macro Zone Progression", self.player, 4), @@ -297,12 +329,14 @@ def set_rules(self): state.has("Mario Zone Progression", self.player, 4), state.has("Turtle Zone Progression", self.player, 3) ].count(True) >= self.multiworld.required_golden_coins[self.player] + and state.has("Pipe Traversal", self.player)) else: - self.multiworld.completion_condition[self.player] = lambda state: [ + self.multiworld.completion_condition[self.player] = lambda state: ([ state.has("Tree Coin", self.player), state.has("Space Coin", self.player), state.has("Macro Coin", self.player), state.has("Pumpkin Coin", self.player), state.has("Mario Coin", self.player), state.has("Turtle Coin", self.player) ].count(True) >= self.multiworld.required_golden_coins[self.player] + and state.has("Pipe Traversal", self.player)) def create_items(self): item_counts = { @@ -353,10 +387,21 @@ def create_items(self): else: item_counts["Super Star Duration Increase"] += 1 + if self.multiworld.shuffle_pipe_traversal[self.player]: + item_counts["Super Star Duration Increase"] -= 1 + item_counts["Pipe Traversal"] = 1 + else: + self.multiworld.push_precollected(self.create_item("Pipe Traversal")) + if self.multiworld.auto_scroll_trap[self.player]: item_counts["Super Star Duration Increase"] -= 1 item_counts["Auto Scroll"] = 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 + item_counts["Super Star Duration Increase"] += 1 + for item_name, count in item_counts.items(): self.multiworld.itempool += [self.create_item(item_name) for _ in range(count)] @@ -383,4 +428,4 @@ def modify_multidata(self, multidata: dict): class MarioLand2Item(Item): - game = "Super Mario Land 2" \ No newline at end of file + game = "Super Mario Land 2" diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 3fcd6d5de2b0..195d1b2c4d42 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -111,6 +111,14 @@ async def game_watcher(self, ctx: BizHawkClientContext): (rom_addresses["Invincibility_Star_B"], [invincibility_length & 0xFF], "ROM"), (rom_addresses["Enable_Bubble"], [0xcb, 0xd7] if "Hippo Bubble" in items_received else [0, 0], "ROM"), (rom_addresses["Enable_Swim"], [0xcb, 0xcf] if "Swim" in items_received else [0, 0], "ROM"), + (rom_addresses["Pipe_Traversal_A"], [16] if "Pipe Traversal" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_B"], [32] if "Pipe Traversal" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_C"], [48] if "Pipe Traversal" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_D"], [64] if "Pipe Traversal" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_SFX_A"], [5] if "Pipe Traversal" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_SFX_B"], [5] if "Pipe Traversal" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_SFX_C"], [5] if "Pipe Traversal" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_SFX_D"], [5] if "Pipe Traversal" in items_received else [0], "ROM"), (0x02E4, [difficulty_mode], "CartRAM"), (0x0848, modified_level_data, "CartRAM") ] diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index e70a9d4c85cf..9957c1187fef 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -41,6 +41,12 @@ class ShuffleMidwayBells(Toggle): display_name = "Shuffle Midway Bells" +class ShufflePipeTraversal(Toggle): + """Shuffle a Pipe Traversal item into the item pool, which is required to enter pipes. + Note that being unable to enter pipes is very limiting and affects nearly half of all levels.""" + display_name = "Shuffle Pipe Traversal" + + class RandomizeEnemies(Toggle): """Randomize enemies throughout levels.""" display_name = "Randomize Enemies" @@ -76,6 +82,7 @@ class RandomizeMusic(Toggle): "required_golden_coins": GoldenCoinsRequired, "difficulty_mode": DifficultyMode, "shuffle_midway_bells": ShuffleMidwayBells, + "shuffle_pipe_traversal": ShufflePipeTraversal, "randomize_enemies": RandomizeEnemies, "randomize_platforms": RandomizePlatforms, "auto_scroll_levels": AutoScrollLevels, diff --git a/worlds/marioland2/rom_addresses.py b/worlds/marioland2/rom_addresses.py index da3d17a1248c..f794cf187ef5 100644 --- a/worlds/marioland2/rom_addresses.py +++ b/worlds/marioland2/rom_addresses.py @@ -1,6 +1,14 @@ rom_addresses = { "Auto_Scroll_Disable": 0x4c0, "Space_Physics": 0x4e7, + "Pipe_Traversal_A": 0x11a4, + "Pipe_Traversal_SFX_A": 0x11a9, + "Pipe_Traversal_B": 0x11d6, + "Pipe_Traversal_SFX_B": 0x11e7, + "Pipe_Traversal_C": 0x1226, + "Pipe_Traversal_SFX_C": 0x123f, + "Pipe_Traversal_D": 0x1256, + "Pipe_Traversal_SFX_D": 0x125b, "Enable_Swim": 0x1d17, "Auto_Scroll_Levels": 0x1f71, "Get_Hurt_To_Big_Mario": 0x31c7, From 1d6ce1a8a1dde324fe3e69b76b4658a61a32afa5 Mon Sep 17 00:00:00 2001 From: Alchav Date: Sun, 31 Dec 2023 14:21:42 -0500 Subject: [PATCH 018/113] EnergyLink --- worlds/marioland2/__init__.py | 3 +- worlds/marioland2/client.py | 59 ++++++++++++++++--- .../marioland2/docs/en_Super Mario Land 2.md | 6 +- worlds/marioland2/options.py | 10 +++- worlds/marioland2/rom.py | 4 ++ worlds/marioland2/rom_addresses.py | 1 + 6 files changed, 71 insertions(+), 12 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 5af811301556..5bfbb5b8b4d6 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -410,7 +410,8 @@ def fill_slot_data(self): "mode": self.multiworld.difficulty_mode[self.player].value, "stars": max(len([loc for loc in self.multiworld.get_filled_locations() if loc.item.player == self.player and loc.item.name == "Super Star Duration Increase"]), 1), - "midway_bells": self.multiworld.shuffle_midway_bells[self.player].value + "midway_bells": self.multiworld.shuffle_midway_bells[self.player].value, + "energy_link": self.multiworld.energy_link[self.player].value } def create_item(self, name: str) -> Item: diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 195d1b2c4d42..58cd93ca218a 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -9,6 +9,8 @@ logger = logging.getLogger("Client") +BANK_EXCHANGE_RATE = 20000000000 + class MarioLand2Client(BizHawkClient): system = ("GB", "SGB") @@ -35,22 +37,25 @@ async def set_auth(self, ctx): async def game_watcher(self, ctx: BizHawkClientContext): from . import locations, items, START_IDS - game_loaded_check, level_data, music, auto_scroll_enabled, auto_scroll_levels, current_level, midway_point = \ + (game_loaded_check, level_data, music, auto_scroll_enabled, auto_scroll_levels, current_level, midway_point, + bcd_lives) = \ await read(ctx.bizhawk_ctx, [(0x0046, 10, "CartRAM"), (0x0848, 42, "CartRAM"), (0x0469, 1, "CartRAM"), (rom_addresses["Auto_Scroll_Disable"], 1, "ROM"), (rom_addresses["Auto_Scroll_Levels"], 32, "ROM"), (0x0269, 1, "CartRAM"), - (0x02A0, 1, "CartRAM")]) - - if game_loaded_check != b'\x124Vx\xff\xff\xff\xff\xff\xff': - return + (0x02A0, 1, "CartRAM"), + (0x022C, 1, "CartRAM")]) current_level = int.from_bytes(current_level) midway_point = int.from_bytes(midway_point) music = int.from_bytes(music) auto_scroll_enabled = int.from_bytes(auto_scroll_enabled) - level_data = list(level_data) + lives = bcd_lives.hex() + + # there is no music in the title screen demos, this is how we guard against anything in the demos registering. + if game_loaded_check != b'\x124Vx\xff\xff\xff\xff\xff\xff' or music == 0: + return items_received = [list(items.keys())[item.item - START_IDS] for item in ctx.items_received] @@ -95,6 +100,21 @@ async def game_watcher(self, ctx: BizHawkClientContext): else: difficulty_mode = 0 + new_lives = int(lives) + energy_link_add = None + # TODO: temporary check to ensure energy_link key for backwards compatibility + if ctx.slot_data and "energy_link" in ctx.slot_data and ctx.slot_data["energy_link"]: + if new_lives == 0: + if (f"EnergyLink{ctx.team}" in ctx.stored_data + and ctx.stored_data[f"EnergyLink{ctx.team}"] > BANK_EXCHANGE_RATE): + new_lives = 1 + energy_link_add = -BANK_EXCHANGE_RATE + elif new_lives > 1: + energy_link_add = BANK_EXCHANGE_RATE * (new_lives - 1) + new_lives = 1 + # convert back to binary-coded-decimal + new_lives = int(str(new_lives), 16) + data_writes = [ (rom_addresses["Space_Physics"], [0x7e] if "Space Physics" in items_received else [0xaf], "ROM"), (rom_addresses["Get_Hurt_To_Big_Mario"], [1] if "Mushroom" in items_received else [0], "ROM"), @@ -119,6 +139,7 @@ async def game_watcher(self, ctx: BizHawkClientContext): (rom_addresses["Pipe_Traversal_SFX_B"], [5] if "Pipe Traversal" in items_received else [0], "ROM"), (rom_addresses["Pipe_Traversal_SFX_C"], [5] if "Pipe Traversal" in items_received else [0], "ROM"), (rom_addresses["Pipe_Traversal_SFX_D"], [5] if "Pipe Traversal" in items_received else [0], "ROM"), + (0x022c, [new_lives], "CartRAM"), (0x02E4, [difficulty_mode], "CartRAM"), (0x0848, modified_level_data, "CartRAM") ] @@ -134,7 +155,15 @@ async def game_watcher(self, ctx: BizHawkClientContext): if auto_scroll_levels[current_level] == 1: data_writes.append((0x02C8, [0x01], "CartRAM")) - await guarded_write(ctx.bizhawk_ctx, data_writes, [(0x0848, level_data, "CartRAM")]) + success = await guarded_write(ctx.bizhawk_ctx, data_writes, [(0x0848, level_data, "CartRAM"), + (0x022C, [int.from_bytes(bcd_lives)], "CartRAM")]) + + if success and energy_link_add is not None: + await ctx.send_msgs([{ + "cmd": "Set", "key": f"EnergyLink{ctx.team}", "operations": + [{"operation": "add", "value": energy_link_add}, + {"operation": "max", "value": 0}], + }]) if not ctx.server or not ctx.server.socket.open or ctx.server.socket.closed: return @@ -145,4 +174,18 @@ async def game_watcher(self, ctx: BizHawkClientContext): if music == 0x18: await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) - ctx.finished_game = True \ No newline at end of file + ctx.finished_game = True + + def on_package(self, ctx: BizHawkClientContext, cmd: str, args: dict): + super().on_package(ctx, cmd, args) + if cmd == 'Connected': + ctx.set_notify(f"EnergyLink{ctx.team}") + ctx.ui.enable_energy_link() + ctx.ui.energy_link_label.text = "Lives: Standby" + elif cmd == "SetReply" and args["key"].startswith("EnergyLink"): + if ctx.ui: + ctx.ui.energy_link_label.text = f"Lives: {int(args['value'] / BANK_EXCHANGE_RATE)}" + elif cmd == "Retrieved": + if f"EnergyLink{ctx.team}" in args["keys"]: + if ctx.ui: + ctx.ui.energy_link_label.text = f"Lives: {int(args['keys'][f'EnergyLink{ctx.team}'] / BANK_EXCHANGE_RATE)}" \ No newline at end of file diff --git a/worlds/marioland2/docs/en_Super Mario Land 2.md b/worlds/marioland2/docs/en_Super Mario Land 2.md index fc5f155116e2..d7569df94b6f 100644 --- a/worlds/marioland2/docs/en_Super Mario Land 2.md +++ b/worlds/marioland2/docs/en_Super Mario Land 2.md @@ -23,8 +23,6 @@ Mushroom, you will drop straight down to Small Mario. - Swim: Mario will fall through water as though it is air until this is obtained. - Hippo Bubble: required to use the bubbles in Hippo Zone to fly. - Space Physics: the Space Zone levels will have normal gravity until this is obtained. -- Super Star Duration Increase: you begin with a drastically lowered invincibility star duration, and these items will -increase it. Additionally, the following items can be shuffled depending on your YAML settings: - The 6 Golden Coins: note that the game will still show you the coin being sent to the castle when defeating a boss @@ -34,6 +32,10 @@ Note that you may have to backtrack from the midway point to reach some secret e - Normal Mode/Easy Mode: you can start the game in Normal Mode with an Easy Mode "upgrade" in the item pool, or start in Easy Mode with a Normal Mode "trap" item, swapping the difficulty. - Auto Scroll: auto-scrolling levels can be set to not auto scroll until this trap item is received. +- Pipe Traversal: required to enter pipes. +- Super Star Duration Increase: you begin with a drastically lowered invincibility star duration, and these items will +increase it. This is the main filler item generated to fill out the item pool. One or more will appear unless all other +options to add items are on. ## When the player receives an item, what happens? diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 9957c1187fef..a5af06131941 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -77,6 +77,13 @@ class RandomizeMusic(Toggle): display_name = "Randomize Music" +class EnergyLink(Toggle): + """All extra lives beyond 1 are transferred into the server's shared EnergyLink storage. If you drop to 0, + 1 will be replenished if there is sufficient energy stored.""" + display_name = "Energy Link" + default = 1 + + sml2options = { "golden_coins": GoldenCoins, "required_golden_coins": GoldenCoinsRequired, @@ -87,5 +94,6 @@ class RandomizeMusic(Toggle): "randomize_platforms": RandomizePlatforms, "auto_scroll_levels": AutoScrollLevels, "auto_scroll_trap": AutoScrollTrap, - "randomize_music": RandomizeMusic + "randomize_music": RandomizeMusic, + "energy_link": EnergyLink } \ No newline at end of file diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 3c5a89bcba34..ebda6f5d6033 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -191,6 +191,10 @@ def generate_output(self, output_directory: str): data[rom_addresses["Required_Golden_Coins"]] = self.multiworld.required_golden_coins[self.player].value + if self.multiworld.energy_link[self.player]: + # start with 1 life if Energy Link is on so that you don't deposit lives at the start of the game. + data[rom_addresses["Starting_Lives"]] = 1 + 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))) diff --git a/worlds/marioland2/rom_addresses.py b/worlds/marioland2/rom_addresses.py index f794cf187ef5..f8146d65202b 100644 --- a/worlds/marioland2/rom_addresses.py +++ b/worlds/marioland2/rom_addresses.py @@ -11,6 +11,7 @@ "Pipe_Traversal_SFX_D": 0x125b, "Enable_Swim": 0x1d17, "Auto_Scroll_Levels": 0x1f71, + "Starting_Lives": 0x2920, "Get_Hurt_To_Big_Mario": 0x31c7, "Get_Mushroom_A": 0x345c, "Get_Fire_Flower_A": 0x346d, From 7bc39b1e7c3d77fdab9acb01664213b85f1aee5c Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 1 Jan 2024 00:38:06 -0500 Subject: [PATCH 019/113] Fix EnergyLink hopefully --- worlds/marioland2/client.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 58cd93ca218a..64665c922a4f 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -106,7 +106,8 @@ async def game_watcher(self, ctx: BizHawkClientContext): if ctx.slot_data and "energy_link" in ctx.slot_data and ctx.slot_data["energy_link"]: if new_lives == 0: if (f"EnergyLink{ctx.team}" in ctx.stored_data - and ctx.stored_data[f"EnergyLink{ctx.team}"] > BANK_EXCHANGE_RATE): + and ctx.stored_data[f"EnergyLink{ctx.team}"] + and ctx.stored_data[f"EnergyLink{ctx.team}"] >= BANK_EXCHANGE_RATE): new_lives = 1 energy_link_add = -BANK_EXCHANGE_RATE elif new_lives > 1: @@ -180,12 +181,12 @@ def on_package(self, ctx: BizHawkClientContext, cmd: str, args: dict): super().on_package(ctx, cmd, args) if cmd == 'Connected': ctx.set_notify(f"EnergyLink{ctx.team}") - ctx.ui.enable_energy_link() - ctx.ui.energy_link_label.text = "Lives: Standby" + if ctx.slot_data and "energy_link" in ctx.slot_data and ctx.slot_data["energy_link"] and ctx.ui: + ctx.ui.enable_energy_link() + ctx.ui.energy_link_label.text = "Lives: Standby" elif cmd == "SetReply" and args["key"].startswith("EnergyLink"): if ctx.ui: ctx.ui.energy_link_label.text = f"Lives: {int(args['value'] / BANK_EXCHANGE_RATE)}" elif cmd == "Retrieved": - if f"EnergyLink{ctx.team}" in args["keys"]: - if ctx.ui: - ctx.ui.energy_link_label.text = f"Lives: {int(args['keys'][f'EnergyLink{ctx.team}'] / BANK_EXCHANGE_RATE)}" \ No newline at end of file + if f"EnergyLink{ctx.team}" in args["keys"] and args['keys'][f'EnergyLink{ctx.team}'] and ctx.ui: + ctx.ui.energy_link_label.text = f"Lives: {int(args['keys'][f'EnergyLink{ctx.team}'] / BANK_EXCHANGE_RATE)}" From 9adf4766e62fd61189dccb198799b3f137f3d01e Mon Sep 17 00:00:00 2001 From: Alchav Date: Thu, 4 Jan 2024 10:46:12 -0500 Subject: [PATCH 020/113] Fix Tree Zone 2 midway bell item --- worlds/marioland2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 5bfbb5b8b4d6..88850491ccaa 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -19,7 +19,7 @@ 'Tree Zone 1 - Invincibility!': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone Progression", 1), 'type': 'level'}, 'Tree Zone 1 - Invincibility! Midway Bell': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone 1 - Invincibility! Midway Bell", 1), 'type': 'bell'}, 'Tree Zone 2 - In the Trees': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Progression", 2), 'type': 'level'}, - 'Tree Zone 2 - In the Trees Midway Bell': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Progression", 2), 'type': 'bell'}, + 'Tree Zone 2 - In the Trees Midway Bell': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone 2 - In the Trees Midway Bell", 1), 'type': 'bell'}, 'Tree Zone 3 - The Exit': {'id': 0x04, 'ram_index': 4, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, 'Tree Zone 4 - Honeybees': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, 'Tree Zone 4 - Honeybees Midway Bell': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone 4 - Honeybees Midway Bell", 1), 'type': 'bell'}, From 450e7582a28dcf07eebb72006ad639f53022fa83 Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 6 Jan 2024 20:51:51 -0500 Subject: [PATCH 021/113] Version 9 release: shuffle secret exits --- worlds/marioland2/__init__.py | 283 ++++++++++++++++++---------- worlds/marioland2/basepatch.bsdiff4 | Bin 465 -> 720 bytes worlds/marioland2/client.py | 47 +++-- worlds/marioland2/options.py | 26 ++- worlds/marioland2/rom.py | 4 +- 5 files changed, 220 insertions(+), 140 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 88850491ccaa..b26e6df085ec 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -12,6 +12,27 @@ START_IDS = 7770000 + +def has_pipe_right(state, player): + return state.has_any(["Pipe Traversal - Right", "Pipe Traversal"], player) + + +def has_pipe_left(state, player): + return state.has_any(["Pipe Traversal - Left", "Pipe Traversal"], player) + + +def has_pipe_down(state, player): + return state.has_any(["Pipe Traversal - Down", "Pipe Traversal"], player) + + +def has_pipe_up(state, player): + return state.has_any(["Pipe Traversal - Up", "Pipe Traversal"], player) + + +def has_level_progression(state, item, player, count=1): + return state.count(item, player) + (state.count(item + " x2", player) * 2) >= count + + locations = { 'Mushroom Zone': {'id': 0x00, 'ram_index': 0, 'type': 'level'}, 'Mushroom Zone Midway Bell': {'id': 0x00, 'ram_index': 0, 'clear_condition': ("Mushroom Zone Midway Bell", 1), 'type': 'bell'}, @@ -19,6 +40,7 @@ 'Tree Zone 1 - Invincibility!': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone Progression", 1), 'type': 'level'}, 'Tree Zone 1 - Invincibility! Midway Bell': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone 1 - Invincibility! Midway Bell", 1), 'type': 'bell'}, 'Tree Zone 2 - In the Trees': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Progression", 2), 'type': 'level'}, + 'Tree Zone 2 - In the Trees Secret Exit': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Secret", 1), 'type': 'secret'}, 'Tree Zone 2 - In the Trees Midway Bell': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone 2 - In the Trees Midway Bell", 1), 'type': 'bell'}, 'Tree Zone 3 - The Exit': {'id': 0x04, 'ram_index': 4, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, 'Tree Zone 4 - Honeybees': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, @@ -28,11 +50,13 @@ 'Tree Zone - Secret Course': {'id': 0x1D, 'ram_index': 36, 'type': 'level'}, 'Hippo Zone': {'id': 0x11, 'ram_index': 31, 'type': 'level'}, 'Space Zone 1 - Moon Stage': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Progression", 1), 'type': 'level'}, + 'Space Zone 1 - Moon Stage Secret Exit': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Secret", 1), 'type': 'secret'}, 'Space Zone 1 - Moon Stage Midway Bell': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone 1 - Moon Stage Midway Bell", 1), 'type': 'bell'}, 'Space Zone - Secret Course': {'id': 0x1C, 'ram_index': 41, 'type': 'level'}, 'Space Zone 2 - Star Stage': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Coin", 1), 'type': 'level'}, 'Space Zone 2 - Star Stage Midway Bell': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Zone 2 - Star Stage Midway Bell", 1), 'type': 'bell'}, 'Macro Zone 1 - The Ant Monsters': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Progression", 1), 'type': 'level'}, + 'Macro Zone 1 - The Ant Monsters Secret Exit': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Secret 1", 1), 'type': 'secret'}, 'Macro Zone 1 - The Ant Monsters Midway Bell': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone 1 - The Ant Monsters Midway Bell", 1), 'type': 'bell'}, 'Macro Zone 2 - In the Syrup Sea': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone Progression", 2), 'type': 'level'}, 'Macro Zone 2 - In the Syrup Sea Midway Bell': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone 2 - In the Syrup Sea Midway Bell", 1), 'type': 'bell'}, @@ -40,12 +64,14 @@ 'Macro Zone 3 - Fiery Mario-Special Agent Midway Bell': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", 1), 'type': 'bell'}, 'Macro Zone 4 - One Mighty Mouse': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Coin", 1), 'type': 'level'}, 'Macro Zone 4 - One Mighty Mouse Midway Bell': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Zone 4 - One Mighty Mouse Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone - Secret Course': {'id': 0x1E, 'ram_index': 35, 'clear_condition': ("Macro Zone Secret", 1), 'type': 'level'}, + 'Macro Zone - Secret Course': {'id': 0x1E, 'ram_index': 35, 'clear_condition': ("Macro Zone Secret 2", 1), 'type': 'level'}, 'Pumpkin Zone 1 - Bat Course': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone Progression", 1), 'type': 'level'}, 'Pumpkin Zone 1 - Bat Course Midway Bell': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone 1 - Bat Course Midway Bell", 1), 'type': 'bell'}, 'Pumpkin Zone 2 - Cyclops Course': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'level'}, + 'Pumpkin Zone 2 - Cyclops Course Secret Exit': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Secret 1", 1), 'type': 'secret'}, 'Pumpkin Zone 2 - Cyclops Course Midway Bell': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'bell'}, 'Pumpkin Zone 3 - Ghost House': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'level'}, + 'Pumpkin Zone 3 - Ghost House Secret Exit': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Secret 2", 1), 'type': 'secret'}, 'Pumpkin Zone 3 - Ghost House Midway Bell': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'bell'}, "Pumpkin Zone 4 - Witch's Mansion": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'level'}, "Pumpkin Zone 4 - Witch's Mansion Midway Bell": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'bell'}, @@ -62,6 +88,7 @@ 'Turtle Zone 1 - Cheep Cheep Course': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone Progression", 1), 'type': 'level'}, 'Turtle Zone 1 - Cheep Cheep Course Midway Bell': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone 1 - Cheep Cheep Course Midway Bell", 1), 'type': 'bell'}, 'Turtle Zone 2 - Turtle Zone': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Progression", 2), 'type': 'level'}, + 'Turtle Zone 2 - Turtle Zone Secret Exit': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Secret", 1), 'type': 'secret'}, 'Turtle Zone 2 - Turtle Zone Midway Bell': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone 2 - Turtle Zone Midway Bell", 1), 'type': 'bell'}, 'Turtle Zone 3 - Whale Course': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Coin", 1), 'type': 'level'}, 'Turtle Zone 3 - Whale Course Midway Bell': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Zone 3 - Whale Course Midway Bell", 1), 'type': 'bell'}, @@ -70,12 +97,23 @@ items = { "Space Zone Progression": ItemClassification.progression, + "Space Zone Secret": ItemClassification.progression, "Tree Zone Progression": ItemClassification.progression, + "Tree Zone Progression x2": ItemClassification.progression, + "Tree Zone Secret": ItemClassification.progression, "Macro Zone Progression": ItemClassification.progression, - "Macro Zone Secret": ItemClassification.progression_skip_balancing, + "Macro Zone Progression x2": ItemClassification.progression, + "Macro Zone Secret 1": ItemClassification.progression, + "Macro Zone Secret 2": ItemClassification.progression_skip_balancing, "Pumpkin Zone Progression": ItemClassification.progression, + "Pumpkin Zone Progression x2": ItemClassification.progression, + "Pumpkin Zone Secret 1": ItemClassification.progression, + "Pumpkin Zone Secret 2": ItemClassification.progression, "Mario Zone Progression": ItemClassification.progression, + "Mario Zone Progression x2": ItemClassification.progression, "Turtle Zone Progression": ItemClassification.progression, + "Turtle Zone Progression x2": ItemClassification.progression, + "Turtle Zone Secret": ItemClassification.progression, "Tree Coin": ItemClassification.progression_skip_balancing, "Space Coin": ItemClassification.progression_skip_balancing, "Macro Coin": ItemClassification.progression_skip_balancing, @@ -89,6 +127,10 @@ "Hippo Bubble": ItemClassification.progression_skip_balancing, "Swim": ItemClassification.progression, "Pipe Traversal": ItemClassification.progression, + "Pipe Traversal - Down": ItemClassification.progression, + "Pipe Traversal - Up": ItemClassification.progression, + "Pipe Traversal - Right": ItemClassification.progression, + "Pipe Traversal - Left": ItemClassification.progression_skip_balancing, "Super Star Duration Increase": ItemClassification.filler, "Easy Mode": ItemClassification.useful, "Normal Mode": ItemClassification.trap, @@ -165,7 +207,10 @@ class MarioLand2World(World): "Bosses": { "Tree Zone 5 - The Big Bird", "Space Zone 2 - Star Stage", "Macro Zone 4 - One Mighty Mouse", "Pumpkin Zone 4 - Witch's Mansion", "Mario Zone 4 - Three Mean Pigs!", "Turtle Zone 3 - Whale Course" - } + }, + "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"}, } option_definitions = sml2options @@ -218,99 +263,134 @@ def create_regions(self): self.multiworld.get_region("Macro Zone 4", self.player).connect(self.multiworld.get_region("Macro Zone - Secret Course", self.player)) + castle = Region("Mario's Castle", self.player, self.multiworld) + menu_region.connect(castle) + wario = Location(self.player, "Mario's Castle - Wario", parent=castle) + castle.locations.append(wario) + wario.place_locked_item(MarioLand2Item("Wario Defeated", ItemClassification.progression, None, self.player)) def set_rules(self): entrance_rules = { "Menu -> Space Zone 1": lambda state: state.has_any(["Hippo Bubble", "Carrot"], self.player), - "Space Zone 1 -> Space Zone - Secret Course": lambda state: state.has_any( - ["Space Physics", "Carrot"], self.player), - "Space Zone 1 -> Space Zone 2": lambda state: state.has("Space Zone Progression", self.player), - "Tree Zone 1 -> Tree Zone 2": lambda state: state.has("Tree Zone Progression", self.player), - "Tree Zone 2 -> Tree Zone - Secret Course": lambda state: state.has_all(["Carrot", "Pipe Traversal"], - self.player), - "Tree Zone 2 -> Tree Zone 3": lambda state: state.has("Tree Zone Progression", self.player, 2), - "Tree Zone 4 -> Tree Zone 5": lambda state: state.has("Tree Zone Progression", self.player, 3), - "Macro Zone 1 -> Macro Zone - Secret Course": lambda state: state.has_all(["Fire Flower", "Pipe Traversal"], - self.player), - "Macro Zone - Secret Course -> Macro Zone 4": lambda state: state.has("Macro Zone Secret", self.player), - "Macro Zone 1 -> Macro Zone 2": lambda state: state.has("Macro Zone Progression", self.player), - "Macro Zone 2 -> Macro Zone 3": lambda state: state.has("Macro Zone Progression", self.player, 2), - "Macro Zone 3 -> Macro Zone 4": lambda state: state.has("Macro Zone Progression", self.player, 3), - "Macro Zone 4 -> Macro Zone - Secret Course": lambda state: state.has("Macro Zone Secret", self.player), - "Pumpkin Zone 1 -> Pumpkin Zone 2": lambda state: state.has("Pumpkin Zone Progression", self.player), - # You can only spin jump as Big Mario or Fire Mario - "Pumpkin Zone 2 -> Pumpkin Zone - Secret Course 1": lambda state: state.has_all( - ["Swim", "Pipe Traversal"], self.player) and state.has_any(["Mushroom", "Fire Flower"], self.player), - "Pumpkin Zone 2 -> Pumpkin Zone 3": lambda state: state.has("Pumpkin Zone Progression", self.player, 2), - "Pumpkin Zone 3 -> Pumpkin Zone - Secret Course 2": lambda state: state.has("Carrot", self.player), - "Pumpkin Zone 3 -> Pumpkin Zone 4": lambda state: state.has("Pumpkin Zone Progression", self.player, 3), - "Mario Zone 1 -> Mario Zone 2": lambda state: state.has("Mario Zone Progression", self.player), - "Mario Zone 2 -> Mario Zone 3": lambda state: state.has("Mario Zone Progression", self.player, 2), - "Mario Zone 3 -> Mario Zone 4": lambda state: state.has("Mario Zone Progression", self.player, 3), - "Turtle Zone 1 -> Turtle Zone 2": lambda state: state.has("Turtle Zone Progression", self.player), - "Turtle Zone 2 -> Turtle Zone - Secret Course": lambda state: state.has_all(["Swim", "Pipe Traversal"], - self.player), - "Turtle Zone 2 -> Turtle Zone 3": lambda state: state.has("Turtle Zone Progression", self.player, 2), + "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), + "Menu -> Mario's Castle": lambda state: ([ + state.has("Tree Coin", self.player), state.has("Space Coin", self.player), + state.has("Macro Coin", self.player), state.has("Pumpkin Coin", self.player), + state.has("Mario Coin", self.player), state.has("Turtle Coin", self.player) + ].count(True) >= self.multiworld.required_golden_coins[self.player]) } rules = { "Hippo Zone": lambda state: state.has_any(["Hippo Bubble", "Carrot", "Swim"], self.player), - # It is possible, however quite difficulty, normally to beat the Moon Stage without Carrot or Space Physics. + # It is possible, however tricky, to beat the Moon Stage without Carrot or Space Physics. # However, it requires somewhat precisely jumping off enemies. Enemy shuffle may make this impossible. # I have not done any testing there. Instead, I will just always make one or the other required, since # it is difficult without them anyway. "Space Zone 1 - Moon Stage": lambda state: state.has_any(["Space Physics", "Carrot"], self.player), + # One or the other is actually necessary for the secret exit. + "Space Zone 1 - Moon Stage Secret Exit": lambda state: state.has_any( + ["Space Physics", "Carrot"], self.player), # Without Space Physics, you must be able to take damage once to reach the bell, and again after the bell. # If bells are not shuffled, then any one powerup will do, as you can get the bell and come back. # Otherwise, you need the bell item from the item pool, or you need to be able to take damage twice in one # visit. - "Space Zone 2 - Star Stage": lambda state: state.has("Space Physics", self.player) - or ((not state.multiworld.shuffle_midway_bells[self.player]) - and state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player)) - or (state.has("Mushroom", self.player) - and state.has_any(["Fire Flower", "Carrot"], self.player)) - or (state.has("Space Zone 2 - Star Stage Midway Bell", self.player) and state.has_any( - ["Mushroom", "Fire Flower", "Carrot"], self.player)), + "Space Zone 2 - Star Stage": lambda state: has_pipe_right(state, self.player) and (state.has( + "Space Physics", self.player) or ((not state.multiworld.shuffle_midway_bells[self.player]) + and state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player)) or (state.has( + "Mushroom", self.player) and state.has_any(["Fire Flower", "Carrot"], self.player)) + or (state.has("Space Zone 2 - Star Stage Midway Bell", self.player) and state.has_any( + ["Mushroom", "Fire Flower", "Carrot"], self.player))), "Space Zone 2 - Star Stage Midway Bell": lambda state: state.has_any( ["Space Physics", "Space Zone 2 - Star Stage Midway Bell", "Mushroom", "Fire Flower", "Carrot"], self.player), - "Tree Zone 2 - In the Trees": lambda state: state.has_any(["Pipe Traversal", - "Tree Zone 2 - In the Trees Midway Bell"], self.player), - "Tree Zone 2 - In the Trees Midway Bell": lambda state: state.has_any(["Pipe Traversal", - "Tree Zone 2 - In the Trees Midway Bell"], self.player), - "Tree Zone 4 - Honeybees": lambda state: state.has("Pipe Traversal", self.player), - "Tree Zone 4 - Honeybees Midway Bell": lambda state: state.has_any(["Pipe Traversal", - "Tree Zone 4 - Honeybees Midway Bell"], self.player), - "Tree Zone 5 - The Big Bird": lambda state: state.has("Pipe Traversal", self.player), - "Macro Zone 1 - The Ant Monsters": lambda state: state.has_any( - ["Pipe Traversal", "Macro Zone 1 - The Ant Monsters Midway Bell"], self.player), - "Macro Zone 1 - The Ant Monsters Midway Bell": lambda state: state.has_any( - ["Pipe Traversal", "Macro Zone 1 - The Ant Monsters Midway Bell"], self.player), - "Macro Zone 2 - In the Syrup Sea": lambda state: state.has_all(["Swim", "Pipe Traversal"], self.player), - "Macro Zone 2 - In the Syrup Sea Midway Bell": lambda state: state.has_all( - ["Swim", "Pipe Traversal"], self.player) or state.has("Macro Zone 2 - In the Syrup Sea Midway Bell", - self.player), - "Macro Zone 3 - Fiery Mario-Special Agent": lambda state: state.has_any( - ["Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", "Pipe Traversal"], self.player), - "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": lambda state: state.has_any( - ["Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", "Pipe Traversal"], self.player), - "Macro Zone 4 - One Mighty Mouse": lambda state: state.has("Pipe Traversal", self.player), - "Pumpkin Zone 1 - Bat Course": lambda state: state.has_any( - ["Pipe Traversal", "Pumpkin Zone 1 - Bat Course Midway Bell"], self.player), - "Pumpkin Zone 1 - Bat Course Midway Bell": lambda state: state.has_any( - ["Pipe Traversal", "Pumpkin Zone 1 - Bat Course Midway Bell"], self.player), - "Pumpkin Zone 2 - Cyclops Course": lambda state: state.has_all(["Swim", "Pipe Traversal"], self.player), - "Pumpkin Zone 4 - Witch's Mansion": lambda state: state.has("Pipe Traversal", self.player), - "Mario Zone 1 - Fiery Blocks": lambda state: state.has("Pipe Traversal", self.player), + "Tree Zone 2 - In the Trees": lambda state: has_pipe_right(state, self.player) or state.has( + "Tree Zone 2 - In the Trees Midway Bell", self.player), + "Tree Zone 2 - In the Trees Midway Bell": lambda state: has_pipe_right(state, self.player) or state.has( + "Tree Zone 2 - In the Trees Midway Bell", self.player), + "Tree Zone 2 - In the Trees Secret Exit": lambda state: has_pipe_right(state, self.player) + and state.has("Carrot", self.player), + "Tree Zone 4 - Honeybees": lambda state: has_pipe_down(state, self.player) + and ((has_pipe_right(state, self.player) and has_pipe_up(state, self.player)) + or state.has("Tree Zone 4 - Honeybees Midway Bell", self.player)), + "Tree Zone 4 - Honeybees Midway Bell": lambda state: ((has_pipe_right(state, self.player) + and has_pipe_up(state, self.player)) or state.has("Tree Zone 4 - Honeybees Midway Bell", self.player)), + "Tree Zone 5 - The Big Bird": lambda state: has_pipe_right(state, self.player) + and (has_pipe_up(state, self.player) + or state.has("Carrot", self.player)), + "Macro Zone 1 - The Ant Monsters": lambda state: has_pipe_down(state, self.player) + or state.has("Macro Zone 1 - The Ant Monsters Midway Bell", + self.player), + "Macro Zone 1 - The Ant Monsters Midway Bell": lambda state: has_pipe_down(state, self.player) + or state.has("Macro Zone 1 - The Ant Monsters Midway Bell", + self.player), + "Macro Zone 1 - The Ant Monsters Secret Exit": lambda state: (has_pipe_down(state, self.player) + or state.has("Macro Zone 1 - The Ant Monsters Midway Bell", self.player)) + and state.has("Fire Flower", self.player) and has_pipe_up(state, self.player), + "Macro Zone - Secret Course": lambda state: state.has_any(["Mushroom", "Fire Flower"], self.player), + "Macro Zone 2 - In the Syrup Sea": lambda state: (has_pipe_down(state, self.player) or state.has( + "Macro Zone 2 - In the Syrup Sea Midway Bell", self.player)) + and state.has("Swim", self.player) and has_pipe_up(state, self.player), + "Macro Zone 2 - In the Syrup Sea Midway Bell": lambda state: (has_pipe_down( + state, self.player) and state.has("Swim", self.player)) or state.has( + "Macro Zone 2 - In the Syrup Sea Midway Bell", self.player), + "Macro Zone 3 - Fiery Mario-Special Agent": lambda state: (has_pipe_down(state, self.player) + and has_pipe_down(state, self.player)) or state.has( + "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", self.player), + "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": lambda state: (has_pipe_down(state, self.player) + and has_pipe_down(state, self.player)) or state.has( + "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", self.player), + "Macro Zone 4 - One Mighty Mouse": lambda state: has_pipe_right(state, self.player), + "Pumpkin Zone 1 - Bat Course": lambda state: has_pipe_down(state, self.player) or state.has( + "Pumpkin Zone 1 - Bat Course Midway Bell", self.player), + "Pumpkin Zone 1 - Bat Course Midway Bell": lambda state: has_pipe_down(state, self.player) or state.has( + "Pumpkin Zone 1 - Bat Course Midway Bell", self.player), + "Pumpkin Zone 2 - Cyclops Course": lambda state: has_pipe_down(state, self.player) and has_pipe_up( + state, self.player) and has_pipe_right(state, self.player) and state.has("Swim", self.player), + # You can only spin jump as Big Mario or Fire Mario + "Pumpkin Zone 2 - Cyclops Course Secret Exit": lambda state: has_pipe_down( + state, self.player) and has_pipe_up(state, self.player) and has_pipe_right( + state, self.player) and state.has("Swim", self.player) and state.has_any( + ["Mushroom", "Fire Flower"], self.player), + "Pumpkin Zone 3 - Ghost House Secret Exit": lambda state: state.has("Carrot", self.player), + "Pumpkin Zone 4 - Witch's Mansion": lambda state: has_pipe_right(state, self.player), + "Mario Zone 1 - Fiery Blocks": lambda state: has_pipe_right(state, self.player), # It is possible to get as small mario, but it is a very precise jump and you will die afterward. "Mario Zone 1 - Fiery Blocks Midway Bell": lambda state: (state.has_any( - ["Mushroom", "Fire Flower", "Carrot"], self.player) and state.has("Pipe Traversal", self.player)) + ["Mushroom", "Fire Flower", "Carrot"], self.player) and has_pipe_right(state, self.player)) or state.has("Mario Zone 1 - Fiery Blocks Midway Bell", self.player), - "Mario Zone 4 - Three Mean Pigs!": lambda state: state.has("Pipe Traversal", self.player), - "Turtle Zone 2 - Turtle Zone": lambda state: state.has_all(["Swim", "Pipe Traversal"], self.player), + "Mario Zone 4 - Three Mean Pigs!": lambda state: has_pipe_right(state, self.player), + "Turtle Zone 2 - Turtle Zone": lambda state: has_pipe_up(state, self.player) and has_pipe_down( + state, self.player) and has_pipe_right(state, self.player) and has_pipe_left(state, self.player) + and state.has("Swim", self.player), "Turtle Zone 2 - Turtle Zone Midway Bell": lambda state: state.has_any( ["Swim", "Turtle Zone 2 - Turtle Zone Midway Bell"], self.player), + "Turtle Zone 2 - Turtle Zone Secret Exit": lambda state: has_pipe_up( + state, self.player) and state.has("Swim", self.player), #state.has_any(["Swim", "Turtle Zone 2 - Turtle Zone Midway Bell"], self.player), # hard logic option? "Turtle Zone - Secret Course": lambda state: state.has_any(["Fire Flower", "Carrot"], self.player), - "Turtle Zone 3 - Whale Course": lambda state: state.has("Pipe Traversal", self.player), + "Turtle Zone 3 - Whale Course": lambda state: has_pipe_right(state, self.player), + "Mario's Castle - Wario": lambda state: has_pipe_right( + state, self.player) and has_pipe_left(state, self.player) } for entrance, rule in entrance_rules.items(): @@ -320,43 +400,40 @@ def set_rules(self): if ("Midway Bell" not in level) or self.multiworld.shuffle_midway_bells[self.player]: self.multiworld.get_location(level, self.player).access_rule = rule - if self.multiworld.golden_coins[self.player] == "progressive": - self.multiworld.completion_condition[self.player] = lambda state: ([ - state.has("Space Zone Progression", self.player, 3), - state.has("Tree Zone Progression", self.player, 4), - state.has("Macro Zone Progression", self.player, 4), - state.has("Pumpkin Zone Progression", self.player, 4), - state.has("Mario Zone Progression", self.player, 4), - state.has("Turtle Zone Progression", self.player, 3) - ].count(True) >= self.multiworld.required_golden_coins[self.player] - and state.has("Pipe Traversal", self.player)) - else: - self.multiworld.completion_condition[self.player] = lambda state: ([ - state.has("Tree Coin", self.player), state.has("Space Coin", self.player), - state.has("Macro Coin", self.player), state.has("Pumpkin Coin", self.player), - state.has("Mario Coin", self.player), state.has("Turtle Coin", self.player) - ].count(True) >= self.multiworld.required_golden_coins[self.player] - and state.has("Pipe Traversal", 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, - "Tree Zone Progression": 3, - "Macro Zone Progression": 3, - "Macro Zone Secret": 1, - "Pumpkin Zone Progression": 3, - "Mario Zone Progression": 3, + "Space Zone Secret": 1, + "Tree Zone Progression": 1, + "Tree Zone Progression x2": 1, + "Tree Zone Secret": 1, + "Macro Zone Progression": 1, + "Macro Zone Progression x2": 1, + "Macro Zone Secret 1": 1, + "Macro Zone Secret 2": 1, + "Pumpkin Zone Progression": 1, + "Pumpkin Zone Progression x2": 1, + "Pumpkin Zone Secret 1": 1, + "Pumpkin Zone Secret 2": 1, + "Mario Zone Progression": 1, + "Mario Zone Progression x2": 1, "Turtle Zone Progression": 2, + "Turtle Zone Secret": 1, "Mushroom": 1, "Fire Flower": 1, "Carrot": 1, "Space Physics": 1, "Hippo Bubble": 1, "Swim": 1, - "Super Star Duration Increase": 2, + "Super Star Duration Increase": 6, } - if self.multiworld.golden_coins[self.player] == "vanilla": + if self.multiworld.coinsanity[self.player]: + for item in self.item_name_groups["Coins"]: + item_counts[item] = 1 + else: for item, location_name in ( ("Mario Coin", "Mario Zone 4 - Three Mean Pigs!"), ("Tree Coin", "Tree Zone 5 - The Big Bird"), @@ -369,12 +446,6 @@ def create_items(self): location.place_locked_item(self.create_item(item)) location.address = None location.item.code = None - elif self.multiworld.golden_coins[self.player] == "shuffled": - for item in self.item_name_groups["Coins"]: - item_counts[item] = 1 - elif self.multiworld.golden_coins[self.player] == "progressive": - for item in [item for item in items if "Progressive" in item and "Zone" in item]: - item_counts[item] += 1 if self.multiworld.shuffle_midway_bells[self.player]: for item in [item for item in items if "Midway Bell" in item]: @@ -387,9 +458,15 @@ def create_items(self): else: item_counts["Super Star Duration Increase"] += 1 - if self.multiworld.shuffle_pipe_traversal[self.player]: + if self.multiworld.shuffle_pipe_traversal[self.player] == "single": item_counts["Super Star Duration Increase"] -= 1 item_counts["Pipe Traversal"] = 1 + elif self.multiworld.shuffle_pipe_traversal[self.player] == "split": + item_counts["Super Star Duration Increase"] -= 4 + 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")) diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index 0ad024705350a541cfe80ca4520cef41465a0f93..2dc4420c00fa8201e6abc8a3fda28fabea188441 100644 GIT binary patch literal 720 zcmZ+_-WC^-~faSOpFW) z42BF0EK^c4CoaA${rJowr=6!b7Vpr$E%i_DNms!_g+nfEatI@w+V3o6U{I|8|Nmcn z1!M7dp%?|$m&f=RoEkVpg&G7K7o>`KIJ}f`$lJguW+17wd__hEvjc-v=7O|^TTL!s zG08Y_;pBq9!6#-{t#Gv!`3iL@kdk72#S%1;ap3|Jsf-H@49pHDYz=G;j9>nJGt^bE zxW+WGl4XIa%Q1l$$*LZWl7fjRR`@A!y0W<4iFOuxk}^}PX5(z8{|*z`MHq||g5SG2 z>LjK#XdO^CVsY@P2xTmP7?AGJH1qNrMn{IJOic{A1|9JZavQXkOkQl4?H^RNz&dzS zl&j|LTn49ko`LMz-x3;MZ7H2U@qf;OQ=eAnY!1~{uacj_v*rBx-F$6Q|09wb`{wrW z96WL&ynTyc?SrSRjR7t%q}&h1%{VzlHFmGx^gVwM2aDZMWbr=p%J93D{8I~-G+U=Ic| literal 465 zcmZ6YroQ`qH+rV(9QNzn^maPMW6tj<8TLUjc=BuiC zSNy9&EKW{#7VKQi(8F?w;efA6=EMaOo?1RVoDDubJq#=v37pFq7*>8vNPj93RN?5q z>TKezU5)4V7aMWCG25(Y)G;ZgE$57a|4oj^zK7UzCbtPJ*}#-0z|gs7l7njDB!@*C z8kMF`a!_G#PZ3_hz#z44hvLO&C*r2duzZSs^=)rnZ}aY?eO=!Y)4pf^sW-T$dB!ZU zEH*%WLQlvR4fnnM-IgZDV&bmrsKkHku2>%^wp#b&5f6!bD?C$Hyn3b3(a|yOSKQy@ z%QEY`cPh=%@X+A=FqZ)lIM=xUb22b6*I&P(z|f$;;K0bh0wft47?~JY7=R211_1^} jV0in?n91fQ!f?T6R-@x=wLiCtI2d+8Lc484{0|QR2cM|i diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 64665c922a4f..4d4625f40b8c 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -59,26 +59,33 @@ async def game_watcher(self, ctx: BizHawkClientContext): items_received = [list(items.keys())[item.item - START_IDS] for item in ctx.items_received] - progressive_coins = { - "Space Zone Progression": 3, - "Tree Zone Progression": 4, - "Macro Zone Progression": 4, - "Pumpkin Zone Progression": 4, - "Mario Zone Progression": 4, - "Turtle Zone Progression": 3 + level_progression = { + "Space Zone Progression", + "Tree Zone Progression", + "Macro Zone Progression", + "Pumpkin Zone Progression", + "Mario Zone Progression", + "Turtle Zone Progression", } - for level_item, count in progressive_coins.items(): - if items_received.count(level_item) >= count: - items_received.append(level_item.split(" ")[1] + " Coin") + for level_item in level_progression: + for _ in range(items_received.count(level_item + " x2")): + items_received += ([level_item] * 2) + + if "Pipe Traversal" in items_received: + items_received += ["Pipe Traversal - Left", "Pipe Traversal - Right", + "Pipe Traversal - Up", "Pipe Traversal - Down"] locations_checked = [] modified_level_data = level_data.copy() for ID, (location, data) in enumerate(locations.items(), START_IDS): if "clear_condition" in data: if items_received.count(data["clear_condition"][0]) >= data["clear_condition"][1]: - modified_level_data[data["ram_index"]] |= 0x08 if data["type"] == "bell" else 0x80 + modified_level_data[data["ram_index"]] |= (0x08 if data["type"] == "bell" + else 0x01 if data["type"] == "secret" else 0x80) - if data["type"] == "level" and level_data[data["ram_index"]] & 0x41: + if data["type"] == "level" and level_data[data["ram_index"]] & 0x40: + locations_checked.append(ID) + if data["type"] == "secret" and level_data[data["ram_index"]] & 0x02: locations_checked.append(ID) elif data["type"] == "bell" and data["id"] == current_level and midway_point == 0xFF: locations_checked.append(ID) @@ -132,14 +139,14 @@ async def game_watcher(self, ctx: BizHawkClientContext): (rom_addresses["Invincibility_Star_B"], [invincibility_length & 0xFF], "ROM"), (rom_addresses["Enable_Bubble"], [0xcb, 0xd7] if "Hippo Bubble" in items_received else [0, 0], "ROM"), (rom_addresses["Enable_Swim"], [0xcb, 0xcf] if "Swim" in items_received else [0, 0], "ROM"), - (rom_addresses["Pipe_Traversal_A"], [16] if "Pipe Traversal" in items_received else [0], "ROM"), - (rom_addresses["Pipe_Traversal_B"], [32] if "Pipe Traversal" in items_received else [0], "ROM"), - (rom_addresses["Pipe_Traversal_C"], [48] if "Pipe Traversal" in items_received else [0], "ROM"), - (rom_addresses["Pipe_Traversal_D"], [64] if "Pipe Traversal" in items_received else [0], "ROM"), - (rom_addresses["Pipe_Traversal_SFX_A"], [5] if "Pipe Traversal" in items_received else [0], "ROM"), - (rom_addresses["Pipe_Traversal_SFX_B"], [5] if "Pipe Traversal" in items_received else [0], "ROM"), - (rom_addresses["Pipe_Traversal_SFX_C"], [5] if "Pipe Traversal" in items_received else [0], "ROM"), - (rom_addresses["Pipe_Traversal_SFX_D"], [5] if "Pipe Traversal" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_A"], [16] if "Pipe Traversal - Down" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_B"], [32] if "Pipe Traversal - Up" in items_received else [10], "ROM"), + (rom_addresses["Pipe_Traversal_C"], [48] if "Pipe Traversal - Right" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_D"], [64] if "Pipe Traversal - Left" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_SFX_A"], [5] if "Pipe Traversal - Down" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_SFX_B"], [5] if "Pipe Traversal - Up" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_SFX_C"], [5] if "Pipe Traversal - Right" in items_received else [0], "ROM"), + (rom_addresses["Pipe_Traversal_SFX_D"], [5] if "Pipe Traversal - Left" in items_received else [0], "ROM"), (0x022c, [new_lives], "CartRAM"), (0x02E4, [difficulty_mode], "CartRAM"), (0x0848, modified_level_data, "CartRAM") diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index a5af06131941..49f0e4e61080 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -1,17 +1,10 @@ from Options import Toggle, Choice, NamedRange, Range -class GoldenCoins(Choice): - """Vanilla: The coins are found in their original locations. - Shuffled: The coins are shuffled into the item pool. - Progressive: The coins are at the end of the Level Progression chains. For example, there will be a third - Space Zone Progression item, and the final one received will grant the Space Coin. - +class GoldenCoins(Toggle): + """Shuffle the Golden Coins into the item pool. You will see a Golden Coin being received when defeating bosses regardless of whether you are actually getting a coin.""" - display_name = "Golden Coins" - option_vanilla = 0 - option_shuffled = 1 - option_progressive = 2 + display_name = "Coinsanity" default = 0 @@ -41,10 +34,15 @@ class ShuffleMidwayBells(Toggle): display_name = "Shuffle Midway Bells" -class ShufflePipeTraversal(Toggle): - """Shuffle a Pipe Traversal item into the item pool, which is required to enter pipes. +class ShufflePipeTraversal(Choice): + """Single: Shuffle a Pipe Traversal item into the item pool, which is required to enter any pipes. + Split: Shuffle 4 Pipe Traversal items, one required for entering pipes from each direction. Note that being unable to enter pipes is very limiting and affects nearly half of all levels.""" display_name = "Shuffle Pipe Traversal" + option_off = 0 + option_single = 1 + option_split = 2 + default = 0 class RandomizeEnemies(Toggle): @@ -63,7 +61,7 @@ class AutoScrollLevels(NamedRange): display_name = "Auto Scroll Levels" range_start = 0 range_end = 18 - special_range_names = {"vanilla": -1} + special_range_names = {"vanilla": -1, "none": 0, "all": 18} default = -1 @@ -85,7 +83,7 @@ class EnergyLink(Toggle): sml2options = { - "golden_coins": GoldenCoins, + "coinsanity": GoldenCoins, "required_golden_coins": GoldenCoinsRequired, "difficulty_mode": DifficultyMode, "shuffle_midway_bells": ShuffleMidwayBells, diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index ebda6f5d6033..9b45e9641577 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -184,7 +184,7 @@ def generate_output(self, output_directory: str): if self.multiworld.auto_scroll_trap[self.player]: data[rom_addresses["Auto_Scroll_Disable"]] = 0xAF - if self.multiworld.golden_coins[self.player] != "vanilla": + if self.multiworld.coinsanity[self.player]: data[rom_addresses["Coin_Shuffle"]] = 0x40 if self.multiworld.shuffle_midway_bells[self.player]: data[rom_addresses["Disable_Midway_Bell"]] = 0xC9 @@ -213,7 +213,6 @@ def generate_output(self, output_directory: str): os.unlink(rompath) - class SuperMarioLand2DeltaPatch(APDeltaPatch): hash = "a8413347d5df8c9d14f97f0330d67bce" patch_file_ending = ".apsml2" @@ -225,7 +224,6 @@ def get_source_data(cls) -> bytes: return get_base_rom_bytes() - def get_base_rom_bytes(): file_name = get_base_rom_path() with open(file_name, "rb") as file: From 0df91e69cce7d9b141638bc6c4f40c8ada79fca7 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 8 Jan 2024 12:22:06 -0500 Subject: [PATCH 022/113] SML2R Sprite Randomizer fix --- worlds/marioland2/rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 9b45e9641577..5c0bba7ceff3 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -120,7 +120,7 @@ def randomize_enemies(data, random): while i < 0xEBB5: if i < 0xE30C or 0xE384 < i < 0xE3D4 or 0xE431 < i < 0xE8F7 or i > 0xE954: sprite = sprite_extract(data[i], data[i + 1]) - if sprite == 0xFF: + if data[i] == 0xFF: i -= 2 elif sprite in (0x0C, 0x0D): copy_sprite(data, sprite_insert(data[i], data[i+1], random.choice([0x0C, 0x0D])), i) From 0e6475a2c916dced324cbd5b5d20c108dc4ffd4c Mon Sep 17 00:00:00 2001 From: Alchav Date: Tue, 9 Jan 2024 23:30:43 -0500 Subject: [PATCH 023/113] Fix Pumpkin Zone secret levels --- worlds/marioland2/basepatch.bsdiff4 | Bin 720 -> 724 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index 2dc4420c00fa8201e6abc8a3fda28fabea188441..205e7e384da1390720e32a90a0ebcf5ce2f79b64 100644 GIT binary patch delta 385 zcmV-{0e=3_1=Ix*LQ_OZMn*I+LjV8(00000YLO9Kf8q(kpa1|OfB*mfU3h_U_Yzzn z1?k2J078HW7ZQLGfuLm+NI>ZvKyI)B7BCqSr>M|+jEw*qG6qcp)M7nNsiut?Gyv1o z@+R7ZRQ*p-fB+f*003wJ00000dVv(AKn8%&8UsK80002VpbZ%^A5HIDLQD)bfQW&S z2p$4me~>^!ndp(i4Mlm5S|$(#MMXH`i@0K3jF&R8C5*@y`UHqO-~pKem9?Z2))q(u z-T@nE5dkwHR0ZXQL7M^!YE85NL;_R*hylLBA%_4{fk`Bjt4o1}^z=Xk^rJ06t44ze z5GRs$B{#10va^<1vhVE{pB|ts{*`&|yF> fpd=DWJed}1wn*J>A$KHGg$WKIoGJjb$pHre<5!a1 delta 381 zcmV-@0fPS21<(Z%LQ_OZMn*I+LjV8(00000W|0wGe?o`cp#T6OfB*mfUw8p?_Yzzn z1?k2P078HW6%v3EfuLm+NI>ZvKyI)B7BCqir>JOX13&;8X`p7IwKUVzG-$}s$)NpH z$eVhoMLiVt0000000000000^R>I71e0imEY8fee}00TfY27m^D0rdX%F)biC)&h}v z1fV5Ef5s5#VO&05f1y??yo^VPt?R zz#}pQKuma50e9h0XF!6P)2snO0F?rQ0BtacUqBwPDx{O6K5I`>dZ0T~vRXwc+id_s zo=H&$EB0W4>a=yAk^gL<%JQphvsEi6dLNVwUbN58yAFmL|5##yj-8AQ!NkZ{hqMuW z;N=B@P(f4ioV}J^ From 09d6461ef7e4862fcd9b84c5cc22106d2c6bad19 Mon Sep 17 00:00:00 2001 From: Alchav Date: Fri, 12 Jan 2024 13:07:30 -0500 Subject: [PATCH 024/113] Update SML2 Docs --- .../marioland2/docs/en_Super Mario Land 2.md | 27 ++++++++++++------- worlds/marioland2/docs/setup_en.md | 2 +- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/worlds/marioland2/docs/en_Super Mario Land 2.md b/worlds/marioland2/docs/en_Super Mario Land 2.md index d7569df94b6f..b0cf74e3de5e 100644 --- a/worlds/marioland2/docs/en_Super Mario Land 2.md +++ b/worlds/marioland2/docs/en_Super Mario Land 2.md @@ -7,13 +7,19 @@ config file. ## What items and locations get shuffled? -Completing a level's normal exit OR secret exit results in a location check for that level. Completing a normal exit -does not automatically unlock the next level, but completing a secret exit does always bring you to the secret course. -This means that if you can complete the secret exit, you do not need to return to complete the normal exit, as the check -for the level will have been registered already. +Completing a level's exits results in a location check instead of automatically bringing you to the next level. +Where there are secret exits, the secret exit will be a separate location check. There is one exception, Hippo Zone, +that does not have a separate check for its secret exit. The Hippo Zone secret exit will still bring you to the Space +Zone. -Unlocking "normal exit" paths requires shuffled zone progression items. So, for example, finding or receiving a "Tree -Zone Progression" will unlock the path from Tree Zone 1 to Tree Zone 2. +Unlocking paths to new levels requires finding or receiving Zone Progression items. For example, receiving the first +"Turtle Zone Progression" will unlock the path from Turtle Zone 1 to Turtle Zone 2. Paths to secret levels are separate +items, so Turtle Zone Secret will open the path from Turtle Zone 2 to the Turtle Zone Secret Course. + +Mario Zone, Pumpkin Zone, Tree Zone, and Macro Zone each have one "Zone Progression x2" item that opens two paths at +once. + +The path from Tree Zone 2 to the branch to Tree Zone 3 and 4 is one unlock, so both levels will open at this point. Besides the zone progression unlocks, the following items are always shuffled: - Mushroom: required to become Big Mario. If you are Fire or Bunny Mario and take damage, and have not obtained the @@ -23,6 +29,8 @@ Mushroom, you will drop straight down to Small Mario. - Swim: Mario will fall through water as though it is air until this is obtained. - Hippo Bubble: required to use the bubbles in Hippo Zone to fly. - Space Physics: the Space Zone levels will have normal gravity until this is obtained. +- Super Star Duration Increase: you begin with a drastically lowered invincibility star duration, and these items will +increase it. This is the main filler item and the number of them appearing in the item pool depends on your settings. Additionally, the following items can be shuffled depending on your YAML settings: - The 6 Golden Coins: note that the game will still show you the coin being sent to the castle when defeating a boss @@ -32,10 +40,9 @@ Note that you may have to backtrack from the midway point to reach some secret e - Normal Mode/Easy Mode: you can start the game in Normal Mode with an Easy Mode "upgrade" in the item pool, or start in Easy Mode with a Normal Mode "trap" item, swapping the difficulty. - Auto Scroll: auto-scrolling levels can be set to not auto scroll until this trap item is received. -- Pipe Traversal: required to enter pipes. -- Super Star Duration Increase: you begin with a drastically lowered invincibility star duration, and these items will -increase it. This is the main filler item generated to fill out the item pool. One or more will appear unless all other -options to add items are on. +- Pipe Traversal: required to enter pipes. Can also be split into 4 items, each enabling pipe entry from a different +direction. + ## When the player receives an item, what happens? diff --git a/worlds/marioland2/docs/setup_en.md b/worlds/marioland2/docs/setup_en.md index 31210d04cd22..f7e5d09a9100 100644 --- a/worlds/marioland2/docs/setup_en.md +++ b/worlds/marioland2/docs/setup_en.md @@ -11,7 +11,7 @@ As we are using BizHawk, this guide is only applicable to Windows and Linux syst - Detailed installation instructions for BizHawk can be found at the above link. - Windows users must run the prereq installer first, which can also be found at the above link. - The built-in Archipelago client, which can be installed [here](https://github.com/ArchipelagoMW/Archipelago/releases) -- A Super Mario Land 2: 6 Golden Coins version 1.0 ROM file. The Archipelago community cannot provide these. +- A Super Mario Land 2: 6 Golden Coins version 1.0 ROM file. The Archipelago community cannot provide this. ## Configuring BizHawk From 7d0e9a99accdf3c25f6ee3ac08df09cf7be0cd3e Mon Sep 17 00:00:00 2001 From: Alchav Date: Fri, 12 Jan 2024 13:15:32 -0500 Subject: [PATCH 025/113] Special Thanks --- worlds/marioland2/docs/en_Super Mario Land 2.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/worlds/marioland2/docs/en_Super Mario Land 2.md b/worlds/marioland2/docs/en_Super Mario Land 2.md index b0cf74e3de5e..a7e99737d5af 100644 --- a/worlds/marioland2/docs/en_Super Mario Land 2.md +++ b/worlds/marioland2/docs/en_Super Mario Land 2.md @@ -47,4 +47,12 @@ direction. ## When the player receives an item, what happens? There is no in-game indication that an item has been received. You will need to watch the client to be sure you're aware -of the items you've received. \ No newline at end of file +of the items you've received. + +## Special Thanks to: + +- [froggestspirit](https://github.com/froggestspirit) for his Super Mario Land 2 disassembly. While very incomplete, it +had enough memory values mapped out to make my work significantly easier. +- [slashinfty](https://github.com/slashinfty), the author of the +[Super Mario Land 2 Randomizer](https://sml2r.download/) for permitting me to port features such as Randomize Enemies +and Randomize Platforms directly from it. \ No newline at end of file From 4ea35a4800b834b036740f433feefe211ac9469d Mon Sep 17 00:00:00 2001 From: Alchav Date: Fri, 12 Jan 2024 13:44:35 -0500 Subject: [PATCH 026/113] Fix Macro Zone 1 Secret Exit --- worlds/marioland2/basepatch.bsdiff4 | Bin 724 -> 724 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index 205e7e384da1390720e32a90a0ebcf5ce2f79b64..6322d79274014fc9c058320b965b2c7672f18902 100644 GIT binary patch delta 366 zcmV-!0g?XH1=IzQZGU@S&zk@MBLDyY{#|&1arY8jAO-2h2mnHY2o(~55rLp(6i7hn z96)Zc0X7&6l9MKlJxvUnGz|kxAk9q-N1|kC$kB)%r;$AMP>P?SPf!2=8UO$Q00000 z0QCYXLVyhcqeDTU00E%T0004?1L{5NW?DdG5Gw(SP-0O70DtyQVGd_GNFdNxAd5u; z0C8%?NVkS2k&@P1`G#~0{Q^WBumH}1PrVrgv4xTV^MFRm1VBu9RRMRQP-nn`ne*%f z5dfA1L;%@f5TO87fmKzZnUgEG2V5aaB6NXIDwLpZ$23%o-Aa90^Hy-lxZExKY6f4> zy*Zm`FDHTzlwu6T&(FLLxaI$-;-P-V4CzCajx9T6h$4qn76n1n^A9=@~ delta 366 zcmV-!0g?XH1=IzQZGYkk!k_>EB7gt?{#|&1arY8jAO-2h2mnHW2p1B75rLp(6i7hn z96)Zc0TwVB5~rxpdW?+#8Zrh=1Jq(YO{u1h88iUX)bb|UgjD@cP=Ej$0001J00000 z0D6HGq(BCM&>90k00000$)F7xG9OLvTS80>HGqhLkq90FU4M{3Lz(E2!VN`vj#?%V z1Vu$S;)}RqT#T18u_cVi7y1N*0Ehv;!XbwMQ-Mh&ldDUCh4l151oWdVK&wWB2oNWdb|p8i^s=*-S+ewxddmxM zCf3^QAhez6K4Q=>y8hm|3=muVlsSxjcQLdDHG#ZV2^d@uv=t3OQY9nhFlh3#No8Nb z)l=G)0{&mYAp}k6hn_nS#r(+{=q`)x|E(j9N6=wFE}$e5Nj#YrYPLw-ZXtIhQ-ui* MAe<@yv&{hq0jYJ66aWAK From 07a5b82113f0bd645d133b68e6dd3f8b0c124d1a Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 15 Jan 2024 23:59:53 -0500 Subject: [PATCH 027/113] SML2 finishing touches --- worlds/marioland2/__init__.py | 164 +++------------------------------ worlds/marioland2/client.py | 5 +- worlds/marioland2/items.py | 65 +++++++++++++ worlds/marioland2/locations.py | 61 ++++++++++++ worlds/marioland2/logic.py | 18 ++++ worlds/marioland2/options.py | 2 +- 6 files changed, 161 insertions(+), 154 deletions(-) create mode 100644 worlds/marioland2/items.py create mode 100644 worlds/marioland2/locations.py create mode 100644 worlds/marioland2/logic.py diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index b26e6df085ec..36f259bfc887 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -9,157 +9,13 @@ from . import client from .rom import generate_output, SuperMarioLand2DeltaPatch from .options import sml2options +from .locations import locations +from .items import items +from .logic import has_pipe_up, has_pipe_down, has_pipe_left, has_pipe_right, has_level_progression START_IDS = 7770000 -def has_pipe_right(state, player): - return state.has_any(["Pipe Traversal - Right", "Pipe Traversal"], player) - - -def has_pipe_left(state, player): - return state.has_any(["Pipe Traversal - Left", "Pipe Traversal"], player) - - -def has_pipe_down(state, player): - return state.has_any(["Pipe Traversal - Down", "Pipe Traversal"], player) - - -def has_pipe_up(state, player): - return state.has_any(["Pipe Traversal - Up", "Pipe Traversal"], player) - - -def has_level_progression(state, item, player, count=1): - return state.count(item, player) + (state.count(item + " x2", player) * 2) >= count - - -locations = { - 'Mushroom Zone': {'id': 0x00, 'ram_index': 0, 'type': 'level'}, - 'Mushroom Zone Midway Bell': {'id': 0x00, 'ram_index': 0, 'clear_condition': ("Mushroom Zone Midway Bell", 1), 'type': 'bell'}, - 'Scenic Course': {'id': 0x19, 'ram_index': 40, 'type': 'level'}, - 'Tree Zone 1 - Invincibility!': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone Progression", 1), 'type': 'level'}, - 'Tree Zone 1 - Invincibility! Midway Bell': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone 1 - Invincibility! Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone 2 - In the Trees': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Progression", 2), 'type': 'level'}, - 'Tree Zone 2 - In the Trees Secret Exit': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Secret", 1), 'type': 'secret'}, - 'Tree Zone 2 - In the Trees Midway Bell': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone 2 - In the Trees Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone 3 - The Exit': {'id': 0x04, 'ram_index': 4, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, - 'Tree Zone 4 - Honeybees': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, - 'Tree Zone 4 - Honeybees Midway Bell': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone 4 - Honeybees Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone 5 - The Big Bird': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Coin", 1), 'type': 'level'}, - 'Tree Zone 5 - The Big Bird Midway Bell': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Zone 5 - The Big Bird Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone - Secret Course': {'id': 0x1D, 'ram_index': 36, 'type': 'level'}, - 'Hippo Zone': {'id': 0x11, 'ram_index': 31, 'type': 'level'}, - 'Space Zone 1 - Moon Stage': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Progression", 1), 'type': 'level'}, - 'Space Zone 1 - Moon Stage Secret Exit': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Secret", 1), 'type': 'secret'}, - 'Space Zone 1 - Moon Stage Midway Bell': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone 1 - Moon Stage Midway Bell", 1), 'type': 'bell'}, - 'Space Zone - Secret Course': {'id': 0x1C, 'ram_index': 41, 'type': 'level'}, - 'Space Zone 2 - Star Stage': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Coin", 1), 'type': 'level'}, - 'Space Zone 2 - Star Stage Midway Bell': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Zone 2 - Star Stage Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 1 - The Ant Monsters': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Progression", 1), 'type': 'level'}, - 'Macro Zone 1 - The Ant Monsters Secret Exit': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Secret 1", 1), 'type': 'secret'}, - 'Macro Zone 1 - The Ant Monsters Midway Bell': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone 1 - The Ant Monsters Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 2 - In the Syrup Sea': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone Progression", 2), 'type': 'level'}, - 'Macro Zone 2 - In the Syrup Sea Midway Bell': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone 2 - In the Syrup Sea Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 3 - Fiery Mario-Special Agent': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone Progression", 3), 'type': 'level'}, - 'Macro Zone 3 - Fiery Mario-Special Agent Midway Bell': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 4 - One Mighty Mouse': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Coin", 1), 'type': 'level'}, - 'Macro Zone 4 - One Mighty Mouse Midway Bell': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Zone 4 - One Mighty Mouse Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone - Secret Course': {'id': 0x1E, 'ram_index': 35, 'clear_condition': ("Macro Zone Secret 2", 1), 'type': 'level'}, - 'Pumpkin Zone 1 - Bat Course': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone Progression", 1), 'type': 'level'}, - 'Pumpkin Zone 1 - Bat Course Midway Bell': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone 1 - Bat Course Midway Bell", 1), 'type': 'bell'}, - 'Pumpkin Zone 2 - Cyclops Course': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'level'}, - 'Pumpkin Zone 2 - Cyclops Course Secret Exit': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Secret 1", 1), 'type': 'secret'}, - 'Pumpkin Zone 2 - Cyclops Course Midway Bell': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'bell'}, - 'Pumpkin Zone 3 - Ghost House': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'level'}, - 'Pumpkin Zone 3 - Ghost House Secret Exit': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Secret 2", 1), 'type': 'secret'}, - 'Pumpkin Zone 3 - Ghost House Midway Bell': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'bell'}, - "Pumpkin Zone 4 - Witch's Mansion": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'level'}, - "Pumpkin Zone 4 - Witch's Mansion Midway Bell": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'bell'}, - 'Pumpkin Zone - Secret Course 1': {'id': 0x1B, 'ram_index': 38, 'type': 'level'}, - 'Pumpkin Zone - Secret Course 2': {'id': 0x1F, 'ram_index': 39, 'type': 'level'}, - 'Mario Zone 1 - Fiery Blocks': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone Progression", 1), 'type': 'level'}, - 'Mario Zone 1 - Fiery Blocks Midway Bell': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone 1 - Fiery Blocks Midway Bell", 1), 'type': 'bell'}, - 'Mario Zone 2 - Mario the Circus Star!': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone Progression", 2), 'type': 'level'}, - 'Mario Zone 2 - Mario the Circus Star! Midway Bell': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone 2 - Mario the Circus Star! Midway Bell", 1), 'type': 'bell'}, - 'Mario Zone 3 - Beware: Jagged Spikes': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone Progression", 3), 'type': 'level'}, - 'Mario Zone 3 - Beware: Jagged Spikes Midway Bell': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone 3 - Beware: Jagged Spikes Midway Bell", 1), 'type': 'bell'}, - 'Mario Zone 4 - Three Mean Pigs!': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Coin", 1), 'type': 'level'}, - 'Mario Zone 4 - Three Mean Pigs! Midway Bell': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Zone 4 - Three Mean Pigs! Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone 1 - Cheep Cheep Course': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone Progression", 1), 'type': 'level'}, - 'Turtle Zone 1 - Cheep Cheep Course Midway Bell': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone 1 - Cheep Cheep Course Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone 2 - Turtle Zone': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Progression", 2), 'type': 'level'}, - 'Turtle Zone 2 - Turtle Zone Secret Exit': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Secret", 1), 'type': 'secret'}, - 'Turtle Zone 2 - Turtle Zone Midway Bell': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone 2 - Turtle Zone Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone 3 - Whale Course': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Coin", 1), 'type': 'level'}, - 'Turtle Zone 3 - Whale Course Midway Bell': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Zone 3 - Whale Course Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone - Secret Course': {'id': 0x1A, 'ram_index': 37, 'type': 'level'} -} - -items = { - "Space Zone Progression": ItemClassification.progression, - "Space Zone Secret": ItemClassification.progression, - "Tree Zone Progression": ItemClassification.progression, - "Tree Zone Progression x2": ItemClassification.progression, - "Tree Zone Secret": ItemClassification.progression, - "Macro Zone Progression": ItemClassification.progression, - "Macro Zone Progression x2": ItemClassification.progression, - "Macro Zone Secret 1": ItemClassification.progression, - "Macro Zone Secret 2": ItemClassification.progression_skip_balancing, - "Pumpkin Zone Progression": ItemClassification.progression, - "Pumpkin Zone Progression x2": ItemClassification.progression, - "Pumpkin Zone Secret 1": ItemClassification.progression, - "Pumpkin Zone Secret 2": ItemClassification.progression, - "Mario Zone Progression": ItemClassification.progression, - "Mario Zone Progression x2": ItemClassification.progression, - "Turtle Zone Progression": ItemClassification.progression, - "Turtle Zone Progression x2": ItemClassification.progression, - "Turtle Zone Secret": ItemClassification.progression, - "Tree Coin": ItemClassification.progression_skip_balancing, - "Space Coin": ItemClassification.progression_skip_balancing, - "Macro Coin": ItemClassification.progression_skip_balancing, - "Pumpkin Coin": ItemClassification.progression_skip_balancing, - "Mario Coin": ItemClassification.progression_skip_balancing, - "Turtle Coin": ItemClassification.progression_skip_balancing, - "Mushroom": ItemClassification.progression, - "Fire Flower": ItemClassification.progression, - "Carrot": ItemClassification.progression, - "Space Physics": ItemClassification.progression_skip_balancing, - "Hippo Bubble": ItemClassification.progression_skip_balancing, - "Swim": ItemClassification.progression, - "Pipe Traversal": ItemClassification.progression, - "Pipe Traversal - Down": ItemClassification.progression, - "Pipe Traversal - Up": ItemClassification.progression, - "Pipe Traversal - Right": ItemClassification.progression, - "Pipe Traversal - Left": ItemClassification.progression_skip_balancing, - "Super Star Duration Increase": ItemClassification.filler, - "Easy Mode": ItemClassification.useful, - "Normal Mode": ItemClassification.trap, - "Auto Scroll": ItemClassification.trap, - "Mushroom Zone Midway Bell": ItemClassification.filler, - "Tree Zone 1 - Invincibility! Midway Bell": ItemClassification.filler, - "Tree Zone 2 - In the Trees Midway Bell": ItemClassification.progression_skip_balancing, - "Tree Zone 4 - Honeybees Midway Bell": ItemClassification.progression_skip_balancing, - "Tree Zone 5 - The Big Bird Midway Bell": ItemClassification.filler, - "Space Zone 1 - Moon Stage Midway Bell": ItemClassification.filler, - "Space Zone 2 - Star Stage Midway Bell": ItemClassification.progression_skip_balancing, - "Macro Zone 1 - The Ant Monsters Midway Bell": ItemClassification.progression_skip_balancing, - "Macro Zone 2 - In the Syrup Sea Midway Bell": ItemClassification.progression_skip_balancing, - "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": ItemClassification.progression_skip_balancing, - "Macro Zone 4 - One Mighty Mouse Midway Bell": ItemClassification.filler, - "Pumpkin Zone 1 - Bat Course Midway Bell": ItemClassification.progression_skip_balancing, - "Pumpkin Zone 2 - Cyclops Course Midway Bell": ItemClassification.filler, - "Pumpkin Zone 3 - Ghost House Midway Bell": ItemClassification.filler, - "Pumpkin Zone 4 - Witch's Mansion Midway Bell": ItemClassification.filler, - "Mario Zone 1 - Fiery Blocks Midway Bell": ItemClassification.progression_skip_balancing, - "Mario Zone 2 - Mario the Circus Star! Midway Bell": ItemClassification.filler, - "Mario Zone 3 - Beware: Jagged Spikes Midway Bell": ItemClassification.filler, - "Mario Zone 4 - Three Mean Pigs! Midway Bell": ItemClassification.filler, - "Turtle Zone 1 - Cheep Cheep Course Midway Bell": ItemClassification.filler, - "Turtle Zone 2 - Turtle Zone Midway Bell": ItemClassification.progression_skip_balancing, - "Turtle Zone 3 - Whale Course Midway Bell": ItemClassification.filler, -} - - class MarioLand2Settings(settings.Group): class SML2RomFile(settings.UserFilePath): """File name of the Super Mario Land 2 1.0 ROM""" @@ -256,8 +112,8 @@ def create_regions(self): if "Midway Bell" in location_name and not self.multiworld.shuffle_midway_bells[self.player]: continue - region.locations.append(Location(self.player, location_name, self.location_name_to_id[location_name], - region)) + 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", @@ -265,7 +121,7 @@ def create_regions(self): self.player)) castle = Region("Mario's Castle", self.player, self.multiworld) menu_region.connect(castle) - wario = Location(self.player, "Mario's Castle - Wario", parent=castle) + 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)) @@ -301,7 +157,7 @@ def set_rules(self): state.has("Mario Coin", self.player), state.has("Turtle Coin", self.player) ].count(True) >= self.multiworld.required_golden_coins[self.player]) } - rules = { + location_rules = { "Hippo Zone": lambda state: state.has_any(["Hippo Bubble", "Carrot", "Swim"], self.player), # It is possible, however tricky, to beat the Moon Stage without Carrot or Space Physics. # However, it requires somewhat precisely jumping off enemies. Enemy shuffle may make this impossible. @@ -396,7 +252,7 @@ def set_rules(self): for entrance, rule in entrance_rules.items(): self.multiworld.get_entrance(entrance, self.player).access_rule = rule - for level, rule in rules.items(): + for level, rule in location_rules.items(): if ("Midway Bell" not in level) or self.multiworld.shuffle_midway_bells[self.player]: self.multiworld.get_location(level, self.player).access_rule = rule @@ -505,5 +361,9 @@ def modify_multidata(self, multidata: dict): multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]] +class MarioLand2Location(Location): + game = "Super Mario Land 2" + + class MarioLand2Item(Item): game = "Super Mario Land 2" diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 4d4625f40b8c..c0ec28690f47 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -36,7 +36,10 @@ async def set_auth(self, ctx): ctx.auth = auth_name async def game_watcher(self, ctx: BizHawkClientContext): - from . import locations, items, START_IDS + from . import START_IDS + from .items import items + from .locations import locations + (game_loaded_check, level_data, music, auto_scroll_enabled, auto_scroll_levels, current_level, midway_point, bcd_lives) = \ await read(ctx.bizhawk_ctx, [(0x0046, 10, "CartRAM"), (0x0848, 42, "CartRAM"), (0x0469, 1, "CartRAM"), diff --git a/worlds/marioland2/items.py b/worlds/marioland2/items.py new file mode 100644 index 000000000000..fec99c90f5a2 --- /dev/null +++ b/worlds/marioland2/items.py @@ -0,0 +1,65 @@ +from BaseClasses import ItemClassification + +items = { + "Space Zone Progression": ItemClassification.progression, + "Space Zone Secret": ItemClassification.progression, + "Tree Zone Progression": ItemClassification.progression, + "Tree Zone Progression x2": ItemClassification.progression, + "Tree Zone Secret": ItemClassification.progression, + "Macro Zone Progression": ItemClassification.progression, + "Macro Zone Progression x2": ItemClassification.progression, + "Macro Zone Secret 1": ItemClassification.progression, + "Macro Zone Secret 2": ItemClassification.progression_skip_balancing, + "Pumpkin Zone Progression": ItemClassification.progression, + "Pumpkin Zone Progression x2": ItemClassification.progression, + "Pumpkin Zone Secret 1": ItemClassification.progression, + "Pumpkin Zone Secret 2": ItemClassification.progression, + "Mario Zone Progression": ItemClassification.progression, + "Mario Zone Progression x2": ItemClassification.progression, + "Turtle Zone Progression": ItemClassification.progression, + "Turtle Zone Progression x2": ItemClassification.progression, + "Turtle Zone Secret": ItemClassification.progression, + "Tree Coin": ItemClassification.progression_skip_balancing, + "Space Coin": ItemClassification.progression_skip_balancing, + "Macro Coin": ItemClassification.progression_skip_balancing, + "Pumpkin Coin": ItemClassification.progression_skip_balancing, + "Mario Coin": ItemClassification.progression_skip_balancing, + "Turtle Coin": ItemClassification.progression_skip_balancing, + "Mushroom": ItemClassification.progression, + "Fire Flower": ItemClassification.progression, + "Carrot": ItemClassification.progression, + "Space Physics": ItemClassification.progression_skip_balancing, + "Hippo Bubble": ItemClassification.progression_skip_balancing, + "Swim": ItemClassification.progression, + "Pipe Traversal": ItemClassification.progression, + "Pipe Traversal - Down": ItemClassification.progression, + "Pipe Traversal - Up": ItemClassification.progression, + "Pipe Traversal - Right": ItemClassification.progression, + "Pipe Traversal - Left": ItemClassification.progression_skip_balancing, + "Super Star Duration Increase": ItemClassification.filler, + "Easy Mode": ItemClassification.useful, + "Normal Mode": ItemClassification.trap, + "Auto Scroll": ItemClassification.trap, + "Mushroom Zone Midway Bell": ItemClassification.filler, + "Tree Zone 1 - Invincibility! Midway Bell": ItemClassification.filler, + "Tree Zone 2 - In the Trees Midway Bell": ItemClassification.progression_skip_balancing, + "Tree Zone 4 - Honeybees Midway Bell": ItemClassification.progression_skip_balancing, + "Tree Zone 5 - The Big Bird Midway Bell": ItemClassification.filler, + "Space Zone 1 - Moon Stage Midway Bell": ItemClassification.filler, + "Space Zone 2 - Star Stage Midway Bell": ItemClassification.progression_skip_balancing, + "Macro Zone 1 - The Ant Monsters Midway Bell": ItemClassification.progression_skip_balancing, + "Macro Zone 2 - In the Syrup Sea Midway Bell": ItemClassification.progression_skip_balancing, + "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": ItemClassification.progression_skip_balancing, + "Macro Zone 4 - One Mighty Mouse Midway Bell": ItemClassification.filler, + "Pumpkin Zone 1 - Bat Course Midway Bell": ItemClassification.progression_skip_balancing, + "Pumpkin Zone 2 - Cyclops Course Midway Bell": ItemClassification.filler, + "Pumpkin Zone 3 - Ghost House Midway Bell": ItemClassification.filler, + "Pumpkin Zone 4 - Witch's Mansion Midway Bell": ItemClassification.filler, + "Mario Zone 1 - Fiery Blocks Midway Bell": ItemClassification.progression_skip_balancing, + "Mario Zone 2 - Mario the Circus Star! Midway Bell": ItemClassification.filler, + "Mario Zone 3 - Beware: Jagged Spikes Midway Bell": ItemClassification.filler, + "Mario Zone 4 - Three Mean Pigs! Midway Bell": ItemClassification.filler, + "Turtle Zone 1 - Cheep Cheep Course Midway Bell": ItemClassification.filler, + "Turtle Zone 2 - Turtle Zone Midway Bell": ItemClassification.progression_skip_balancing, + "Turtle Zone 3 - Whale Course Midway Bell": ItemClassification.filler, +} \ No newline at end of file diff --git a/worlds/marioland2/locations.py b/worlds/marioland2/locations.py new file mode 100644 index 000000000000..19e001b111b5 --- /dev/null +++ b/worlds/marioland2/locations.py @@ -0,0 +1,61 @@ +locations = { + 'Mushroom Zone': {'id': 0x00, 'ram_index': 0, 'type': 'level'}, + 'Mushroom Zone Midway Bell': {'id': 0x00, 'ram_index': 0, 'clear_condition': ("Mushroom Zone Midway Bell", 1), 'type': 'bell'}, + 'Scenic Course': {'id': 0x19, 'ram_index': 40, 'type': 'level'}, + 'Tree Zone 1 - Invincibility!': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone Progression", 1), 'type': 'level'}, + 'Tree Zone 1 - Invincibility! Midway Bell': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone 1 - Invincibility! Midway Bell", 1), 'type': 'bell'}, + 'Tree Zone 2 - In the Trees': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Progression", 2), 'type': 'level'}, + 'Tree Zone 2 - In the Trees Secret Exit': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Secret", 1), 'type': 'secret'}, + 'Tree Zone 2 - In the Trees Midway Bell': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone 2 - In the Trees Midway Bell", 1), 'type': 'bell'}, + 'Tree Zone 3 - The Exit': {'id': 0x04, 'ram_index': 4, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, + 'Tree Zone 4 - Honeybees': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, + 'Tree Zone 4 - Honeybees Midway Bell': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone 4 - Honeybees Midway Bell", 1), 'type': 'bell'}, + 'Tree Zone 5 - The Big Bird': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Coin", 1), 'type': 'level'}, + 'Tree Zone 5 - The Big Bird Midway Bell': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Zone 5 - The Big Bird Midway Bell", 1), 'type': 'bell'}, + 'Tree Zone - Secret Course': {'id': 0x1D, 'ram_index': 36, 'type': 'level'}, + 'Hippo Zone': {'id': 0x11, 'ram_index': 31, 'type': 'level'}, + 'Space Zone 1 - Moon Stage': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Progression", 1), 'type': 'level'}, + 'Space Zone 1 - Moon Stage Secret Exit': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Secret", 1), 'type': 'secret'}, + 'Space Zone 1 - Moon Stage Midway Bell': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone 1 - Moon Stage Midway Bell", 1), 'type': 'bell'}, + 'Space Zone - Secret Course': {'id': 0x1C, 'ram_index': 41, 'type': 'level'}, + 'Space Zone 2 - Star Stage': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Coin", 1), 'type': 'level'}, + 'Space Zone 2 - Star Stage Midway Bell': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Zone 2 - Star Stage Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 1 - The Ant Monsters': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Progression", 1), 'type': 'level'}, + 'Macro Zone 1 - The Ant Monsters Secret Exit': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Secret 1", 1), 'type': 'secret'}, + 'Macro Zone 1 - The Ant Monsters Midway Bell': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone 1 - The Ant Monsters Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 2 - In the Syrup Sea': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone Progression", 2), 'type': 'level'}, + 'Macro Zone 2 - In the Syrup Sea Midway Bell': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone 2 - In the Syrup Sea Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 3 - Fiery Mario-Special Agent': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone Progression", 3), 'type': 'level'}, + 'Macro Zone 3 - Fiery Mario-Special Agent Midway Bell': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 4 - One Mighty Mouse': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Coin", 1), 'type': 'level'}, + 'Macro Zone 4 - One Mighty Mouse Midway Bell': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Zone 4 - One Mighty Mouse Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone - Secret Course': {'id': 0x1E, 'ram_index': 35, 'clear_condition': ("Macro Zone Secret 2", 1), 'type': 'level'}, + 'Pumpkin Zone 1 - Bat Course': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone Progression", 1), 'type': 'level'}, + 'Pumpkin Zone 1 - Bat Course Midway Bell': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone 1 - Bat Course Midway Bell", 1), 'type': 'bell'}, + 'Pumpkin Zone 2 - Cyclops Course': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'level'}, + 'Pumpkin Zone 2 - Cyclops Course Secret Exit': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Secret 1", 1), 'type': 'secret'}, + 'Pumpkin Zone 2 - Cyclops Course Midway Bell': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'bell'}, + 'Pumpkin Zone 3 - Ghost House': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'level'}, + 'Pumpkin Zone 3 - Ghost House Secret Exit': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Secret 2", 1), 'type': 'secret'}, + 'Pumpkin Zone 3 - Ghost House Midway Bell': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'bell'}, + "Pumpkin Zone 4 - Witch's Mansion": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'level'}, + "Pumpkin Zone 4 - Witch's Mansion Midway Bell": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'bell'}, + 'Pumpkin Zone - Secret Course 1': {'id': 0x1B, 'ram_index': 38, 'type': 'level'}, + 'Pumpkin Zone - Secret Course 2': {'id': 0x1F, 'ram_index': 39, 'type': 'level'}, + 'Mario Zone 1 - Fiery Blocks': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone Progression", 1), 'type': 'level'}, + 'Mario Zone 1 - Fiery Blocks Midway Bell': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone 1 - Fiery Blocks Midway Bell", 1), 'type': 'bell'}, + 'Mario Zone 2 - Mario the Circus Star!': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone Progression", 2), 'type': 'level'}, + 'Mario Zone 2 - Mario the Circus Star! Midway Bell': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone 2 - Mario the Circus Star! Midway Bell", 1), 'type': 'bell'}, + 'Mario Zone 3 - Beware: Jagged Spikes': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone Progression", 3), 'type': 'level'}, + 'Mario Zone 3 - Beware: Jagged Spikes Midway Bell': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone 3 - Beware: Jagged Spikes Midway Bell", 1), 'type': 'bell'}, + 'Mario Zone 4 - Three Mean Pigs!': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Coin", 1), 'type': 'level'}, + 'Mario Zone 4 - Three Mean Pigs! Midway Bell': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Zone 4 - Three Mean Pigs! Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone 1 - Cheep Cheep Course': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone Progression", 1), 'type': 'level'}, + 'Turtle Zone 1 - Cheep Cheep Course Midway Bell': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone 1 - Cheep Cheep Course Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone 2 - Turtle Zone': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Progression", 2), 'type': 'level'}, + 'Turtle Zone 2 - Turtle Zone Secret Exit': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Secret", 1), 'type': 'secret'}, + 'Turtle Zone 2 - Turtle Zone Midway Bell': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone 2 - Turtle Zone Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone 3 - Whale Course': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Coin", 1), 'type': 'level'}, + 'Turtle Zone 3 - Whale Course Midway Bell': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Zone 3 - Whale Course Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone - Secret Course': {'id': 0x1A, 'ram_index': 37, 'type': 'level'} +} \ No newline at end of file diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py new file mode 100644 index 000000000000..e39cd0115c86 --- /dev/null +++ b/worlds/marioland2/logic.py @@ -0,0 +1,18 @@ +def has_pipe_right(state, player): + return state.has_any(["Pipe Traversal - Right", "Pipe Traversal"], player) + + +def has_pipe_left(state, player): + return state.has_any(["Pipe Traversal - Left", "Pipe Traversal"], player) + + +def has_pipe_down(state, player): + return state.has_any(["Pipe Traversal - Down", "Pipe Traversal"], player) + + +def has_pipe_up(state, player): + return state.has_any(["Pipe Traversal - Up", "Pipe Traversal"], player) + + +def has_level_progression(state, item, player, count=1): + return state.count(item, player) + (state.count(item + " x2", player) * 2) >= count \ No newline at end of file diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 49f0e4e61080..b0e90eb46ff6 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -18,7 +18,7 @@ class GoldenCoinsRequired(Range): class DifficultyMode(Choice): """Play in normal or easy mode. You can also start in Normal Mode with an "upgrade" to Easy Mode in the item pool, - or start in Easy Mode with a Normal Mode "Trap" in the item pool.""" + or start in Easy Mode with a Normal Mode "trap" in the item pool.""" display_name = "Difficulty Mode" option_normal = 0 option_easy = 1 From f94da4c13846bb8e1fbf1cdca6dede65c4490b11 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:48:21 -0500 Subject: [PATCH 028/113] Update worlds/marioland2/docs/en_Super Mario Land 2.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> --- worlds/marioland2/docs/en_Super Mario Land 2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/marioland2/docs/en_Super Mario Land 2.md b/worlds/marioland2/docs/en_Super Mario Land 2.md index a7e99737d5af..97115f5eafb1 100644 --- a/worlds/marioland2/docs/en_Super Mario Land 2.md +++ b/worlds/marioland2/docs/en_Super Mario Land 2.md @@ -1,8 +1,8 @@ # Super Mario Land 2: 6 Golden Coins -## Where is the settings page? +## Where is the options page? -The [player settings page for this game](../player-settings) contains all the options you need to configure and export a +The [player options page for this game](../player-options) contains all the options you need to configure and export a config file. ## What items and locations get shuffled? From c63cba983b42a92554529304ca2891c79892f12d Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:48:37 -0500 Subject: [PATCH 029/113] Update worlds/marioland2/docs/setup_en.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> --- worlds/marioland2/docs/setup_en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/docs/setup_en.md b/worlds/marioland2/docs/setup_en.md index f7e5d09a9100..e31cf1efda85 100644 --- a/worlds/marioland2/docs/setup_en.md +++ b/worlds/marioland2/docs/setup_en.md @@ -37,7 +37,7 @@ an experience customized for their taste, and different players in the same mult ### Where do I get a YAML file? -You can generate a yaml or download a template by visiting the [Super Mario Land 2 Player Settings Page](/games/Super%20Mario%20Land%202/player-settings) +You can generate a yaml or download a template by visiting the [Super Mario Land 2 Player Options Page](/games/Super%20Mario%20Land%202/player-options) ## Joining a MultiWorld Game From 46900e0205cd1a8c376ce7b74b50772d66928201 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:48:46 -0500 Subject: [PATCH 030/113] Update worlds/marioland2/docs/setup_en.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> --- worlds/marioland2/docs/setup_en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/docs/setup_en.md b/worlds/marioland2/docs/setup_en.md index e31cf1efda85..293f7487068c 100644 --- a/worlds/marioland2/docs/setup_en.md +++ b/worlds/marioland2/docs/setup_en.md @@ -43,7 +43,7 @@ You can generate a yaml or download a template by visiting the [Super Mario Land ### Generating and Patching a Game -1. Create your settings file (YAML). +1. Create your options file (YAML). 2. Follow the general Archipelago instructions for [generating a game](../../Archipelago/setup/en#generating-a-game). This will generate an output file for you. Your patch file will have a `.apsml2` file extension. 3. Open `ArchipelagoLauncher.exe` From e648a74486ccc8ede909ce69bbd44358b493e094 Mon Sep 17 00:00:00 2001 From: Alchav Date: Fri, 19 Jan 2024 12:01:56 -0500 Subject: [PATCH 031/113] Update readme and codeowners --- README.md | 1 + docs/CODEOWNERS | 3 +++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index a1e03293d587..3173c1230ebd 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Currently, the following games are supported: * Heretic * Landstalker: The Treasures of King Nole * Final Fantasy Mystic Quest +* Super Mario Land 2: 6 Golden Coins For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/). Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index e221371b2417..ac2c36958b58 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -137,6 +137,9 @@ # Super Mario 64 /worlds/sm64ex/ @N00byKing +# Super Mario Land 2: 6 Golden Coins +/worlds/marioland2/ @Alchav + # Super Mario World /worlds/smw/ @PoryGone From 6076bbf3b0538ba2212f96bf8437767625302bde Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 20 Jan 2024 18:18:25 -0500 Subject: [PATCH 032/113] Python 3.8 compatibility fix --- worlds/marioland2/client.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index c0ec28690f47..61dcb7beb176 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -49,10 +49,10 @@ async def game_watcher(self, ctx: BizHawkClientContext): (0x02A0, 1, "CartRAM"), (0x022C, 1, "CartRAM")]) - current_level = int.from_bytes(current_level) - midway_point = int.from_bytes(midway_point) - music = int.from_bytes(music) - auto_scroll_enabled = int.from_bytes(auto_scroll_enabled) + current_level = int.from_bytes(current_level, "big") + midway_point = int.from_bytes(midway_point, "big") + music = int.from_bytes(music, "big") + auto_scroll_enabled = int.from_bytes(auto_scroll_enabled, "big") level_data = list(level_data) lives = bcd_lives.hex() @@ -167,7 +167,8 @@ async def game_watcher(self, ctx: BizHawkClientContext): data_writes.append((0x02C8, [0x01], "CartRAM")) success = await guarded_write(ctx.bizhawk_ctx, data_writes, [(0x0848, level_data, "CartRAM"), - (0x022C, [int.from_bytes(bcd_lives)], "CartRAM")]) + (0x022C, [int.from_bytes(bcd_lives, "big")], + "CartRAM")]) if success and energy_link_add is not None: await ctx.send_msgs([{ From 33b197e9041688191e0336f68f64b13f68166caf Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 20 Jan 2024 18:18:10 -0500 Subject: [PATCH 033/113] Pumpkin Zone 2 secret exit fix --- worlds/marioland2/basepatch.bsdiff4 | Bin 724 -> 723 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index 6322d79274014fc9c058320b965b2c7672f18902..bcc736ffd979e64bd04a2391d7c9bdeab7a44a64 100644 GIT binary patch delta 384 zcmV-`0e}9~1=9r)LQ_OZMn*I+LjV8(00000X^|0Jf7W`}c>n+-fB*mfU3h_U_Yzzn z1?k2p078HW6%v3E!JuUnNI>ZxKyI)BHW&>GrcY6X0|}ERfEW!-3A7U?jDTnxOiAXD zil3>J^#A|?pa1{>000001Jnqm5&$#?jSU8X00x6V0009(1L^(hVoC`LAxsEz0t>`I zpgmHQe?yUsLVN?a&g+ZMH3TDo(1VjQ@5fB4ighG4)QUz63epWo8vj5P%7>^Y{(c0MQb7j#U(5r&mnmD|3{S~Z?jP*FpMtsnN@f~F8UU^-xk e5fKqpTNEeXQ=3of7ji{7P>|Mo*Lkze0S5uew~hb+ delta 385 zcmV-{0e=3|1=Ix*LQ_OZMn*I+LjV8(00000YLO9Ke|uifn*abJ|NsB~U3h_U_Yzzn z1?k2J078NY6%v3EfuLm+NI>ZvKyI)BHW&<&lO~NlO$?ee4FgOd%}oqPqGV{u(TEOJdbT0mqFD*=j7 zVo?JCfA&sc4re(?AkbGJi$wweacad#w}vK>lGa=KhI9-40z@3J0M3C=y%_|tg^~dC zfJVv$Kuma50e7KLXTXA)^Xvo>0G0$q0NG#=p#W8ZRaK#xlPk9eTp>y#bb(JQl%Q_M zG*pb;N_|@MR&dF<+%5ZR24B#}BqnqwwKkC!!mzj}N|%#37&4=ra~e%r5p#D!V~p_O_+tHT*nwFC$R f2nhs|O-%c`D+}!}_`8xR!i0m`_I%j0%mD`hsLz&o From 3c542e3e15522ad2d77487cfb1092818821fd2ec Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 20 Jan 2024 20:16:28 -0500 Subject: [PATCH 034/113] Fix client bug when energy link disabled --- worlds/marioland2/client.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 61dcb7beb176..c75e127b1873 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -191,10 +191,11 @@ async def game_watcher(self, ctx: BizHawkClientContext): def on_package(self, ctx: BizHawkClientContext, cmd: str, args: dict): super().on_package(ctx, cmd, args) if cmd == 'Connected': - ctx.set_notify(f"EnergyLink{ctx.team}") - if ctx.slot_data and "energy_link" in ctx.slot_data and ctx.slot_data["energy_link"] and ctx.ui: - ctx.ui.enable_energy_link() - ctx.ui.energy_link_label.text = "Lives: Standby" + if ctx.slot_data["energy_link"]: + ctx.set_notify(f"EnergyLink{ctx.team}") + if ctx.ui: + ctx.ui.enable_energy_link() + ctx.ui.energy_link_label.text = "Lives: Standby" elif cmd == "SetReply" and args["key"].startswith("EnergyLink"): if ctx.ui: ctx.ui.energy_link_label.text = f"Lives: {int(args['value'] / BANK_EXCHANGE_RATE)}" From 9b8f1ce16089f1ba0aa380519e35d132e3303a40 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 5 Feb 2024 00:01:24 -0500 Subject: [PATCH 035/113] Add SML2 description --- worlds/marioland2/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 36f259bfc887..ad114bf0ba36 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -40,6 +40,10 @@ class MarioLand2WebWorld(WebWorld): 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" From 718595235ae83490831d115ced6dad96a391b3cf Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 10 Feb 2024 10:34:12 -0500 Subject: [PATCH 036/113] remove backwards compatibility energy link test --- worlds/marioland2/client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index c75e127b1873..ba403cf7dd6f 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -112,8 +112,7 @@ async def game_watcher(self, ctx: BizHawkClientContext): new_lives = int(lives) energy_link_add = None - # TODO: temporary check to ensure energy_link key for backwards compatibility - if ctx.slot_data and "energy_link" in ctx.slot_data and ctx.slot_data["energy_link"]: + if ctx.slot_data and ctx.slot_data["energy_link"]: if new_lives == 0: if (f"EnergyLink{ctx.team}" in ctx.stored_data and ctx.stored_data[f"EnergyLink{ctx.team}"] From cf0417a3472d1c57277f83907a4ceeb6ef4c0204 Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 10 Feb 2024 10:35:09 -0500 Subject: [PATCH 037/113] remove unnecessary logic --- worlds/marioland2/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index ad114bf0ba36..16a9a93b0c00 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -207,7 +207,6 @@ def set_rules(self): "Macro Zone 1 - The Ant Monsters Secret Exit": lambda state: (has_pipe_down(state, self.player) or state.has("Macro Zone 1 - The Ant Monsters Midway Bell", self.player)) and state.has("Fire Flower", self.player) and has_pipe_up(state, self.player), - "Macro Zone - Secret Course": lambda state: state.has_any(["Mushroom", "Fire Flower"], self.player), "Macro Zone 2 - In the Syrup Sea": lambda state: (has_pipe_down(state, self.player) or state.has( "Macro Zone 2 - In the Syrup Sea Midway Bell", self.player)) and state.has("Swim", self.player) and has_pipe_up(state, self.player), From 9318b71d1cf2b229fa10ec235d18c38c2388dbd4 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 4 Mar 2024 15:41:29 -0500 Subject: [PATCH 038/113] Options dataclass --- worlds/marioland2/__init__.py | 42 ++++++++++++++++++----------------- worlds/marioland2/options.py | 30 ++++++++++++------------- worlds/marioland2/rom.py | 22 +++++++++--------- 3 files changed, 48 insertions(+), 46 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 16a9a93b0c00..b0b34f278fe4 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -8,7 +8,7 @@ from . import client from .rom import generate_output, SuperMarioLand2DeltaPatch -from .options import sml2options +from .options import SML2Options from .locations import locations from .items import items from .logic import has_pipe_up, has_pipe_down, has_pipe_left, has_pipe_right, has_level_progression @@ -73,7 +73,8 @@ class MarioLand2World(World): "Bells": {location for location in locations if locations[location]["type"] == "bell"}, } - option_definitions = sml2options + options_dataclass = SML2Options + options: SML2Options generate_output = generate_output @@ -114,7 +115,7 @@ def create_regions(self): self.multiworld.regions.append(region) created_regions.append(region_name) - if "Midway Bell" in location_name and not self.multiworld.shuffle_midway_bells[self.player]: + if "Midway Bell" in location_name and not self.options.shuffle_midway_bells: continue region.locations.append(MarioLand2Location(self.player, location_name, self.location_name_to_id[location_name], region)) @@ -159,7 +160,7 @@ def set_rules(self): state.has("Tree Coin", self.player), state.has("Space Coin", self.player), state.has("Macro Coin", self.player), state.has("Pumpkin Coin", self.player), state.has("Mario Coin", self.player), state.has("Turtle Coin", self.player) - ].count(True) >= self.multiworld.required_golden_coins[self.player]) + ].count(True) >= self.options.required_golden_coins) } location_rules = { "Hippo Zone": lambda state: state.has_any(["Hippo Bubble", "Carrot", "Swim"], self.player), @@ -176,11 +177,12 @@ def set_rules(self): # Otherwise, you need the bell item from the item pool, or you need to be able to take damage twice in one # visit. "Space Zone 2 - Star Stage": lambda state: has_pipe_right(state, self.player) and (state.has( - "Space Physics", self.player) or ((not state.multiworld.shuffle_midway_bells[self.player]) - and state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player)) or (state.has( - "Mushroom", self.player) and state.has_any(["Fire Flower", "Carrot"], self.player)) - or (state.has("Space Zone 2 - Star Stage Midway Bell", self.player) and state.has_any( - ["Mushroom", "Fire Flower", "Carrot"], self.player))), + "Space Physics", self.player) or ((not + state.multiworld.worlds[self.player].options.shuffle_midway_bells) and state.has_any(["Mushroom", + "Fire Flower", "Carrot"], self.player)) or (state.has("Mushroom", self.player) + and state.has_any(["Fire Flower", "Carrot"], self.player)) + or (state.has("Space Zone 2 - Star Stage Midway Bell", self.player) and state.has_any(["Mushroom", + "Fire Flower", "Carrot"], self.player))), "Space Zone 2 - Star Stage Midway Bell": lambda state: state.has_any( ["Space Physics", "Space Zone 2 - Star Stage Midway Bell", "Mushroom", "Fire Flower", "Carrot"], self.player), @@ -256,7 +258,7 @@ def set_rules(self): self.multiworld.get_entrance(entrance, self.player).access_rule = rule for level, rule in location_rules.items(): - if ("Midway Bell" not in level) or self.multiworld.shuffle_midway_bells[self.player]: + if ("Midway Bell" not in level) or self.options.shuffle_midway_bells: self.multiworld.get_location(level, self.player).access_rule = rule self.multiworld.completion_condition[self.player] = lambda state: state.has("Wario Defeated", self.player) @@ -289,7 +291,7 @@ def create_items(self): "Super Star Duration Increase": 6, } - if self.multiworld.coinsanity[self.player]: + if self.options.coinsanity: for item in self.item_name_groups["Coins"]: item_counts[item] = 1 else: @@ -306,21 +308,21 @@ def create_items(self): location.address = None location.item.code = None - if self.multiworld.shuffle_midway_bells[self.player]: + if self.options.shuffle_midway_bells: for item in [item for item in items if "Midway Bell" in item]: item_counts[item] = 1 - if self.multiworld.difficulty_mode[self.player] == "easy_to_normal": + if self.options.difficulty_mode == "easy_to_normal": item_counts["Normal Mode"] = 1 - elif self.multiworld.difficulty_mode[self.player] == "normal_to_easy": + elif self.options.difficulty_mode == "normal_to_easy": item_counts["Easy Mode"] = 1 else: item_counts["Super Star Duration Increase"] += 1 - if self.multiworld.shuffle_pipe_traversal[self.player] == "single": + if self.options.shuffle_pipe_traversal == "single": item_counts["Super Star Duration Increase"] -= 1 item_counts["Pipe Traversal"] = 1 - elif self.multiworld.shuffle_pipe_traversal[self.player] == "split": + elif self.options.shuffle_pipe_traversal == "split": item_counts["Super Star Duration Increase"] -= 4 item_counts["Pipe Traversal - Right"] = 1 item_counts["Pipe Traversal - Left"] = 1 @@ -329,7 +331,7 @@ def create_items(self): else: self.multiworld.push_precollected(self.create_item("Pipe Traversal")) - if self.multiworld.auto_scroll_trap[self.player]: + if self.options.auto_scroll_trap: item_counts["Super Star Duration Increase"] -= 1 item_counts["Auto Scroll"] = 1 @@ -343,11 +345,11 @@ def create_items(self): def fill_slot_data(self): return { - "mode": self.multiworld.difficulty_mode[self.player].value, + "mode": self.options.difficulty_mode.value, "stars": max(len([loc for loc in self.multiworld.get_filled_locations() if loc.item.player == self.player and loc.item.name == "Super Star Duration Increase"]), 1), - "midway_bells": self.multiworld.shuffle_midway_bells[self.player].value, - "energy_link": self.multiworld.energy_link[self.player].value + "midway_bells": self.options.shuffle_midway_bells.value, + "energy_link": self.options.energy_link.value } def create_item(self, name: str) -> Item: diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index b0e90eb46ff6..35043c09f8b6 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -1,4 +1,5 @@ -from Options import Toggle, Choice, NamedRange, Range +from Options import Toggle, Choice, NamedRange, Range, PerGameCommonOptions +from dataclasses import dataclass class GoldenCoins(Toggle): @@ -81,17 +82,16 @@ class EnergyLink(Toggle): display_name = "Energy Link" default = 1 - -sml2options = { - "coinsanity": GoldenCoins, - "required_golden_coins": GoldenCoinsRequired, - "difficulty_mode": DifficultyMode, - "shuffle_midway_bells": ShuffleMidwayBells, - "shuffle_pipe_traversal": ShufflePipeTraversal, - "randomize_enemies": RandomizeEnemies, - "randomize_platforms": RandomizePlatforms, - "auto_scroll_levels": AutoScrollLevels, - "auto_scroll_trap": AutoScrollTrap, - "randomize_music": RandomizeMusic, - "energy_link": EnergyLink -} \ No newline at end of file +@dataclass +class SML2Options(PerGameCommonOptions): + coinsanity: GoldenCoins + required_golden_coins: GoldenCoinsRequired + difficulty_mode: DifficultyMode + shuffle_midway_bells: ShuffleMidwayBells + shuffle_pipe_traversal: ShufflePipeTraversal + randomize_enemies: RandomizeEnemies + randomize_platforms: RandomizePlatforms + auto_scroll_levels: AutoScrollLevels + auto_scroll_trap: AutoScrollTrap + randomize_music: RandomizeMusic + energy_link: EnergyLink diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 5c0bba7ceff3..d8f24d020bfa 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -5,7 +5,7 @@ import Utils -from Patch import APDeltaPatch +from worlds.Files import APDeltaPatch from settings import get_settings from .rom_addresses import rom_addresses @@ -173,25 +173,25 @@ def generate_output(self, output_directory: str): random = self.multiworld.per_slot_randoms[self.player] - if self.multiworld.randomize_enemies[self.player]: + if self.options.randomize_enemies: randomize_enemies(data, random) - if self.multiworld.randomize_platforms[self.player]: + if self.options.randomize_platforms: randomize_platforms(data, random) - if self.multiworld.auto_scroll_levels[self.player] > -1: - randomize_auto_scroll_levels(data, random, self.multiworld.auto_scroll_levels[self.player].value) - if self.multiworld.randomize_music[self.player]: + if self.options.auto_scroll_levels > -1: + randomize_auto_scroll_levels(data, random, self.options.auto_scroll_levels.value) + if self.options.randomize_music: randomize_music(data, random) - if self.multiworld.auto_scroll_trap[self.player]: + if self.options.auto_scroll_trap: data[rom_addresses["Auto_Scroll_Disable"]] = 0xAF - if self.multiworld.coinsanity[self.player]: + if self.options.coinsanity: data[rom_addresses["Coin_Shuffle"]] = 0x40 - if self.multiworld.shuffle_midway_bells[self.player]: + if self.options.shuffle_midway_bells: data[rom_addresses["Disable_Midway_Bell"]] = 0xC9 - data[rom_addresses["Required_Golden_Coins"]] = self.multiworld.required_golden_coins[self.player].value + data[rom_addresses["Required_Golden_Coins"]] = self.options.required_golden_coins.value - if self.multiworld.energy_link[self.player]: + if self.options.energy_link: # start with 1 life if Energy Link is on so that you don't deposit lives at the start of the game. data[rom_addresses["Starting_Lives"]] = 1 From d9e261b8dd8cf04642f8a23710b97973c6b617e4 Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 30 Mar 2024 19:22:17 -0400 Subject: [PATCH 039/113] Coinsanity! --- worlds/marioland2/__init__.py | 338 +++++++---- worlds/marioland2/basepatch.bsdiff4 | Bin 723 -> 1155 bytes worlds/marioland2/client.py | 112 ++-- worlds/marioland2/coin_logic.py | 294 ++++++++++ .../marioland2/docs/en_Super Mario Land 2.md | 13 +- worlds/marioland2/items.py | 45 +- worlds/marioland2/locations.py | 541 ++++++++++++++++-- worlds/marioland2/options.py | 76 ++- worlds/marioland2/rom.py | 38 +- worlds/marioland2/rom_addresses.py | 8 + 10 files changed, 1192 insertions(+), 273 deletions(-) create mode 100644 worlds/marioland2/coin_logic.py diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index b0b34f278fe4..f95b3cd11aab 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -9,11 +9,10 @@ from . import client from .rom import generate_output, SuperMarioLand2DeltaPatch from .options import SML2Options -from .locations import locations +from .locations import locations, location_name_to_id, level_name_to_id, START_IDS, coins_coords, auto_scroll_max from .items import items from .logic import has_pipe_up, has_pipe_down, has_pipe_left, has_pipe_right, has_level_progression - -START_IDS = 7770000 +from . import coin_logic class MarioLand2Settings(settings.Group): @@ -49,7 +48,7 @@ class MarioLand2World(World): settings_key = "sml2_options" settings: MarioLand2Settings - location_name_to_id = {location_name: ID for ID, location_name in enumerate(locations, START_IDS)} + 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() @@ -58,7 +57,8 @@ class MarioLand2World(World): "Level Progression": {item_name for item_name in items if item_name.endswith("Progression") or item_name.endswith("Secret")}, "Bells": {item_name for item_name in items if "Bell" in item_name}, - "Coins": {item_name for item_name in items if "Coin" 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, 410)}}, "Powerups": {"Mushroom", "Fire Flower", "Carrot"}, "Difficulties": {"Easy Mode", "Normal Mode"} } @@ -71,6 +71,7 @@ class MarioLand2World(World): "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 @@ -78,36 +79,45 @@ class MarioLand2World(World): 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.coin_fragments_required = 0 + + def generate_early(self): + if self.options.auto_scroll_levels > -1: + eligible_levels = [0, 1, 2, 3, 5, 8, 9, 11, 13, 14, 16, 19, 20, 23, 25, 30, 31] + self.auto_scroll_levels = self.random.sample(eligible_levels, self.options.auto_scroll_levels.value) + else: + self.auto_scroll_levels = [19, 25, 30] + 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(): - if "Secret Course" in location_name: - region_name = location_name - elif "Mushroom Zone" in location_name: - region_name = "Mushroom Zone" - else: - region_name = location_name.split(" -")[0] + 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 location_name == "Tree Zone - Secret Course": + if region_name == "Tree Zone Secret Course": region_to_connect = self.multiworld.get_region("Tree Zone 2", self.player) - elif location_name == "Space Zone - Secret Course": + elif region_name == "Space Zone Secret Course": region_to_connect = self.multiworld.get_region("Space Zone 1", self.player) - elif location_name == "Macro Zone - Secret Course": + elif region_name == "Macro Zone Secret Course": region_to_connect = self.multiworld.get_region("Macro Zone 1", self.player) - elif location_name == "Pumpkin Zone - Secret Course 1": + elif region_name == "Pumpkin Zone Secret Course 1": region_to_connect = self.multiworld.get_region("Pumpkin Zone 2", self.player) - elif location_name == "Pumpkin Zone - Secret Course 2": + elif region_name == "Pumpkin Zone Secret Course 2": region_to_connect = self.multiworld.get_region("Pumpkin Zone 3", self.player) - elif location_name == "Turtle Zone - Secret Course": + elif region_name == "Turtle Zone Secret Course": region_to_connect = self.multiworld.get_region("Turtle Zone 2", self.player) - elif "-" in location_name and int(location_name.split(" ")[2]) > 1: - region_to_connect = self.multiworld.get_region(" ".join(location_name.split(" ")[:2]) - + f" {int(location_name.split(' ')[2]) - 1}", + 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 @@ -119,137 +129,161 @@ def create_regions(self): 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)) + 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 = Region("Mario's Castle", self.player, self.multiworld) menu_region.connect(castle) 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: + self.num_coin_locations = [[region, 1] for region in created_regions] + self.max_coin_locations = {region: len(coins_coords[region]) for region in created_regions} + if self.options.accessibility == "locations": + for level in self.max_coin_locations: + if level in auto_scroll_max and level_name_to_id[level] in self.auto_scroll_levels: + self.max_coin_locations[level] = min(self.max_coin_locations[level], auto_scroll_max[level]) + for i in range(self.options.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: + coin_locations += [f"{level} - " + str(int(self.max_coin_locations[level] / coins * i)) + + f" Coin{'s' if int(self.max_coin_locations[level] / coins * i) > 1 else ''}" + for i in range(1, coins + 1)] + 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_any(["Hippo Bubble", "Carrot"], self.player), - "Space Zone 1 -> Space Zone - Secret Course": lambda state: state.has("Space Zone Secret", self.player), + "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 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 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), + "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 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 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 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), - "Menu -> Mario's Castle": lambda state: ([ + } + + 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("Tree Coin", self.player), state.has("Space Coin", self.player), state.has("Macro Coin", self.player), state.has("Pumpkin Coin", self.player), state.has("Mario Coin", self.player), state.has("Turtle Coin", self.player) ].count(True) >= self.options.required_golden_coins) - } + location_rules = { - "Hippo Zone": lambda state: state.has_any(["Hippo Bubble", "Carrot", "Swim"], self.player), + "Hippo Zone - Normal or Secret Exit": lambda state: state.has_any(["Hippo Bubble", "Carrot", "Swim"], self.player), # It is possible, however tricky, to beat the Moon Stage without Carrot or Space Physics. # However, it requires somewhat precisely jumping off enemies. Enemy shuffle may make this impossible. # I have not done any testing there. Instead, I will just always make one or the other required, since # it is difficult without them anyway. - "Space Zone 1 - Moon Stage": lambda state: state.has_any(["Space Physics", "Carrot"], self.player), + "Space Zone 1 - Normal Exit": lambda state: state.has_any(["Space Physics", "Carrot"], self.player), # One or the other is actually necessary for the secret exit. - "Space Zone 1 - Moon Stage Secret Exit": lambda state: state.has_any( + "Space Zone 1 - Secret Exit": lambda state: state.has_any( ["Space Physics", "Carrot"], self.player), # Without Space Physics, you must be able to take damage once to reach the bell, and again after the bell. # If bells are not shuffled, then any one powerup will do, as you can get the bell and come back. # Otherwise, you need the bell item from the item pool, or you need to be able to take damage twice in one # visit. - "Space Zone 2 - Star Stage": lambda state: has_pipe_right(state, self.player) and (state.has( + "Space Zone 2 - Boss": lambda state: has_pipe_right(state, self.player) and (state.has( "Space Physics", self.player) or ((not state.multiworld.worlds[self.player].options.shuffle_midway_bells) and state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player)) or (state.has("Mushroom", self.player) and state.has_any(["Fire Flower", "Carrot"], self.player)) - or (state.has("Space Zone 2 - Star Stage Midway Bell", self.player) and state.has_any(["Mushroom", + or (state.has("Space Zone 2 Midway Bell", self.player) and state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player))), - "Space Zone 2 - Star Stage Midway Bell": lambda state: state.has_any( - ["Space Physics", "Space Zone 2 - Star Stage Midway Bell", "Mushroom", "Fire Flower", "Carrot"], + "Space Zone 2 - Midway Bell": lambda state: state.has_any( + ["Space Physics", "Space Zone 2 Midway Bell", "Mushroom", "Fire Flower", "Carrot"], self.player), - "Tree Zone 2 - In the Trees": lambda state: has_pipe_right(state, self.player) or state.has( - "Tree Zone 2 - In the Trees Midway Bell", self.player), - "Tree Zone 2 - In the Trees Midway Bell": lambda state: has_pipe_right(state, self.player) or state.has( - "Tree Zone 2 - In the Trees Midway Bell", self.player), - "Tree Zone 2 - In the Trees Secret Exit": lambda state: has_pipe_right(state, self.player) - and state.has("Carrot", self.player), - "Tree Zone 4 - Honeybees": lambda state: has_pipe_down(state, self.player) + "Tree Zone 2 - Normal Exit": lambda state: has_pipe_right(state, self.player) or state.has( + "Tree Zone 2 Midway Bell", self.player), + "Tree Zone 2 - Midway Bell": lambda state: has_pipe_right(state, self.player) or state.has( + "Tree Zone 2 Midway Bell", self.player), + "Tree Zone 2 - Secret Exit": lambda state: has_pipe_right(state, self.player) + and state.has("Carrot", self.player), + "Tree Zone 4 - Normal Exit": lambda state: has_pipe_down(state, self.player) and ((has_pipe_right(state, self.player) and has_pipe_up(state, self.player)) - or state.has("Tree Zone 4 - Honeybees Midway Bell", self.player)), - "Tree Zone 4 - Honeybees Midway Bell": lambda state: ((has_pipe_right(state, self.player) - and has_pipe_up(state, self.player)) or state.has("Tree Zone 4 - Honeybees Midway Bell", self.player)), - "Tree Zone 5 - The Big Bird": lambda state: has_pipe_right(state, self.player) - and (has_pipe_up(state, self.player) - or state.has("Carrot", self.player)), - "Macro Zone 1 - The Ant Monsters": lambda state: has_pipe_down(state, self.player) - or state.has("Macro Zone 1 - The Ant Monsters Midway Bell", - self.player), - "Macro Zone 1 - The Ant Monsters Midway Bell": lambda state: has_pipe_down(state, self.player) - or state.has("Macro Zone 1 - The Ant Monsters Midway Bell", - self.player), - "Macro Zone 1 - The Ant Monsters Secret Exit": lambda state: (has_pipe_down(state, self.player) - or state.has("Macro Zone 1 - The Ant Monsters Midway Bell", self.player)) + or state.has("Tree Zone 4 Midway Bell", self.player)), + "Tree Zone 4 - Midway Bell": lambda state: ((has_pipe_right(state, self.player) + and has_pipe_up(state, self.player)) or state.has("Tree Zone 4 Midway Bell", self.player)), + "Tree Zone 5 - Boss": lambda state: has_pipe_right(state, self.player) and ( + has_pipe_up(state, self.player) or state.has("Carrot", self.player)), + "Macro Zone 1 - Normal Exit": lambda state: has_pipe_down(state, self.player) + or state.has("Macro Zone 1 Midway Bell", self.player), + "Macro Zone 1 - Midway Bell": lambda state: has_pipe_down(state, self.player) + or state.has("Macro Zone 1 Midway Bell", self.player), + "Macro Zone 1 - Secret Exit": lambda state: (has_pipe_down(state, self.player) + or state.has("Macro Zone 1 Midway Bell", self.player)) and state.has("Fire Flower", self.player) and has_pipe_up(state, self.player), - "Macro Zone 2 - In the Syrup Sea": lambda state: (has_pipe_down(state, self.player) or state.has( - "Macro Zone 2 - In the Syrup Sea Midway Bell", self.player)) + "Macro Zone 2 - Normal Exit": lambda state: (has_pipe_down(state, self.player) or state.has( + "Macro Zone 2 Midway Bell", self.player)) and state.has("Swim", self.player) and has_pipe_up(state, self.player), - "Macro Zone 2 - In the Syrup Sea Midway Bell": lambda state: (has_pipe_down( + "Macro Zone 2 - Midway Bell": lambda state: (has_pipe_down( state, self.player) and state.has("Swim", self.player)) or state.has( - "Macro Zone 2 - In the Syrup Sea Midway Bell", self.player), - "Macro Zone 3 - Fiery Mario-Special Agent": lambda state: (has_pipe_down(state, self.player) - and has_pipe_down(state, self.player)) or state.has( - "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", self.player), - "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": lambda state: (has_pipe_down(state, self.player) - and has_pipe_down(state, self.player)) or state.has( - "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", self.player), - "Macro Zone 4 - One Mighty Mouse": lambda state: has_pipe_right(state, self.player), - "Pumpkin Zone 1 - Bat Course": lambda state: has_pipe_down(state, self.player) or state.has( - "Pumpkin Zone 1 - Bat Course Midway Bell", self.player), - "Pumpkin Zone 1 - Bat Course Midway Bell": lambda state: has_pipe_down(state, self.player) or state.has( - "Pumpkin Zone 1 - Bat Course Midway Bell", self.player), - "Pumpkin Zone 2 - Cyclops Course": lambda state: has_pipe_down(state, self.player) and has_pipe_up( + "Macro Zone 2 Midway Bell", self.player), + "Macro Zone 3 - Normal Exit": lambda state: (has_pipe_down(state, self.player) + and has_pipe_down(state, self.player)) or state.has("Macro Zone 3 Midway Bell", self.player), + "Macro Zone 3 - Midway Bell": lambda state: (has_pipe_down(state, self.player) + and has_pipe_down(state, self.player)) or state.has("Macro Zone 3 Midway Bell", self.player), + "Macro Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), + "Pumpkin Zone 1 - Normal Exit": lambda state: has_pipe_down(state, self.player) or state.has( + "Pumpkin Zone 1 Midway Bell", self.player), + "Pumpkin Zone 1 - Midway Bell": lambda state: has_pipe_down(state, self.player) or state.has( + "Pumpkin Zone 1 Midway Bell", self.player), + "Pumpkin Zone 2 - Normal Exit": lambda state: has_pipe_down(state, self.player) and has_pipe_up( state, self.player) and has_pipe_right(state, self.player) and state.has("Swim", self.player), # You can only spin jump as Big Mario or Fire Mario - "Pumpkin Zone 2 - Cyclops Course Secret Exit": lambda state: has_pipe_down( + "Pumpkin Zone 2 - Secret Exit": lambda state: has_pipe_down( state, self.player) and has_pipe_up(state, self.player) and has_pipe_right( state, self.player) and state.has("Swim", self.player) and state.has_any( ["Mushroom", "Fire Flower"], self.player), - "Pumpkin Zone 3 - Ghost House Secret Exit": lambda state: state.has("Carrot", self.player), - "Pumpkin Zone 4 - Witch's Mansion": lambda state: has_pipe_right(state, self.player), - "Mario Zone 1 - Fiery Blocks": lambda state: has_pipe_right(state, self.player), + "Pumpkin Zone 3 - Secret Exit": lambda state: state.has("Carrot", self.player), + "Pumpkin Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), + "Mario Zone 1 - Normal Exit": lambda state: has_pipe_right(state, self.player), # It is possible to get as small mario, but it is a very precise jump and you will die afterward. - "Mario Zone 1 - Fiery Blocks Midway Bell": lambda state: (state.has_any( + "Mario Zone 1 - Midway Bell": lambda state: (state.has_any( ["Mushroom", "Fire Flower", "Carrot"], self.player) and has_pipe_right(state, self.player)) - or state.has("Mario Zone 1 - Fiery Blocks Midway Bell", self.player), - "Mario Zone 4 - Three Mean Pigs!": lambda state: has_pipe_right(state, self.player), - "Turtle Zone 2 - Turtle Zone": lambda state: has_pipe_up(state, self.player) and has_pipe_down( + or state.has("Mario Zone 1 Midway Bell", self.player), + "Mario Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), + "Turtle Zone 2 - Normal Exit": lambda state: has_pipe_up(state, self.player) and has_pipe_down( state, self.player) and has_pipe_right(state, self.player) and has_pipe_left(state, self.player) and state.has("Swim", self.player), - "Turtle Zone 2 - Turtle Zone Midway Bell": lambda state: state.has_any( - ["Swim", "Turtle Zone 2 - Turtle Zone Midway Bell"], self.player), - "Turtle Zone 2 - Turtle Zone Secret Exit": lambda state: has_pipe_up( - state, self.player) and state.has("Swim", self.player), #state.has_any(["Swim", "Turtle Zone 2 - Turtle Zone Midway Bell"], self.player), # hard logic option? - "Turtle Zone - Secret Course": lambda state: state.has_any(["Fire Flower", "Carrot"], self.player), - "Turtle Zone 3 - Whale Course": lambda state: has_pipe_right(state, self.player), + "Turtle Zone 2 - Midway Bell": lambda state: state.has_any( + ["Swim", "Turtle Zone 2 Midway Bell"], self.player), + "Turtle Zone 2 - Secret Exit": lambda state: has_pipe_up( + state, self.player) and state.has("Swim", self.player), #state.has_any(["Swim", "Turtle Zone 2 Midway Bell"], self.player), # hard logic option? + "Turtle Zone Secret Course - Normal Exit": lambda state: state.has_any(["Fire Flower", "Carrot"], + self.player), + "Turtle Zone 3 - Boss": lambda state: has_pipe_right(state, self.player), "Mario's Castle - Wario": lambda state: has_pipe_right( state, self.player) and has_pipe_left(state, self.player) } @@ -257,29 +291,31 @@ def set_rules(self): for entrance, rule in entrance_rules.items(): self.multiworld.get_entrance(entrance, self.player).access_rule = rule - for level, rule in location_rules.items(): - if ("Midway Bell" not in level) or self.options.shuffle_midway_bells: - self.multiworld.get_location(level, self.player).access_rule = rule - + for location in self.multiworld.get_locations(self.player): + if location.name in location_rules: + location.access_rule = location_rules[location.name] + elif location.name.endswith("Coins"): + rule = getattr(coin_logic, location.parent_region.name.lower().replace(" ", "_") + "_coins", None) + if rule: + coins = int(location.name.split(" ")[-2]) + auto_scroll = level_name_to_id[location.name.split(" -")[0]] in self.auto_scroll_levels + location.access_rule = lambda state, coin_rule=rule, \ + num_coins=coins, scroll=auto_scroll: coin_rule(state, self.player, num_coins, scroll) 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": 1, - "Tree Zone Progression x2": 1, + "Tree Zone Progression": 3, "Tree Zone Secret": 1, - "Macro Zone Progression": 1, - "Macro Zone Progression x2": 1, + "Macro Zone Progression": 3, "Macro Zone Secret 1": 1, "Macro Zone Secret 2": 1, - "Pumpkin Zone Progression": 1, - "Pumpkin Zone Progression x2": 1, + "Pumpkin Zone Progression": 3, "Pumpkin Zone Secret 1": 1, "Pumpkin Zone Secret 2": 1, - "Mario Zone Progression": 1, - "Mario Zone Progression x2": 1, + "Mario Zone Progression": 3, "Turtle Zone Progression": 2, "Turtle Zone Secret": 1, "Mushroom": 1, @@ -288,20 +324,53 @@ def create_items(self): "Space Physics": 1, "Hippo Bubble": 1, "Swim": 1, - "Super Star Duration Increase": 6, + "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: - for item in self.item_name_groups["Coins"]: + 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 - Three Mean Pigs!"), - ("Tree Coin", "Tree Zone 5 - The Big Bird"), - ("Space Coin", "Space Zone 2 - Star Stage"), - ("Macro Coin", "Macro Zone 4 - One Mighty Mouse"), - ("Pumpkin Coin", "Pumpkin Zone 4 - Witch's Mansion"), - ("Turtle Coin", "Turtle Zone 3 - Whale Course") + ("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)) @@ -316,14 +385,10 @@ def create_items(self): item_counts["Normal Mode"] = 1 elif self.options.difficulty_mode == "normal_to_easy": item_counts["Easy Mode"] = 1 - else: - item_counts["Super Star Duration Increase"] += 1 if self.options.shuffle_pipe_traversal == "single": - item_counts["Super Star Duration Increase"] -= 1 item_counts["Pipe Traversal"] = 1 elif self.options.shuffle_pipe_traversal == "split": - item_counts["Super Star Duration Increase"] -= 4 item_counts["Pipe Traversal - Right"] = 1 item_counts["Pipe Traversal - Left"] = 1 item_counts["Pipe Traversal - Up"] = 1 @@ -332,23 +397,50 @@ def create_items(self): self.multiworld.push_precollected(self.create_item("Pipe Traversal")) if self.options.auto_scroll_trap: - item_counts["Super Star Duration Increase"] -= 1 item_counts["Auto Scroll"] = 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 - item_counts["Super Star Duration Increase"] += 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 + raise Exception(f"Super Mario Land 2: Too many items for locations. Player: {self.player}") + + 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 { - "mode": self.options.difficulty_mode.value, - "stars": max(len([loc for loc in self.multiworld.get_filled_locations() if loc.item.player == self.player - and loc.item.name == "Super Star Duration Increase"]), 1), - "midway_bells": self.options.shuffle_midway_bells.value, "energy_link": self.options.energy_link.value } diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index bcc736ffd979e64bd04a2391d7c9bdeab7a44a64..fd667da11a169cf7b5505037d90ca65f4cee57cc 100644 GIT binary patch literal 1155 zcmV-}1bq8KQ$$HdMl>)~000000002&0ssI200000KnMT;0000&T4*^jL0KkKS>3SQ z?EnBISa|(7f)Ef8KmY&$ga7~n0RW%?A(YS=0iaU}rhwBDGlWF*RiiSHK<-c%bR&(S z$PJfux0Y4%d|k;D;X*@p!*jGkT4*^jL0KkKS^t%Or~m*e|NsC0eR|_@_g4L=1?k2J zXhQsW^)uiV!LfZ*OhoVAeg41#Hw2kPH8haa#M2;YqeDOq27u9_pa1~S14BS)18QlY z&}7IkhM0p?^q8rqspw4`P-qPRY3cv~003wJ0000010VnoP=h7_ObLX<(9wyBfJ_2p z4GkJ%FcU)nLjnw#000vMX+)lp5D!Sm(?Dnd&^FY1znS41cK~ZWiVEBT??$KybK;WWJ0QIA*DNB=YaCG;H{Y` zK|I8FQzEMZhLGG?gk_kKk9P!$jU@(>^RxkE(n74k2okzr2_WMz5;JT`31M30EYJnT zcmkcE00OH4Ds2K^W&i*YMb6eSSh@HL$%){>wMi{QMOoz(Eis*xg*<5}w2PVM>~>)( zB(K`9h`~T@cWu{x5&)VlvSlxo3nvv=G706pJ8YiiG@D3 z@OR2msXaYu!JGU0ppkWuK!M<&;fvdrVJJ|uVyYShXC@;B(9^T+l%fB%7;czyNo@0E z13uJ5bje4yD#O#`;770Ff;CMkJeujiNod+d-+Bk>rHft{Px*Rd!7SBh3`kjJIAEG1 zD9AsKep1z{>UF2-kAh{2mok|U05QS97-a5~&7&mkqQFDf_=4RhA1VRgi5h6Nj`VST zMpoF3wGvQ8Vs@mInR$u_!rA#pQfjbgM<0zCAVum@7n}*Oknj@I3;CvZYgSAnD7+mE zSI)yq!$5!on_8v9n$q6aMMlc3=>Iiue*&E!@pmLsg$WP&SL%R5T4*^jL0KkKS-KQ{ zG5`RE|Nr@*fW&jP;01ov;>E$xfz$Rt1OQY(&;Sf<08l^>L>aIE*{u;G8UdiupfqR= z2ATjGWB>sDO$Y{}pQM)i}bx3^%IFRba^57=AcSE~LtuY)!bA@QMU-Tlu?3cZbLAj4+C&M|BPu zy=N1;GmeDLN~p-lOShnd5yC+>2z?-6vbod*w?8anHb8WCHvD|N>{^gU%=D=ivP?+% zePeeY+lj-T;Tw~1tj5p*UHk+O8@PHNh3wd_ywUa|(`_bH%kjuAbxJ?gNC7B-?PXA9 V2ugd}!Y>H_7ji{7P>{M5elk)y@1Fnw literal 723 zcmZ+_-WC^-~faSOpFW) z42BF0EK^c4CoaA${rJowr=6!b7Vpr$E%i_DNms!_g+nfEatI@?Rb8)SU{J3A|NmcX zMPuQ2p%?|$m&Y_1oEkVpg&G78E=U#eaCj-_khg)+OoEqdnSWplGv{Qr1_@pt&Mm@f zJq;PsKAz7ebj@Em*Zx@U|?$ox{TR@`Q_hNNnV^>ias1g zOx#Bt7T5+&ah}-Y!Xn9ac)=P!1varI!QByQp2}iH_s&Re(Eq|IQ0~BN#%j0uiO@bV z59U*h(j0<}hKV7pcUm3iGI6DyTgT$U6d@?U9NXel!x+RG8deu(vv4ze2&0=)PN%n0 z&w||JI>DVct3q~_riDIqonBdAdtjzn;m0MLCC&@>ALjft^Z%wz2TuOlu*~RfB3Fxm380`w1oYXPfmazA`s)ARRA^{1-ui&G z?#csK#|w=YmpQO7us8%T@Z>N!I0&#vZeZATA!QK*Fx(h`gt4O`TUYObX2AsvDZsGI zkjltNWD5?;Ff?H}!oa}O>>$NpC*;}5!N4dU)H12AU(})0cUyR*S6?H;k|j~Q-7;NR z1;yHX`;4zX=QOoCZF3l3+dNUdDqsr;o;XqFc0Weuw%{bY#84(hCI$C%JzLhk`&AjUT63%B@i}|m mK2NBN{-SusA))*I1ZkDYED;9= coins_required: + items_received.append("Mario Coin") + + if current_level == 255 and self.previous_level != 255: + if coin_mode < 2: + logger.info(f"Golden Coins required: {coins_required}") + else: + logger.info(f"Mario Coin Fragments required: {coins_required}. " + f"You have {items_received.count('Mario Coin Fragment')}") + self.previous_level = current_level + + # There is no music in the title screen demos, this is how we guard against anything in the demos registering. + # There is also no music at the door to Mario's Castle, which is why the above is before this check. + if game_loaded_check != b'\x124Vx\xff\xff\xff\xff\xff\xff' or music == 0: + return + locations_checked = [] + if current_level in level_id_to_name: + level_name = level_id_to_name[current_level] + coin_tile_data = await read(ctx.bizhawk_ctx, [(0xB000 + ((coords[1] * 256) + coords[0]), 1, "System Bus") + for coords in coins_coords[level_name]]) + num_coins = len([tile[0] for tile in coin_tile_data if tile[0] in (0x7f, 0x60, 0x07)]) + locations_checked = [location_name_to_id[f"{level_name} - {i} Coin{'s' if i > 1 else ''}"] + for i in range(1, num_coins + 1)] + + + new_lives = int(lives) + energy_link_add = None + if energy_link: + if new_lives == 0: + if (f"EnergyLink{ctx.team}" in ctx.stored_data + and ctx.stored_data[f"EnergyLink{ctx.team}"] + and ctx.stored_data[f"EnergyLink{ctx.team}"] >= BANK_EXCHANGE_RATE): + new_lives = 1 + energy_link_add = -BANK_EXCHANGE_RATE + elif new_lives > 1: + energy_link_add = BANK_EXCHANGE_RATE * (new_lives - 1) + new_lives = 1 + # Convert back to binary-coded-decimal + new_lives = int(str(new_lives), 16) + + new_coins = coins.hex() + new_coins = int(new_coins[2:] + new_coins[:2]) + for item in items_received[num_items_received:]: + if item.endswith("Coins") or item == "1 Coin": + new_coins += int(item.split(" ")[0]) + # Limit to 999 and convert back to binary-coded-decimal + new_coins = int(str(min(new_coins, 999)), 16).to_bytes(2, "little") + modified_level_data = level_data.copy() for ID, (location, data) in enumerate(locations.items(), START_IDS): if "clear_condition" in data: @@ -93,37 +150,13 @@ async def game_watcher(self, ctx: BizHawkClientContext): elif data["type"] == "bell" and data["id"] == current_level and midway_point == 0xFF: locations_checked.append(ID) - if ctx.slot_data: - total_stars = ctx.slot_data["stars"] - else: - total_stars = 5 - - invincibility_length = int((832.0 / (total_stars + 1)) + invincibility_length = int((832.0 / (star_count + 1)) * (items_received.count("Super Star Duration Increase") + 1)) if "Easy Mode" in items_received: difficulty_mode = 1 elif "Normal Mode" in items_received: difficulty_mode = 0 - elif ctx.slot_data: - difficulty_mode = ctx.slot_data["mode"] & 1 - else: - difficulty_mode = 0 - - new_lives = int(lives) - energy_link_add = None - if ctx.slot_data and ctx.slot_data["energy_link"]: - if new_lives == 0: - if (f"EnergyLink{ctx.team}" in ctx.stored_data - and ctx.stored_data[f"EnergyLink{ctx.team}"] - and ctx.stored_data[f"EnergyLink{ctx.team}"] >= BANK_EXCHANGE_RATE): - new_lives = 1 - energy_link_add = -BANK_EXCHANGE_RATE - elif new_lives > 1: - energy_link_add = BANK_EXCHANGE_RATE * (new_lives - 1) - new_lives = 1 - # convert back to binary-coded-decimal - new_lives = int(str(new_lives), 16) data_writes = [ (rom_addresses["Space_Physics"], [0x7e] if "Space Physics" in items_received else [0xaf], "ROM"), @@ -151,10 +184,14 @@ async def game_watcher(self, ctx: BizHawkClientContext): (rom_addresses["Pipe_Traversal_SFX_D"], [5] if "Pipe Traversal - Left" in items_received else [0], "ROM"), (0x022c, [new_lives], "CartRAM"), (0x02E4, [difficulty_mode], "CartRAM"), - (0x0848, modified_level_data, "CartRAM") + (0x0848, modified_level_data, "CartRAM"), + (0x0262, new_coins, "CartRAM"), ] - if midway_point == 0xFF and ctx.slot_data and ctx.slot_data["midway_bells"]: + if items_received: + data_writes.append((0x00F0, write_num_items_received, "CartRAM")) + + if midway_point == 0xFF and midway_bells: # after registering the check for the midway bell, clear the value just for safety. data_writes.append((0x02A0, [0], "CartRAM")) @@ -167,7 +204,8 @@ async def game_watcher(self, ctx: BizHawkClientContext): success = await guarded_write(ctx.bizhawk_ctx, data_writes, [(0x0848, level_data, "CartRAM"), (0x022C, [int.from_bytes(bcd_lives, "big")], - "CartRAM")]) + "CartRAM"), + [0x0262, coins, "CartRAM"]]) if success and energy_link_add is not None: await ctx.send_msgs([{ diff --git a/worlds/marioland2/coin_logic.py b/worlds/marioland2/coin_logic.py new file mode 100644 index 000000000000..e42cc9b981b5 --- /dev/null +++ b/worlds/marioland2/coin_logic.py @@ -0,0 +1,294 @@ +from .logic import has_pipe_down, has_pipe_up, has_pipe_right, has_pipe_left + + +def mushroom_zone_coins(state, player, coins, auto_scroll): + reachable_coins = 40 + if has_pipe_down(state, player): + # There's 24 in each of the underground sections. + # The first one requires missing some question mark blocks if auto scrolling (the last +4). + # If you go in the second without pipe up, you can get everything except the last 5 plus the ones in the first + # underground section. + reachable_coins += 19 + if has_pipe_up(state, player) or not auto_scroll: + reachable_coins += 5 + if has_pipe_up(state, player): + reachable_coins += 20 + if not auto_scroll: + reachable_coins += 4 + return coins <= reachable_coins + + +def tree_zone_1_coins(state, player, coins, auto_scroll): + return coins <= 87 or not auto_scroll + + +def tree_zone_2_coins(state, player, coins, auto_scroll): + reachable_coins = 18 + if has_pipe_right(state, player): + reachable_coins += 38 + if state.has("Carrot", player): + reachable_coins += 12 + if not auto_scroll: + reachable_coins += 30 + elif state.has("Tree Zone 2 Midway Bell", player): + reachable_coins = 30 + if not auto_scroll: + reachable_coins += 8 + return coins <= reachable_coins + + +def tree_zone_3_coins(state, player, coins, auto_scroll): + if coins <= 19: + return True + elif state.has_any(["Mushroom", "Fire Flower"], player) and coins <= 21: + return True + return state.has("Carrot", player) + + +def tree_zone_4_coins(state, player, coins, auto_scroll): + reachable_coins = 0 + if has_pipe_up(state, player): + reachable_coins += 14 + if has_pipe_right(state, player): + reachable_coins += 4 + if has_pipe_down(state, player): + reachable_coins += 46 + if not auto_scroll: + reachable_coins += 10 + elif state.has("Tree Zone 4 Midway Bell", player): + if not auto_scroll: + if has_pipe_left(state, player): + reachable_coins += 18 + if has_pipe_down(state, player): + reachable_coins += 46 + if has_pipe_up(state, player): + reachable_coins += 10 + elif has_pipe_down(state, player): + reachable_coins += 10 + return coins <= reachable_coins + + +def tree_zone_5_coins(state, player, coins, auto_scroll): + reachable_coins = 0 + # Not actually sure if these platforms can be randomized / can make the coin blocks unreachable from below + if ((not state.multiworld.worlds[player].options.randomize_platforms) + or state.has_any(["Mushroom", "Fire Flower"], player)): + reachable_coins += 2 + if state.has_any(["Mushroom", "Fire Flower"], player): + reachable_coins += 2 + if state.has("Carrot", player): + reachable_coins += 18 + if has_pipe_up(state, player) and not auto_scroll: + reachable_coins += 13 + elif has_pipe_up(state, player): + reachable_coins += 13 + return coins <= reachable_coins + + +def hippo_zone_coins(state, player, coins, auto_scroll): + reachable_coins = 4 + if state.has_any(["Swim", "Hippo Bubble", "Carrot"], player): + reachable_coins += 108 + if state.has_any(["Mushroom", "Fire Flower", "Hippo Bubble"], player): + reachable_coins += 6 + if state.has_all(["Fire Flower", "Swim"], player): + reachable_coins += 1 + if state.has("Hippo Bubble", player): + # Probably some of these are reachable with Carrot + reachable_coins += 52 + return coins <= reachable_coins + + +def pumpkin_zone_1_coins(state, player, coins, auto_scroll): + reachable_coins = 0 + if state.has("Pumpkin Zone 1 Midway Bell", player) or has_pipe_down(state, player): + reachable_coins += 38 + if has_pipe_up(state, player): + reachable_coins += 2 + return coins <= reachable_coins + + +def pumpkin_zone_2_coins(state, player, coins, auto_scroll): + reachable_coins = 17 + if has_pipe_down(state, player): + reachable_coins += 7 + if state.has("Swim", player): + reachable_coins += 6 + if has_pipe_up(state, player) and has_pipe_right(state, player): + reachable_coins += 1 + if state.has_any(["Mushroom", "Fire Flower"], player): + reachable_coins += 5 + return coins <= reachable_coins + + +def pumpkin_zone_secret_course_1_coins(state, player, coins, auto_scroll): + if coins <= 20: # I've gotten as high as 22 but only once. We'll be a bit forgiving. + return True + return state.has("Carrot", player) + + +def pumpkin_zone_3_coins(state, player, coins, auto_scroll): + reachable_coins = 38 + if has_pipe_up(state, player) and ((not auto_scroll) or has_pipe_down(state, player)): + reachable_coins += 12 + if has_pipe_down(state, player) and not auto_scroll: + reachable_coins += 11 + return coins <= reachable_coins + + +def pumpkin_zone_4_coins(state, player, coins, auto_scroll): + reachable_coins = 29 + if has_pipe_down(state, player): + if auto_scroll: + if has_pipe_up(state, player): + reachable_coins += 16 + else: + reachable_coins += 4 + else: + reachable_coins += 28 + # both sets of coins are down, but you need pipe up to return to go down to the next set in one playthrough + if has_pipe_up(state, player): + reachable_coins += 16 + return coins <= reachable_coins + + +def mario_zone_1_coins(state, player, coins, auto_scroll): + reachable_coins = 0 + if has_pipe_right(state, player) or (has_pipe_left(state, player) + and state.has("Mario Zone 1 Midway Bell", player)): + reachable_coins += 32 + if has_pipe_right(state, player): + reachable_coins += 8 + # coins from end section. I was able to get 13 as small mario, giving some leniency + if state.has("Carrot", player): + reachable_coins += 28 + else: + reachable_coins += 12 + if state.has("Fire Flower", player): + reachable_coins += 46 + return coins <= reachable_coins + + +def mario_zone_3_coins(state, player, coins, auto_scroll): + reachable_coins = 34 + if state.has("Fire Flower", player): + reachable_coins += 23 + return coins <= reachable_coins + + +def mario_zone_4_coins(state, player, coins, auto_scroll): + return coins <= 63 or not auto_scroll + + +def turtle_zone_1_coins(state, player, coins, auto_scroll): + reachable_coins = 37 + if auto_scroll: + reachable_coins -= 1 + if state.has("Swim", player): + reachable_coins += 16 + if state.has("Carrot", player): + reachable_coins += 24 + if auto_scroll: + reachable_coins -= 10 + return coins <= reachable_coins + + +def turtle_zone_2_coins(state, player, coins, auto_scroll): + reachable_coins = 4 + if state.has("Swim", player): + reachable_coins += 20 + elif state.has("Turtle Zone 2 Midway Bell", player): + reachable_coins += 4 + if (has_pipe_right(state, player) and has_pipe_down(state, player) + and state.has_any(["Swim", "Turtle Zone 2 Midway Bell"], player)): + reachable_coins += 1 + if has_pipe_left(state, player) and has_pipe_up(state, player): + reachable_coins += 1 + if state.has("Swim", player): + reachable_coins += 1 + return coins <= reachable_coins + + +def turtle_zone_secret_course_coins(state, player, coins, auto_scroll): + reachable_coins = 53 + if state.has("Carrot", player): + reachable_coins += 44 + elif state.has("Fire Flower", player): + reachable_coins += 36 # was able to get 38, some leniency + return coins <= reachable_coins + + +def turtle_zone_4_coins(state, player, coins, auto_scroll): + reachable_coins = 42 + if state.has("Swim", player): + reachable_coins += 17 + return coins <= reachable_coins + + +def space_zone_1_coins(state, player, coins, auto_scroll): + return (coins <= 21 or (coins <= 50 and state.has_any(["Mushroom", "Fire Flower"], player)) + or state.has_any(["Carrot", "Space Physics"], player)) + + +def space_zone_2_coins(state, player, coins, auto_scroll): + reachable_coins = 12 + if state.has_any(["Mushroom", "Fire Flower", "Carrot", "Space Physics"], player): + reachable_coins += 15 + if state.has("Space Physics", player) or not auto_scroll: + reachable_coins += 4 # last few bottom row question mark blocks that are hard to get when auto scrolling. + if (state.has("Space Physics", player) or ( + state.has("Mushroom", player) and state.has_any(["Fire Flower", "Carrot"], player))): + reachable_coins += 3 + if state.has("Space Physics", player): + reachable_coins += 79 + if not auto_scroll: + reachable_coins += 21 + return coins <= reachable_coins + + +def macro_zone_1_coins(state, player, coins, auto_scroll): + reachable_coins = 0 + if has_pipe_down(state, player): + reachable_coins += 74 + if not auto_scroll: + reachable_coins += 4 + if state.has("Fire Flower", player): + reachable_coins += 19 + elif (not auto_scroll) and state.has("Macro Zone 1 Midway Bell", player): + reachable_coins += 67 + return coins <= reachable_coins + + +def macro_zone_secret_course_coins(state, player, coins, auto_scroll): + return state.has_any(["Mushroom", "Fire Flower"], player) + + +def macro_zone_2_coins(state, player, coins, auto_scroll): + if coins <= 27: + return True + if has_pipe_up(state, player) and state.has("Swim", player): + if has_pipe_down(state, player): + return True + if state.has("Macro Zone 2 Midway Bell", player): + # Cannot return to the first section from the bell + return coins <= 42 + + +def macro_zone_3_coins(state, player, coins, auto_scroll): + if has_pipe_up(state, player) and has_pipe_down(state, player): + return True + reachable_coins = 31 + if has_pipe_up(state, player): + reachable_coins += 36 + if has_pipe_down(state, player): + reachable_coins += 18 + return coins <= reachable_coins + + +def macro_zone_4_coins(state, player, coins, auto_scroll): + reachable_coins = 61 + if auto_scroll: + reachable_coins -= 8 + if state.has("Carrot", player): + reachable_coins += 6 + return coins <= reachable_coins diff --git a/worlds/marioland2/docs/en_Super Mario Land 2.md b/worlds/marioland2/docs/en_Super Mario Land 2.md index 97115f5eafb1..797b2e1e2c2b 100644 --- a/worlds/marioland2/docs/en_Super Mario Land 2.md +++ b/worlds/marioland2/docs/en_Super Mario Land 2.md @@ -16,8 +16,7 @@ Unlocking paths to new levels requires finding or receiving Zone Progression ite "Turtle Zone Progression" will unlock the path from Turtle Zone 1 to Turtle Zone 2. Paths to secret levels are separate items, so Turtle Zone Secret will open the path from Turtle Zone 2 to the Turtle Zone Secret Course. -Mario Zone, Pumpkin Zone, Tree Zone, and Macro Zone each have one "Zone Progression x2" item that opens two paths at -once. +Depending on settings, there may be some "Zone Progression x2" item that opens two paths at once. The path from Tree Zone 2 to the branch to Tree Zone 3 and 4 is one unlock, so both levels will open at this point. @@ -30,11 +29,13 @@ Mushroom, you will drop straight down to Small Mario. - Hippo Bubble: required to use the bubbles in Hippo Zone to fly. - Space Physics: the Space Zone levels will have normal gravity until this is obtained. - Super Star Duration Increase: you begin with a drastically lowered invincibility star duration, and these items will -increase it. This is the main filler item and the number of them appearing in the item pool depends on your settings. +increase it. Additionally, the following items can be shuffled depending on your YAML settings: - The 6 Golden Coins: note that the game will still show you the coin being sent to the castle when defeating a boss regardless of whether the coin is actually obtained in that location. +- Mario Coin Fragments: As an alternative to shuffling the 6 Golden Coins, you can shuffle Mario Coin Fragments, +a chosen percentage of which are needed to assemble the Mario Coin. You will start with the other 5 coins. - Midway Bells: ringing bells results in a location check, and the midway check points are shuffled as items. Note that you may have to backtrack from the midway point to reach some secret exits! - Normal Mode/Easy Mode: you can start the game in Normal Mode with an Easy Mode "upgrade" in the item pool, or start in @@ -42,12 +43,14 @@ Easy Mode with a Normal Mode "trap" item, swapping the difficulty. - Auto Scroll: auto-scrolling levels can be set to not auto scroll until this trap item is received. - Pipe Traversal: required to enter pipes. Can also be split into 4 items, each enabling pipe entry from a different direction. +- Coins: if Coinsanity is enabled, coins will be shuffled into the item pool. A number of checks will be added to each +level for obtaining a specific number of coins within a single playthrough of the level. ## When the player receives an item, what happens? -There is no in-game indication that an item has been received. You will need to watch the client to be sure you're aware -of the items you've received. +There is no in-game indication that an item has been received. You will need to watch the client or web tracker to be +sure you're aware of the items you've received. ## Special Thanks to: diff --git a/worlds/marioland2/items.py b/worlds/marioland2/items.py index fec99c90f5a2..6a601c9a0abf 100644 --- a/worlds/marioland2/items.py +++ b/worlds/marioland2/items.py @@ -25,6 +25,7 @@ "Pumpkin Coin": ItemClassification.progression_skip_balancing, "Mario Coin": ItemClassification.progression_skip_balancing, "Turtle Coin": ItemClassification.progression_skip_balancing, + "Mario Coin Fragment": ItemClassification.progression_skip_balancing, "Mushroom": ItemClassification.progression, "Fire Flower": ItemClassification.progression, "Carrot": ItemClassification.progression, @@ -41,25 +42,27 @@ "Normal Mode": ItemClassification.trap, "Auto Scroll": ItemClassification.trap, "Mushroom Zone Midway Bell": ItemClassification.filler, - "Tree Zone 1 - Invincibility! Midway Bell": ItemClassification.filler, - "Tree Zone 2 - In the Trees Midway Bell": ItemClassification.progression_skip_balancing, - "Tree Zone 4 - Honeybees Midway Bell": ItemClassification.progression_skip_balancing, - "Tree Zone 5 - The Big Bird Midway Bell": ItemClassification.filler, - "Space Zone 1 - Moon Stage Midway Bell": ItemClassification.filler, - "Space Zone 2 - Star Stage Midway Bell": ItemClassification.progression_skip_balancing, - "Macro Zone 1 - The Ant Monsters Midway Bell": ItemClassification.progression_skip_balancing, - "Macro Zone 2 - In the Syrup Sea Midway Bell": ItemClassification.progression_skip_balancing, - "Macro Zone 3 - Fiery Mario-Special Agent Midway Bell": ItemClassification.progression_skip_balancing, - "Macro Zone 4 - One Mighty Mouse Midway Bell": ItemClassification.filler, - "Pumpkin Zone 1 - Bat Course Midway Bell": ItemClassification.progression_skip_balancing, - "Pumpkin Zone 2 - Cyclops Course Midway Bell": ItemClassification.filler, - "Pumpkin Zone 3 - Ghost House Midway Bell": ItemClassification.filler, - "Pumpkin Zone 4 - Witch's Mansion Midway Bell": ItemClassification.filler, - "Mario Zone 1 - Fiery Blocks Midway Bell": ItemClassification.progression_skip_balancing, - "Mario Zone 2 - Mario the Circus Star! Midway Bell": ItemClassification.filler, - "Mario Zone 3 - Beware: Jagged Spikes Midway Bell": ItemClassification.filler, - "Mario Zone 4 - Three Mean Pigs! Midway Bell": ItemClassification.filler, - "Turtle Zone 1 - Cheep Cheep Course Midway Bell": ItemClassification.filler, - "Turtle Zone 2 - Turtle Zone Midway Bell": ItemClassification.progression_skip_balancing, - "Turtle Zone 3 - Whale Course Midway Bell": ItemClassification.filler, + "Tree Zone 1 Midway Bell": ItemClassification.filler, + "Tree Zone 2 Midway Bell": ItemClassification.progression_skip_balancing, + "Tree Zone 4 Midway Bell": ItemClassification.progression_skip_balancing, + "Tree Zone 5 Midway Bell": ItemClassification.filler, + "Space Zone 1 Midway Bell": ItemClassification.filler, + "Space Zone 2 Midway Bell": ItemClassification.progression_skip_balancing, + "Macro Zone 1 Midway Bell": ItemClassification.progression_skip_balancing, + "Macro Zone 2 Midway Bell": ItemClassification.progression_skip_balancing, + "Macro Zone 3 Midway Bell": ItemClassification.progression_skip_balancing, + "Macro Zone 4 Midway Bell": ItemClassification.filler, + "Pumpkin Zone 1 Midway Bell": ItemClassification.progression_skip_balancing, + "Pumpkin Zone 2 Midway Bell": ItemClassification.filler, + "Pumpkin Zone 3 Midway Bell": ItemClassification.filler, + "Pumpkin Zone 4 Midway Bell": ItemClassification.filler, + "Mario Zone 1 Midway Bell": ItemClassification.progression_skip_balancing, + "Mario Zone 2 Midway Bell": ItemClassification.filler, + "Mario Zone 3 Midway Bell": ItemClassification.filler, + "Mario Zone 4 Midway Bell": ItemClassification.filler, + "Turtle Zone 1 Midway Bell": ItemClassification.filler, + "Turtle Zone 2 Midway Bell": ItemClassification.progression_skip_balancing, + "Turtle Zone 3 Midway Bell": ItemClassification.filler, + "1 Coin": ItemClassification.filler, + **{f"{i} Coins": ItemClassification.filler for i in range(2, 169)} } \ No newline at end of file diff --git a/worlds/marioland2/locations.py b/worlds/marioland2/locations.py index 19e001b111b5..96ba65d4c3d6 100644 --- a/worlds/marioland2/locations.py +++ b/worlds/marioland2/locations.py @@ -1,61 +1,482 @@ +START_IDS = 7770000 + locations = { - 'Mushroom Zone': {'id': 0x00, 'ram_index': 0, 'type': 'level'}, - 'Mushroom Zone Midway Bell': {'id': 0x00, 'ram_index': 0, 'clear_condition': ("Mushroom Zone Midway Bell", 1), 'type': 'bell'}, - 'Scenic Course': {'id': 0x19, 'ram_index': 40, 'type': 'level'}, - 'Tree Zone 1 - Invincibility!': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone Progression", 1), 'type': 'level'}, - 'Tree Zone 1 - Invincibility! Midway Bell': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone 1 - Invincibility! Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone 2 - In the Trees': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Progression", 2), 'type': 'level'}, - 'Tree Zone 2 - In the Trees Secret Exit': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Secret", 1), 'type': 'secret'}, - 'Tree Zone 2 - In the Trees Midway Bell': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone 2 - In the Trees Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone 3 - The Exit': {'id': 0x04, 'ram_index': 4, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, - 'Tree Zone 4 - Honeybees': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, - 'Tree Zone 4 - Honeybees Midway Bell': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone 4 - Honeybees Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone 5 - The Big Bird': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Coin", 1), 'type': 'level'}, - 'Tree Zone 5 - The Big Bird Midway Bell': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Zone 5 - The Big Bird Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone - Secret Course': {'id': 0x1D, 'ram_index': 36, 'type': 'level'}, - 'Hippo Zone': {'id': 0x11, 'ram_index': 31, 'type': 'level'}, - 'Space Zone 1 - Moon Stage': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Progression", 1), 'type': 'level'}, - 'Space Zone 1 - Moon Stage Secret Exit': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Secret", 1), 'type': 'secret'}, - 'Space Zone 1 - Moon Stage Midway Bell': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone 1 - Moon Stage Midway Bell", 1), 'type': 'bell'}, - 'Space Zone - Secret Course': {'id': 0x1C, 'ram_index': 41, 'type': 'level'}, - 'Space Zone 2 - Star Stage': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Coin", 1), 'type': 'level'}, - 'Space Zone 2 - Star Stage Midway Bell': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Zone 2 - Star Stage Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 1 - The Ant Monsters': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Progression", 1), 'type': 'level'}, - 'Macro Zone 1 - The Ant Monsters Secret Exit': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Secret 1", 1), 'type': 'secret'}, - 'Macro Zone 1 - The Ant Monsters Midway Bell': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone 1 - The Ant Monsters Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 2 - In the Syrup Sea': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone Progression", 2), 'type': 'level'}, - 'Macro Zone 2 - In the Syrup Sea Midway Bell': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone 2 - In the Syrup Sea Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 3 - Fiery Mario-Special Agent': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone Progression", 3), 'type': 'level'}, - 'Macro Zone 3 - Fiery Mario-Special Agent Midway Bell': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone 3 - Fiery Mario-Special Agent Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 4 - One Mighty Mouse': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Coin", 1), 'type': 'level'}, - 'Macro Zone 4 - One Mighty Mouse Midway Bell': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Zone 4 - One Mighty Mouse Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone - Secret Course': {'id': 0x1E, 'ram_index': 35, 'clear_condition': ("Macro Zone Secret 2", 1), 'type': 'level'}, - 'Pumpkin Zone 1 - Bat Course': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone Progression", 1), 'type': 'level'}, - 'Pumpkin Zone 1 - Bat Course Midway Bell': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone 1 - Bat Course Midway Bell", 1), 'type': 'bell'}, - 'Pumpkin Zone 2 - Cyclops Course': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'level'}, - 'Pumpkin Zone 2 - Cyclops Course Secret Exit': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Secret 1", 1), 'type': 'secret'}, - 'Pumpkin Zone 2 - Cyclops Course Midway Bell': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'bell'}, - 'Pumpkin Zone 3 - Ghost House': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'level'}, - 'Pumpkin Zone 3 - Ghost House Secret Exit': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Secret 2", 1), 'type': 'secret'}, - 'Pumpkin Zone 3 - Ghost House Midway Bell': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'bell'}, - "Pumpkin Zone 4 - Witch's Mansion": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'level'}, - "Pumpkin Zone 4 - Witch's Mansion Midway Bell": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'bell'}, - 'Pumpkin Zone - Secret Course 1': {'id': 0x1B, 'ram_index': 38, 'type': 'level'}, - 'Pumpkin Zone - Secret Course 2': {'id': 0x1F, 'ram_index': 39, 'type': 'level'}, - 'Mario Zone 1 - Fiery Blocks': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone Progression", 1), 'type': 'level'}, - 'Mario Zone 1 - Fiery Blocks Midway Bell': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone 1 - Fiery Blocks Midway Bell", 1), 'type': 'bell'}, - 'Mario Zone 2 - Mario the Circus Star!': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone Progression", 2), 'type': 'level'}, - 'Mario Zone 2 - Mario the Circus Star! Midway Bell': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone 2 - Mario the Circus Star! Midway Bell", 1), 'type': 'bell'}, - 'Mario Zone 3 - Beware: Jagged Spikes': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone Progression", 3), 'type': 'level'}, - 'Mario Zone 3 - Beware: Jagged Spikes Midway Bell': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone 3 - Beware: Jagged Spikes Midway Bell", 1), 'type': 'bell'}, - 'Mario Zone 4 - Three Mean Pigs!': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Coin", 1), 'type': 'level'}, - 'Mario Zone 4 - Three Mean Pigs! Midway Bell': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Zone 4 - Three Mean Pigs! Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone 1 - Cheep Cheep Course': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone Progression", 1), 'type': 'level'}, - 'Turtle Zone 1 - Cheep Cheep Course Midway Bell': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone 1 - Cheep Cheep Course Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone 2 - Turtle Zone': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Progression", 2), 'type': 'level'}, - 'Turtle Zone 2 - Turtle Zone Secret Exit': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Secret", 1), 'type': 'secret'}, - 'Turtle Zone 2 - Turtle Zone Midway Bell': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone 2 - Turtle Zone Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone 3 - Whale Course': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Coin", 1), 'type': 'level'}, - 'Turtle Zone 3 - Whale Course Midway Bell': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Zone 3 - Whale Course Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone - Secret Course': {'id': 0x1A, 'ram_index': 37, 'type': 'level'} -} \ No newline at end of file + 'Mushroom Zone - Normal Exit': {'id': 0x00, 'ram_index': 0, 'type': 'level'}, + 'Mushroom Zone - Midway Bell': {'id': 0x00, 'ram_index': 0, 'clear_condition': ("Mushroom Zone Midway Bell", 1), 'type': 'bell'}, + 'Scenic Course - Normal Exit': {'id': 0x19, 'ram_index': 40, 'type': 'level'}, + 'Tree Zone 1 - Normal Exit': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone Progression", 1), 'type': 'level'}, + 'Tree Zone 1 - Midway Bell': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone 1 Midway Bell", 1), 'type': 'bell'}, + 'Tree Zone 2 - Normal Exit': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Progression", 2), 'type': 'level'}, + 'Tree Zone 2 - Secret Exit': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Secret", 1), 'type': 'secret'}, + 'Tree Zone 2 - Midway Bell': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone 2 Midway Bell", 1), 'type': 'bell'}, + 'Tree Zone 3 - Normal Exit': {'id': 0x04, 'ram_index': 4, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, + 'Tree Zone 4 - Normal Exit': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, + 'Tree Zone 4 - Midway Bell': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone 4 Midway Bell", 1), 'type': 'bell'}, + 'Tree Zone 5 - Boss': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Coin", 1), 'type': 'level'}, + 'Tree Zone 5 - Midway Bell': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Zone 5 Midway Bell", 1), 'type': 'bell'}, + 'Tree Zone Secret Course - Normal Exit': {'id': 0x1D, 'ram_index': 36, 'type': 'level'}, + 'Hippo Zone - Normal or Secret Exit': {'id': 0x11, 'ram_index': 31, 'type': 'level'}, + 'Space Zone 1 - Normal Exit': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Progression", 1), 'type': 'level'}, + 'Space Zone 1 - Secret Exit': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Secret", 1), 'type': 'secret'}, + 'Space Zone 1 - Midway Bell': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone 1 Midway Bell", 1), 'type': 'bell'}, + 'Space Zone Secret Course - Normal Exit': {'id': 0x1C, 'ram_index': 41, 'type': 'level'}, + 'Space Zone 2 - Boss': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Coin", 1), 'type': 'level'}, + 'Space Zone 2 - Midway Bell': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Zone 2 Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 1 - Normal Exit': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Progression", 1), 'type': 'level'}, + 'Macro Zone 1 - Secret Exit': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Secret 1", 1), 'type': 'secret'}, + 'Macro Zone 1 - Midway Bell': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone 1 Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 2 - Normal Exit': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone Progression", 2), 'type': 'level'}, + 'Macro Zone 2 - Midway Bell': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone 2 Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 3 - Normal Exit': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone Progression", 3), 'type': 'level'}, + 'Macro Zone 3 - Midway Bell': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone 3 Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone 4 - Boss': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Coin", 1), 'type': 'level'}, + 'Macro Zone 4 - Midway Bell': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Zone 4 Midway Bell", 1), 'type': 'bell'}, + 'Macro Zone Secret Course - Normal Exit': {'id': 0x1E, 'ram_index': 35, 'clear_condition': ("Macro Zone Secret 2", 1), 'type': 'level'}, + 'Pumpkin Zone 1 - Normal Exit': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone Progression", 1), 'type': 'level'}, + 'Pumpkin Zone 1 - Midway Bell': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone 1 Midway Bell", 1), 'type': 'bell'}, + 'Pumpkin Zone 2 - Normal Exit': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'level'}, + 'Pumpkin Zone 2 - Secret Exit': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Secret 1", 1), 'type': 'secret'}, + 'Pumpkin Zone 2 - Midway Bell': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone 2 Midway Bell", 2), 'type': 'bell'}, + 'Pumpkin Zone 3 - Normal Exit': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'level'}, + 'Pumpkin Zone 3 - Secret Exit': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Secret 2", 1), 'type': 'secret'}, + 'Pumpkin Zone 3 - Midway Bell': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone 3 Midway Bell", 3), 'type': 'bell'}, + "Pumpkin Zone 4 - Boss": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'level'}, + "Pumpkin Zone 4 - Midway Bell": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Zone 4 Midway Bell", 1), 'type': 'bell'}, + 'Pumpkin Zone Secret Course 1 - Normal Exit': {'id': 0x1B, 'ram_index': 38, 'type': 'level'}, + 'Pumpkin Zone Secret Course 2 - Normal Exit': {'id': 0x1F, 'ram_index': 39, 'type': 'level'}, + 'Mario Zone 1 - Normal Exit': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone Progression", 1), 'type': 'level'}, + 'Mario Zone 1 - Midway Bell': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone 1 Midway Bell", 1), 'type': 'bell'}, + 'Mario Zone 2 - Normal Exit': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone Progression", 2), 'type': 'level'}, + 'Mario Zone 2 - Midway Bell': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone 2 Midway Bell", 1), 'type': 'bell'}, + 'Mario Zone 3 - Normal Exit': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone Progression", 3), 'type': 'level'}, + 'Mario Zone 3 - Midway Bell': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone 3 Midway Bell", 1), 'type': 'bell'}, + 'Mario Zone 4 - Boss': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Coin", 1), 'type': 'level'}, + 'Mario Zone 4 - Midway Bell': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Zone 4 Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone 1 - Normal Exit': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone Progression", 1), 'type': 'level'}, + 'Turtle Zone 1 - Midway Bell': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone 1 Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone 2 - Normal Exit': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Progression", 2), 'type': 'level'}, + 'Turtle Zone 2 - Secret Exit': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Secret", 1), 'type': 'secret'}, + 'Turtle Zone 2 - Midway Bell': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone 2 Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone 3 - Boss': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Coin", 1), 'type': 'level'}, + 'Turtle Zone 3 - Midway Bell': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Zone 3 Midway Bell", 1), 'type': 'bell'}, + 'Turtle Zone Secret Course - Normal Exit': {'id': 0x1A, 'ram_index': 37, 'type': 'level'} +} + + +coins_coords = { + "Mushroom Zone": + [(22, 28), (24, 28), (42, 28), (43, 28), (74, 36), (74, 37), (74, 38), (76, 36), (76, 37), + (76, 38), (78, 36), (78, 37), (78, 38), (80, 36), (80, 37), (80, 38), (82, 36), (82, 37), + (82, 38), (83, 25), (84, 25), (84, 36), (84, 37), (84, 38), (85, 25), (86, 25), (86, 36), + (86, 37), (86, 38), (87, 25), (88, 36), (88, 37), (88, 38), (116, 24), (117, 24), (118, 24), + (151, 28), (152, 28), (180, 28), (181, 24), (181, 28), (182, 24), (182, 28), (183, 24), (183, 28), + (184, 24), (184, 28), (185, 24), (185, 28), (186, 24), (186, 28), (187, 24), (187, 28), (188, 24), + (188, 28), (189, 28), (211, 25), (212, 25), (212, 36), (212, 37), (212, 38), (212, 39), (213, 25), + (213, 36), (213, 37), (213, 38), (213, 39), (214, 25), (214, 36), (214, 37), (214, 38), (214, 39), + (215, 25), (216, 25), (217, 25), (217, 36), (217, 37), (217, 38), (217, 39), (218, 25), (218, 36), + (218, 37), (218, 38), (218, 39), (219, 25), (219, 36), (219, 37), (219, 38), (219, 39), (220, 25), + (231, 24), (232, 24)], + "Tree Zone 1": + [(27, 30), (28, 30), (29, 30), (33, 27), (34, 27), (35, 27), (40, 30), (41, 30), (42, 30), (47, 27), + (48, 27), (49, 27), (56, 30), (57, 30), (58, 30), (64, 30), (65, 30), (66, 30), (88, 30), (89, 30), + (90, 30), (94, 30), (95, 30), (96, 30), (100, 30), (101, 30), (102, 30), (106, 27), (107, 27), + (108, 27), (112, 30), (113, 30), (114, 30), (119, 28), (138, 30), (139, 30), (140, 30), (150, 28), + (151, 20), (151, 28), (152, 20), (152, 26), (152, 28), (153, 26), (153, 28), (154, 26), (154, 28), + (155, 26), (155, 28), (156, 26), (156, 28), (157, 20), (157, 26), (157, 28), (158, 20), (158, 26), + (158, 28), (159, 26), (159, 28), (160, 28), (161, 28), (176, 13), (177, 13), (177, 29), (178, 13), + (178, 29), (179, 13), (179, 29), (180, 13), (181, 13), (182, 13), (183, 13), (184, 13), (185, 13), + (186, 13), (187, 13), (187, 29), (188, 13), (188, 29), (189, 13), (189, 29), (190, 13), (191, 13), + (192, 13), (193, 13), (194, 13), (195, 13), (196, 13), (197, 13), (197, 29), (198, 13), (198, 29), + (199, 13), (199, 29), (200, 13), (201, 13), (202, 13), (203, 13), (204, 13), (205, 13), (206, 13), + (207, 27), (208, 13), (208, 27), (209, 14), (209, 27), (210, 10), (210, 11), (210, 12), (210, 13), + (210, 14), (210, 15), (211, 14), (212, 13), (219, 30), (220, 30), (221, 30), (229, 27), (230, 27), + (231, 27)], + "Tree Zone 2": + [(27, 11), (28, 11), (42, 10), (43, 10), (44, 10), (51, 28), (61, 9), (65, 26), (66, 26), (67, 26), + (70, 24), (71, 24), (72, 10), (72, 24), (73, 10), (73, 24), (75, 10), (76, 10), (76, 26), (77, 26), + (78, 10), (78, 26), (79, 10), (80, 24), (81, 10), (81, 24), (82, 10), (82, 24), (83, 24), (127, 7), + (128, 7), (129, 7), (130, 7), (136, 43), (138, 9), (138, 10), (138, 11), (139, 41), (140, 41), + (141, 41), (142, 9), (142, 10), (142, 11), (144, 41), (145, 41), (146, 9), (146, 10), (146, 11), + (146, 41), (149, 41), (150, 41), (151, 41), (154, 41), (155, 41), (156, 41), (159, 41), (160, 41), + (161, 41), (164, 41), (165, 41), (166, 41), (169, 41), (170, 41), (171, 41), (174, 41), (175, 41), + (176, 41), (182, 3), (188, 42), (188, 43), (188, 44), (189, 42), (189, 43), (189, 44), (190, 42), + (190, 43), (190, 44), (191, 42), (191, 43), (191, 44), (192, 42), (192, 43), (192, 44), (193, 42), + (193, 43), (193, 44), (213, 8), (213, 9), (213, 10), (213, 11), (213, 12), (213, 13), (213, 14), + (213, 15), (213, 16), (213, 17), (213, 18), (213, 19), (213, 20), (213, 21), (213, 22), (213, 23), + (213, 24), (213, 25)], + "Tree Zone Secret Course": + [(10, 24), (11, 24), (12, 24), (17, 23), (39, 24), (40, 24), (41, 24), (42, 24), (45, 24), + (46, 24), (47, 24), (48, 24), (51, 25), (52, 25), (53, 25), (54, 25), (58, 26), (59, 26), + (60, 26), (61, 24), (61, 25), (62, 24), (62, 25), (63, 24), (63, 25), (64, 24), (64, 25), + (67, 25), (68, 26), (69, 27), (70, 27), (73, 26), (74, 27), (75, 27), (76, 27), (80, 23), + (80, 24), (81, 23), (81, 24), (82, 23), (82, 24), (83, 23), (83, 24), (87, 25), (88, 24), + (89, 24), (90, 25), (91, 26), (100, 23), (114, 27), (114, 28), (115, 27), (115, 28), (116, 27), + (116, 28), (117, 27), (117, 28), (118, 27), (118, 28), (119, 27), (119, 28), (120, 27), + (120, 28), (121, 27), (121, 28), (128, 27), (128, 28), (131, 27), (131, 28), (134, 27), + (134, 28), (137, 27), (137, 28), (138, 27), (138, 28), (143, 27), (143, 28), (159, 23)], + "Tree Zone 4": + [(22, 10), (24, 12), (26, 10), (28, 27), (29, 11), (30, 11), (31, 11), (32, 11), (33, 11), (34, 11), + (35, 11), (37, 10), (38, 12), (41, 11), (43, 12), (61, 11), (70, 11), (79, 11), (89, 11), (103, 22), + (103, 25), (103, 28), (105, 22), (105, 25), (105, 28), (107, 22), (107, 25), (107, 28), (109, 22), + (109, 25), (109, 28), (111, 22), (111, 25), (111, 28), (113, 22), (113, 25), (113, 28), (115, 22), + (115, 25), (115, 28), (117, 22), (117, 25), (117, 28), (122, 22), (122, 25), (122, 28), (124, 22), + (124, 25), (124, 28), (126, 22), (126, 25), (126, 28), (128, 22), (128, 25), (128, 28), (130, 22), + (130, 25), (130, 28), (132, 22), (132, 25), (132, 28), (134, 22), (134, 25), (134, 28), (136, 22), + (136, 25), (136, 28), (171, 10), (196, 26), (196, 29), (197, 26), (197, 29), (198, 26), (198, 29), + (199, 26), (199, 29), (200, 26), (200, 29)], + "Tree Zone 3": + [(18, 11), (18, 12), (19, 11), (19, 12), (20, 11), (20, 12), (21, 11), (21, 12), (22, 11), (22, 12), + (26, 40), (27, 11), (27, 12), (28, 11), (28, 12), (29, 11), (29, 12), (30, 11), (30, 12), (31, 11), + (31, 12), (48, 41), (49, 41), (50, 41), (51, 41), (61, 25), (77, 24)], + "Tree Zone 5": + [(23, 41), (84, 39), (85, 39), (116, 42), (123, 39), (132, 39), (134, 36), (134, 39), (134, 43), + (134, 44), (135, 43), (135, 44), (136, 36), (136, 39), (136, 43), (136, 44), (137, 43), (137, 44), + (138, 36), (138, 39), (138, 43), (138, 44), (139, 43), (139, 44), (140, 36), (140, 39), (140, 43), + (140, 44), (141, 43), (141, 44), (142, 36), (142, 39), (142, 43), (142, 44), (144, 36), (144, 39), + (146, 36), (146, 39)], + "Scenic Course": + [(24, 28), (39, 28), (54, 28), (72, 28), (87, 28), (103, 28), (117, 28)], + "Hippo Zone": + [(2, 20), (3, 3), (15, 26), (16, 26), (17, 26), (28, 4), (28, 7), (28, 10), (28, 13), (29, 4), + (29, 7), (29, 10), (29, 13), (29, 21), (30, 4), (30, 7), (30, 10), (30, 13), (32, 15), (33, 15), + (34, 15), (35, 15), (36, 15), (37, 15), (41, 12), (41, 13), (42, 11), (43, 10), (44, 10), (45, 10), + (46, 11), (47, 12), (47, 13), (48, 14), (49, 15), (50, 15), (51, 15), (52, 14), (53, 12), (53, 13), + (54, 11), (55, 10), (56, 10), (57, 10), (58, 11), (59, 12), (59, 13), (60, 14), (61, 15), (62, 15), + (63, 15), (64, 14), (65, 12), (65, 13), (66, 11), (67, 10), (68, 10), (69, 10), (70, 11), (71, 12), + (71, 13), (72, 14), (73, 15), (74, 15), (75, 15), (76, 14), (77, 12), (77, 13), (84, 11), (85, 11), + (85, 22), (86, 22), (91, 6), (92, 6), (92, 11), (93, 6), (93, 11), (94, 11), (95, 16), (96, 12), + (96, 16), (97, 8), (97, 12), (97, 16), (98, 8), (98, 12), (99, 8), (112, 6), (112, 7), (112, 12), + (112, 13), (113, 2), (113, 5), (113, 8), (113, 11), (113, 14), (113, 17), (114, 2), (114, 5), + (114, 8), (114, 11), (114, 14), (114, 17), (115, 3), (115, 4), (115, 9), (115, 10), (115, 15), + (115, 16), (124, 3), (124, 4), (124, 9), (124, 10), (124, 15), (124, 16), (125, 2), (125, 5), + (125, 8), (125, 11), (125, 14), (125, 17), (126, 2), (126, 5), (126, 8), (126, 11), (126, 14), + (126, 17), (127, 6), (127, 7), (127, 12), (127, 13), (129, 13), (130, 13), (131, 13), (132, 13), + (132, 22), (133, 13), (134, 13), (135, 13), (136, 13), (136, 21), (137, 13), (138, 13), (139, 13), + (139, 22), (140, 13), (141, 13), (142, 13), (154, 7), (155, 7), (156, 7), (157, 10), (158, 10), + (159, 10), (162, 15), (162, 16), (162, 17), (164, 15), (164, 16), (164, 17), (166, 15), (166, 16), + (166, 17), (168, 15), (168, 16), (168, 17), (170, 15), (170, 16), (170, 17), (172, 15), (172, 16), + (172, 17), (174, 15), (174, 16), (174, 17), (176, 15), (176, 16), (176, 17)], + "Space Zone 1": + [(38, 26), (45, 25), (46, 25), (47, 25), (57, 24), (58, 24), (59, 24), (60, 19), (60, 23), (61, 23), + (62, 23), (63, 23), (75, 24), (89, 16), (89, 17), (89, 18), (89, 19), (90, 16), (90, 17), (90, 18), + (90, 19), (91, 16), (91, 17), (91, 18), (91, 19), (92, 16), (92, 17), (92, 18), (92, 19), (93, 16), + (93, 17), (93, 18), (93, 19), (104, 22), (105, 22), (114, 22), (115, 22), (125, 10), (126, 9), + (127, 8), (128, 8), (129, 8), (130, 8), (131, 8), (132, 9), (133, 10), (136, 10), (137, 9), + (138, 8), (139, 8), (140, 8), (141, 8), (142, 8), (143, 9), (144, 10), (147, 10), (148, 9), + (149, 8), (150, 8), (151, 8), (152, 8), (153, 8), (154, 9), (155, 10), (155, 18), (155, 19), + (155, 20), (156, 17), (156, 18), (156, 19), (156, 20), (156, 21), (157, 16), (157, 17), (157, 18), + (157, 19), (157, 20), (157, 21), (157, 22), (158, 10), (158, 16), (158, 17), (158, 18), (158, 19), + (158, 20), (158, 21), (158, 22), (159, 9), (159, 16), (159, 17), (159, 18), (159, 19), (159, 20), + (159, 21), (159, 22), (160, 8), (160, 16), (160, 17), (160, 18), (160, 19), (160, 20), (160, 21), + (160, 22), (161, 8), (161, 16), (161, 17), (161, 18), (161, 19), (161, 20), (161, 21), (161, 22), + (162, 8), (162, 17), (162, 18), (162, 19), (162, 20), (162, 21), (163, 8), (163, 18), (163, 19), + (163, 20), (164, 8), (165, 9), (166, 10), (168, 10), (169, 9), (170, 8), (171, 8), (172, 8), + (173, 8), (174, 8), (175, 9), (176, 10)], + "Space Zone Secret Course": + [(16, 22), (16, 23), (16, 24), (18, 21), (18, 22), (18, 23), (20, 21), (20, 22), (20, 23), + (22, 20), (22, 21), (22, 22), (24, 19), (24, 20), (24, 21), (26, 18), (26, 19), (26, 20), + (28, 18), (28, 19), (28, 20), (30, 17), (30, 18), (30, 19), (36, 15), (36, 16), (36, 17), + (38, 14), (38, 15), (38, 16), (40, 13), (40, 14), (40, 15), (40, 24), (41, 24), (42, 13), + (42, 14), (42, 15), (44, 12), (44, 13), (44, 14), (46, 12), (46, 13), (46, 14), (48, 12), + (48, 13), (48, 14), (50, 11), (50, 12), (50, 13), (50, 27), (51, 27), (52, 10), (52, 11), + (52, 12), (52, 27), (53, 27), (54, 27), (58, 11), (58, 12), (58, 13), (60, 12), (60, 13), + (60, 14), (62, 12), (62, 13), (62, 14), (64, 12), (64, 13), (64, 14), (66, 13), (66, 14), + (66, 15), (68, 13), (68, 14), (68, 15), (70, 14), (70, 15), (70, 16), (72, 15), (72, 16), + (72, 17), (74, 16), (74, 17), (74, 18), (80, 18), (80, 19), (80, 20), (82, 19), (82, 20), + (82, 21), (84, 19), (84, 20), (84, 21), (86, 20), (86, 21), (86, 22), (88, 21), (88, 22), + (88, 23)], + "Space Zone 2": + [(11, 13), (12, 13), (13, 13), (20, 8), (21, 8), (22, 8), (25, 5), (26, 5), (27, 5), (33, 6), + (34, 6), (35, 6), (36, 10), (37, 10), (38, 10), (45, 7), (46, 7), (47, 7), (59, 5), (60, 5), + (61, 5), (64, 3), (93, 8), (94, 8), (95, 8), (96, 11), (97, 11), (98, 11), (100, 6), (101, 6), + (102, 6), (102, 8), (120, 5), (124, 12), (124, 13), (125, 12), (125, 13), (126, 12), (126, 13), + (127, 3), (127, 12), (127, 13), (128, 3), (128, 7), (129, 3), (129, 7), (130, 7), (148, 6), + (148, 7), (148, 8), (149, 5), (149, 6), (149, 7), (149, 8), (149, 9), (150, 5), (150, 6), (150, 7), + (150, 8), (150, 9), (151, 5), (151, 6), (151, 7), (151, 8), (151, 9), (152, 5), (152, 6), (152, 7), + (152, 8), (152, 9), (153, 6), (153, 7), (153, 8), (165, 7), (165, 8), (166, 7), (166, 8), (167, 7), + (167, 8), (168, 7), (168, 8), (169, 7), (169, 8), (170, 7), (170, 8), (171, 7), (171, 8), (181, 9), + (185, 4), (200, 3), (200, 6), (200, 9), (201, 3), (201, 6), (201, 9), (202, 3), (202, 6), (202, 9), + (203, 3), (203, 6), (203, 9), (204, 3), (204, 6), (204, 9), (205, 3), (205, 6), (205, 9), (206, 3), + (206, 6), (206, 9), (207, 3), (207, 6), (207, 9), (208, 3), (208, 6), (208, 9), (209, 3), (209, 6), + (209, 9), (210, 3), (210, 6), (210, 9), (230, 12), (231, 12), (232, 12), (236, 2), (236, 3), + (236, 4), (236, 5), (237, 2), (237, 3), (237, 4), (237, 5), (238, 2), (238, 3), (238, 4), (238, 5), + (248, 10)], + "Turtle Zone 1": + [(22, 34), (27, 37), (28, 37), (29, 37), (30, 37), (31, 37), (32, 37), (33, 37), (34, 37), + (35, 37), (36, 37), (46, 32), (46, 33), (47, 32), (47, 33), (50, 32), (50, 33), (51, 32), + (51, 33), (54, 32), (54, 33), (55, 32), (55, 33), (56, 33), (57, 33), (58, 32), (58, 33), + (59, 32), (59, 33), (62, 32), (62, 33), (63, 32), (63, 33), (66, 32), (66, 33), (67, 32), + (67, 33), (73, 43), (74, 43), (75, 43), (77, 41), (78, 41), (79, 41), (81, 40), (82, 40), + (83, 40), (85, 41), (86, 41), (87, 41), (122, 36), (123, 36), (124, 36), (125, 36), (126, 36), + (127, 36), (130, 36), (131, 36), (132, 36), (133, 36), (134, 36), (135, 36), (136, 36), (137, 36), + (138, 36), (139, 36), (140, 36), (141, 36), (143, 34), (163, 36), (164, 36), (166, 36), (167, 36), + (169, 36), (170, 36), (180, 37), (181, 37), (182, 37), (183, 37), (184, 37), (185, 37), (188, 44), + (189, 44)], + "Turtle Zone 2": + [(6, 34), (11, 34), (15, 43), (48, 36), (51, 28), (56, 35), (57, 35), (59, 42), (61, 20), (62, 20), + (62, 35), (63, 20), (63, 35), (64, 20), (65, 20), (67, 35), (68, 35), (72, 39), (79, 34), + (82, 35), (87, 42), (96, 43), (105, 43), (107, 43), (109, 43), (118, 28), (121, 28), (139, 39), + (142, 39)], + "Turtle Zone Secret Course": + [(19, 27), (39, 27), (39, 28), (40, 26), (40, 27), (41, 25), (41, 27), (42, 25), (42, 27), + (43, 26), (43, 27), (44, 27), (44, 28), (48, 25), (48, 26), (48, 27), (48, 28), (49, 25), + (49, 27), (50, 25), (50, 27), (51, 25), (51, 27), (52, 26), (52, 28), (53, 27), (61, 25), + (61, 28), (62, 25), (62, 26), (62, 27), (62, 28), (63, 25), (63, 28), (64, 26), (73, 26), + (73, 27), (74, 25), (74, 28), (75, 25), (75, 28), (76, 25), (76, 28), (77, 26), (77, 27), + (82, 25), (82, 26), (82, 27), (82, 28), (83, 28), (84, 28), (85, 28), (87, 27), (89, 27), + (89, 28), (90, 26), (90, 27), (91, 25), (91, 27), (92, 25), (92, 27), (93, 26), (93, 27), + (94, 27), (94, 28), (98, 24), (98, 25), (98, 26), (98, 27), (99, 25), (100, 26), (101, 27), + (102, 24), (102, 25), (102, 26), (102, 27), (108, 24), (108, 25), (108, 26), (108, 27), + (109, 24), (109, 27), (110, 24), (110, 27), (111, 24), (111, 27), (112, 25), (112, 26), + (116, 24), (116, 27), (117, 23), (117, 26), (117, 27), (118, 23), (118, 25), (118, 27), + (119, 23), (119, 25), (119, 27), (120, 24), (120, 27), (121, 28), (122, 28), (123, 28)], + "Turtle Zone 3": + [(16, 25), (17, 25), (18, 25), (19, 25), (20, 25), (21, 25), (22, 25), (23, 25), (24, 25), + (35, 24), (36, 24), (37, 24), (38, 24), (39, 24), (40, 24), (41, 24), (42, 24), (43, 24), + (75, 28), (75, 29), (76, 28), (76, 29), (81, 28), (81, 29), (82, 28), (82, 29), (92, 26), + (93, 26), (94, 26), (98, 26), (99, 26), (100, 26), (123, 26), (124, 26), (126, 26), (127, 26), + (129, 26), (130, 26), (146, 22), (146, 29), (147, 22), (147, 29), (148, 22), (150, 22), (151, 22), + (152, 23), (152, 29), (153, 29), (154, 22), (155, 22), (156, 22), (158, 29), (159, 29), (161, 22), + (162, 22), (163, 22), (165, 22), (166, 22), (167, 22), (169, 22), (170, 22), (171, 22)], + "Mario Zone 1": + [(18, 44), (47, 36), (47, 37), (47, 38), (47, 39), (49, 36), (49, 37), (49, 38), (50, 36), (50, 37), + (50, 38), (52, 36), (52, 37), (52, 38), (53, 36), (53, 37), (53, 38), (60, 35), (60, 36), (60, 37), + (61, 35), (61, 36), (61, 37), (64, 35), (64, 36), (64, 37), (65, 35), (65, 36), (65, 37), (71, 36), + (78, 36), (78, 37), (78, 38), (98, 38), (145, 42), (146, 22), (146, 23), (146, 25), (146, 26), + (146, 42), (147, 22), (147, 23), (147, 25), (147, 26), (147, 30), (147, 31), (147, 32), (147, 42), + (148, 22), (148, 23), (148, 25), (148, 30), (148, 31), (148, 32), (148, 42), (149, 21), (149, 22), + (149, 23), (149, 24), (149, 30), (149, 31), (149, 42), (150, 21), (150, 22), (150, 23), (150, 24), + (150, 26), (150, 27), (150, 28), (150, 30), (150, 31), (150, 42), (151, 27), (151, 28), (151, 30), + (151, 31), (151, 42), (152, 27), (152, 29), (152, 30), (152, 31), (152, 42), (153, 27), (153, 29), + (153, 30), (153, 42), (154, 27), (154, 29), (154, 30), (164, 20), (167, 21), (167, 26), (167, 34), + (168, 21), (168, 25), (168, 27), (168, 33), (168, 35), (169, 20), (169, 24), (169, 28), (169, 33), + (169, 35), (170, 20), (170, 23), (170, 29), (170, 32), (170, 35), (171, 20), (171, 23), (171, 29), + (171, 32), (171, 36), (171, 37), (172, 21), (172, 22), (172, 30), (172, 31)], + "Mario Zone 2": + [(25, 24), (25, 27), (26, 24), (26, 27), (27, 24), (27, 27), (81, 27), (112, 24), (113, 24), + (114, 24), (115, 24), (116, 24), (117, 24), (118, 24), (121, 24), (122, 24), (123, 24), (124, 24), + (125, 24), (126, 24), (127, 24), (138, 26), (139, 26), (140, 24), (140, 28), (141, 24), (141, 28), + (144, 26), (145, 26), (146, 24), (146, 28), (147, 28), (151, 26), (152, 24), (152, 28), (153, 24), + (153, 28), (156, 26), (157, 26), (158, 24), (158, 28), (159, 24), (159, 28), (162, 26), (163, 26), + (164, 28), (165, 28)], + "Mario Zone 3": + [(8, 28), (11, 28), (14, 28), (17, 28), (20, 28), (23, 28), (54, 25), (100, 27), (109, 18), + (109, 19), (110, 18), (110, 19), (111, 17), (111, 18), (111, 19), (112, 17), (112, 18), (112, 19), + (127, 17), (127, 18), (127, 19), (128, 17), (128, 18), (128, 19), (129, 18), (129, 19), (130, 18), + (130, 19), (130, 20), (131, 18), (131, 19), (131, 20), (132, 18), (132, 19), (132, 20), (133, 18), + (133, 19), (133, 20), (133, 27), (134, 18), (134, 19), (134, 20), (157, 28), (158, 28), (159, 28), + (160, 28), (161, 28), (168, 28), (169, 28), (170, 28), (171, 28), (172, 28), (189, 44), (199, 28), + (200, 28), (201, 28), (202, 28), (203, 28), (214, 27), (217, 27), (220, 27), (223, 27)], + "Mario Zone 4": + [(20, 25), (114, 24), (114, 25), (114, 26), (115, 24), (115, 25), (115, 26), (115, 27), (115, 28), + (115, 29), (116, 24), (116, 25), (116, 26), (116, 27), (116, 29), (117, 24), (117, 25), (117, 26), + (117, 27), (117, 28), (117, 29), (118, 24), (118, 25), (118, 26), (118, 27), (118, 28), (118, 29), + (119, 24), (119, 25), (119, 27), (119, 28), (119, 29), (120, 24), (120, 25), (120, 26), (120, 27), + (120, 28), (120, 29), (121, 24), (121, 25), (121, 26), (121, 27), (121, 28), (121, 29), (122, 12), + (122, 24), (122, 25), (122, 26), (122, 27), (122, 29), (123, 12), (123, 24), (123, 25), (123, 26), + (123, 27), (123, 28), (123, 29), (124, 12), (124, 24), (124, 25), (124, 26), (124, 27), (124, 28), + (124, 29), (125, 12), (179, 12)], + "Pumpkin Zone 1": + [(23, 12), (55, 24), (55, 26), (56, 27), (57, 24), (57, 26), (63, 24), (63, 26), (64, 27), + (65, 24), (65, 26), (71, 24), (71, 26), (72, 27), (73, 24), (73, 26), (79, 24), (79, 26), + (80, 27), (81, 24), (81, 26), (86, 25), (86, 27), (92, 4), (93, 27), (95, 25), (95, 27), (98, 4), + (102, 26), (102, 28), (104, 4), (104, 26), (104, 28), (165, 14), (166, 15), (171, 20), (172, 21), + (173, 22), (175, 24), (176, 25), (177, 26), (179, 28), (180, 29), (189, 6)], + "Pumpkin Zone 2": + [(34, 26), (40, 21), (41, 21), (42, 21), (43, 21), (48, 20), (49, 20), (50, 20), (50, 41), + (51, 20), (51, 41), (52, 41), (53, 41), (54, 41), (56, 21), (57, 21), (58, 21), (59, 21), + (61, 41), (62, 41), (64, 20), (65, 20), (66, 20), (67, 20), (114, 36), (115, 35), (115, 36), + (116, 34), (116, 35), (116, 36), (132, 20), (132, 21), (132, 22), (132, 23), (132, 24), + (144, 23), (169, 27)], + "Pumpkin Zone 3": + [(18, 26), (20, 26), (22, 26), (24, 26), (26, 26), (31, 18), (32, 18), (33, 18), (34, 18), + (35, 18), (36, 18), (37, 18), (38, 18), (39, 18), (40, 18), (41, 18), (42, 18), (48, 24), + (52, 20), (52, 24), (56, 24), (87, 27), (88, 27), (89, 27), (90, 27), (94, 27), (95, 27), + (96, 27), (97, 27), (101, 27), (102, 27), (103, 27), (104, 27), (104, 42), (108, 27), (109, 27), + (110, 27), (111, 27), (115, 27), (116, 27), (117, 27), (118, 27), (134, 35), (134, 41), + (135, 35), (135, 41), (136, 35), (136, 41), (137, 35), (137, 41), (138, 35), (138, 41), + (139, 35), (139, 41), (140, 41), (225, 38), (226, 37), (227, 36), (227, 37), (227, 38), + (227, 39), (227, 40), (227, 41), (228, 37), (229, 38)], + "Pumpkin Zone Secret Course 1": + [(14, 15), (16, 9), (16, 10), (16, 11), (16, 12), (16, 13), (16, 14), (16, 15), (16, 16), + (17, 9), (17, 10), (17, 11), (17, 12), (17, 13), (17, 14), (17, 15), (17, 16), (18, 9), + (18, 10), (18, 11), (18, 12), (18, 13), (18, 14), (18, 15), (18, 16), (19, 9), (19, 10), + (19, 11), (19, 12), (19, 13), (19, 14), (19, 15), (19, 16), (20, 9), (20, 10), (20, 11), + (20, 12), (20, 13), (20, 14), (20, 15), (20, 16), (21, 9), (21, 10), (21, 11), (21, 12), + (21, 13), (21, 14), (21, 15), (21, 16), (22, 9), (22, 10), (22, 11), (22, 12), (22, 13), + (22, 14), (22, 15), (22, 16), (23, 9), (23, 10), (23, 11), (23, 12), (23, 13), (23, 14), + (23, 15), (23, 16), (24, 9), (24, 10), (24, 11), (24, 12), (24, 13), (24, 14), (24, 15), + (24, 16), (25, 9), (25, 10), (25, 11), (25, 12), (25, 13), (25, 14), (25, 15), (25, 16), + (26, 9), (26, 10), (26, 11), (26, 12), (26, 13), (26, 14), (26, 15), (27, 16), (28, 9), + (28, 10), (28, 11), (28, 12), (28, 13), (28, 14), (28, 15), (28, 16), (29, 9), (29, 10), + (29, 11), (29, 12), (29, 13), (29, 14), (29, 15), (29, 16), (30, 9), (30, 10), (30, 11), + (30, 12), (30, 13), (30, 14), (30, 15), (30, 16), (31, 9), (31, 10), (31, 11), (31, 12), + (31, 13), (31, 14), (31, 15), (31, 16), (32, 9), (32, 10), (32, 11), (32, 12), (32, 13), + (32, 14), (32, 15), (32, 16), (33, 9), (33, 10), (33, 11), (33, 12), (33, 13), (33, 14), + (33, 15), (33, 16), (34, 9), (34, 10), (34, 11), (34, 12), (34, 13), (34, 14), (34, 15), + (34, 16), (35, 9), (35, 10), (35, 11), (35, 12), (35, 13), (35, 14), (35, 15), (35, 16), + (36, 9), (36, 10), (36, 11), (36, 12), (36, 13), (36, 14), (36, 15), (36, 16), (37, 9), + (37, 10), (37, 11), (37, 12), (37, 13), (37, 14), (37, 15), (37, 16), (39, 16), (40, 9), + (40, 10), (40, 11), (40, 12), (40, 13), (40, 14), (40, 15), (40, 16), (41, 9), (41, 10), + (41, 11), (41, 12), (41, 13), (41, 14), (41, 15), (41, 16), (42, 9), (42, 10), (42, 11), + (42, 12), (42, 13), (42, 14), (42, 15), (42, 16), (43, 9), (43, 10), (43, 11), (43, 12), + (43, 13), (43, 14), (43, 15), (43, 16), (44, 9), (44, 10), (44, 11), (44, 12), (44, 13), + (44, 14), (44, 15), (44, 16), (45, 9), (45, 10), (45, 11), (45, 12), (45, 13), (45, 14), + (45, 15), (45, 16), (46, 9), (46, 10), (46, 11), (46, 12), (46, 13), (46, 14), (46, 15), + (46, 16), (47, 9), (47, 10), (47, 11), (47, 12), (47, 13), (47, 14), (47, 15), (47, 16), + (48, 9), (48, 10), (48, 11), (48, 12), (48, 13), (48, 14), (48, 15), (48, 16), (49, 9), + (49, 10), (49, 11), (49, 12), (49, 13), (49, 14), (49, 15), (49, 16), (52, 9), (52, 10), + (52, 11), (52, 12), (52, 13), (52, 14), (52, 15), (52, 16), (53, 9), (53, 10), (53, 11), + (53, 12), (53, 13), (53, 14), (53, 15), (53, 16), (54, 9), (54, 10), (54, 11), (54, 12), + (54, 13), (54, 14), (54, 15), (54, 16), (55, 9), (55, 10), (55, 11), (55, 12), (55, 13), + (55, 14), (55, 15), (55, 16), (56, 9), (56, 10), (56, 11), (56, 12), (56, 13), (56, 14), + (56, 15), (56, 16), (57, 9), (57, 10), (57, 11), (57, 12), (57, 13), (57, 14), (57, 15), + (57, 16), (58, 9), (58, 10), (58, 11), (58, 12), (58, 13), (58, 14), (58, 15), (58, 16), + (59, 9), (59, 10), (59, 11), (59, 12), (59, 13), (59, 14), (59, 15), (59, 16), (60, 9), + (60, 10), (60, 11), (60, 12), (60, 13), (60, 14), (60, 15), (60, 16), (61, 9), (61, 10), + (61, 11), (61, 12), (61, 13), (61, 14), (61, 15), (61, 16), (64, 9), (64, 10), (64, 11), + (64, 12), (64, 13), (64, 14), (64, 15), (64, 16), (65, 9), (65, 10), (65, 11), (65, 12), + (65, 13), (65, 14), (65, 15), (65, 16), (66, 9), (66, 10), (66, 11), (66, 12), (66, 13), + (66, 14), (66, 15), (66, 16), (67, 9), (67, 10), (67, 11), (67, 12), (67, 13), (67, 14), + (67, 15), (67, 16), (68, 9), (68, 10), (68, 11), (68, 12), (68, 13), (68, 14), (68, 15), + (68, 16), (69, 9), (69, 10), (69, 11), (69, 12), (69, 13), (69, 14), (69, 15), (69, 16), + (70, 9), (70, 10), (70, 11), (70, 12), (70, 13), (70, 14), (70, 15), (70, 16), (71, 9), + (71, 10), (71, 11), (71, 12), (71, 13), (71, 14), (71, 15), (71, 16), (72, 9), (72, 10), + (72, 11), (72, 12), (72, 13), (72, 14), (72, 15), (72, 16), (73, 9), (73, 10), (73, 11), + (73, 12), (73, 13), (73, 14), (73, 15), (73, 16)], + "Pumpkin Zone Secret Course 2": + [(12, 6), (72, 7), (73, 7), (80, 7), (81, 7), (85, 8), (90, 10), (91, 10), (94, 10), + (95, 10), (98, 9), (99, 9), (102, 9), (103, 9)], + "Pumpkin Zone 4": + [(18, 28), (19, 28), (20, 28), (21, 28), (83, 37), (83, 38), (84, 37), (84, 38), (85, 37), + (85, 38), (85, 39), (86, 37), (87, 37), (87, 40), (88, 37), (88, 38), (88, 39), (88, 40), + (89, 37), (89, 38), (89, 39), (89, 40), (90, 37), (90, 38), (91, 37), (91, 38), (92, 37), + (92, 38), (92, 39), (92, 40), (93, 23), (93, 37), (93, 40), (94, 23), (94, 37), (94, 40), + (103, 23), (104, 23), (113, 23), (114, 23), (169, 30), (170, 28), (170, 30), (171, 26), + (171, 28), (171, 30), (172, 24), (172, 26), (172, 28), (172, 30), (173, 24), (173, 26), + (173, 28), (173, 30), (174, 26), (174, 28), (174, 30), (175, 28), (175, 30), (176, 30), + (198, 37), (198, 38), (199, 37), (199, 38), (204, 37), (204, 38), (205, 37), (205, 38), + (210, 37), (210, 38), (211, 37), (211, 38), (216, 37), (216, 38), (217, 37), (217, 38)], + "Macro Zone 1": + [(22, 32), (22, 33), (22, 34), (23, 20), (23, 21), (23, 22), (23, 38), (23, 39), (23, 40), (24, 26), + (24, 27), (24, 28), (39, 42), (40, 41), (41, 40), (42, 39), (49, 43), (62, 42), (62, 43), (62, 44), + (68, 42), (68, 43), (68, 44), (75, 42), (75, 43), (75, 44), (84, 40), (84, 41), (84, 42), (87, 39), + (89, 42), (89, 43), (89, 44), (107, 42), (108, 42), (109, 42), (118, 42), (119, 42), (120, 42), + (121, 42), (122, 42), (128, 42), (128, 43), (130, 42), (130, 43), (134, 42), (134, 43), (140, 42), + (141, 42), (142, 42), (143, 42), (144, 42), (154, 42), (155, 42), (156, 42), (163, 22), (163, 23), + (164, 21), (164, 22), (164, 23), (165, 22), (165, 23), (166, 21), (166, 22), (166, 23), (167, 22), + (167, 23), (168, 21), (168, 22), (168, 23), (169, 22), (169, 23), (170, 21), (170, 22), (170, 23), + (171, 22), (171, 23), (177, 40), (182, 40), (189, 41), (189, 42), (189, 43), (189, 44), (189, 45), + (205, 46), (206, 46), (208, 37), (209, 37), (212, 41), (213, 41), (214, 41), (215, 41), (216, 41), + (220, 37), (221, 37), (224, 46), (225, 46), (234, 43), (246, 38), (246, 39), (246, 40), (246, 41), + (246, 42), (246, 43)], + "Macro Zone 2": + [(18, 28), (19, 27), (22, 28), (23, 27), (25, 26), (27, 27), (28, 27), (31, 27), (32, 27), (41, 29), + (42, 29), (43, 29), (55, 29), (57, 29), (60, 29), (62, 29), (69, 27), (70, 27), (71, 27), (74, 27), + (75, 27), (76, 27), (79, 27), (80, 27), (81, 27), (84, 27), (85, 27), (86, 27), (99, 40), + (137, 37), (180, 40), (181, 8), (182, 8), (183, 8), (184, 8), (185, 8), (186, 8), (187, 8), + (188, 8), (189, 8), (190, 8), (191, 8), (192, 8), (193, 8), (194, 8), (195, 8), (196, 8), (197, 8), + (198, 8), (199, 8), (200, 8), (201, 8), (202, 8), (204, 8), (205, 8), (206, 8), (207, 8), (208, 8), + (209, 8), (210, 8), (211, 8), (212, 8), (213, 8), (215, 8), (216, 8), (217, 8), (217, 11), + (218, 8), (218, 9), (218, 10), (218, 11), (218, 12), (219, 11)], + "Macro Zone 3": + [(24, 23), (37, 28), (38, 28), (39, 28), (40, 28), (57, 30), (58, 30), (59, 30), (65, 17), (65, 18), + (65, 19), (66, 17), (66, 18), (66, 19), (67, 17), (67, 18), (67, 19), (68, 17), (68, 18), (68, 19), + (69, 17), (69, 18), (69, 19), (70, 17), (70, 18), (70, 19), (73, 17), (73, 18), (73, 19), (74, 17), + (74, 18), (74, 19), (75, 17), (75, 18), (75, 19), (76, 17), (76, 18), (76, 19), (77, 17), (77, 18), + (77, 19), (78, 17), (78, 18), (78, 19), (85, 43), (95, 17), (95, 18), (95, 19), (95, 20), (96, 17), + (96, 18), (96, 19), (96, 20), (97, 17), (97, 18), (97, 19), (97, 20), (98, 17), (98, 18), (98, 19), + (98, 20), (100, 17), (100, 18), (100, 19), (100, 20), (101, 17), (101, 18), (101, 19), (101, 20), + (103, 42), (104, 42), (105, 42), (106, 42), (107, 42), (116, 42), (117, 42), (118, 42), (119, 42), + (120, 42), (121, 42), (122, 42), (129, 19), (131, 42), (132, 42), (133, 42), (134, 42), (135, 42), + (136, 42), (165, 21), (165, 22), (166, 21), (166, 22), (171, 21), (171, 22), (172, 21), (172, 22), + (176, 20), (176, 21), (176, 22), (179, 20), (179, 21), (179, 22), (183, 21), (183, 22), (184, 21), + (184, 22), (194, 27), (194, 28), (197, 27), (197, 28), (200, 27), (200, 28), (203, 25), (203, 27), + (203, 28), (205, 18), (205, 19), (206, 18), (206, 19)], + "Macro Zone 4": + [(16, 28), (34, 28), (39, 28), (39, 29), (39, 30), (40, 28), (40, 29), (40, 30), (41, 28), (41, 29), + (41, 30), (62, 29), (63, 29), (64, 29), (65, 29), (66, 29), (67, 29), (68, 29), (69, 29), (81, 17), + (81, 18), (82, 17), (82, 18), (83, 17), (83, 18), (84, 17), (84, 18), (85, 17), (85, 18), (86, 17), + (86, 18), (87, 17), (87, 18), (87, 28), (88, 17), (88, 18), (114, 28), (144, 22), (146, 27), + (146, 28), (147, 27), (147, 28), (148, 27), (148, 28), (149, 27), (149, 28), (150, 27), (150, 28), + (151, 27), (151, 28), (152, 27), (152, 28), (153, 27), (153, 28), (154, 27), (154, 28), (155, 27), + (155, 28), (156, 27), (156, 28), (157, 27), (157, 28), (158, 27), (158, 28)], + "Macro Zone Secret Course": + [(21, 24), (70, 27), (70, 28), (71, 27), (71, 28), (72, 27), (72, 28), (73, 27), (73, 28), + (74, 27), (74, 28), (75, 27), (75, 28), (76, 24), (76, 25), (76, 26), (76, 27), (76, 28), + (77, 24), (77, 25), (77, 26), (77, 27), (77, 28), (78, 24), (78, 25), (78, 26), (78, 27), + (78, 28), (79, 24), (79, 25), (79, 26), (79, 27), (80, 25), (80, 26), (81, 25), (81, 26), + (88, 25), (108, 30)], + "Mario's Castle": + [(7, 25), (160, 44), (167, 28), (247, 26)], +} +powerup_coords = { + "Mushroom Zone": [(42, 28), (151, 28), (152, 28), (188, 28)], + "Scenic Course": [(39, 28), (72, 28), (117, 28)], + "Tree Zone 1": [(119, 28), (152, 28)], + "Tree Zone 2": [(43, 10), (61, 9), (51, 28), (130, 7), (182, 3), (136, 43)], + "Tree Zone Secret Course": [(17, 23), (100, 23), (159, 23)], + "Tree Zone 3": [(26, 40), (77, 24)], + "Tree Zone 4": [(28, 27), (105, 25), (136, 22), (171, 10)], + "Tree Zone 5": [(123, 39), (138, 39), (146, 36)], + "Pumpkin Zone 1": [(23, 12), (72, 27), (98, 4), (189, 6)], + "Pumpkin Zone 2": [(144, 23)], + "Pumpkin Zone Secret Course 1": [(14, 15)], + "Pumpkin Zone 3": [(52, 20), (104, 42), (139, 35), (140, 41)], + "Pumpkin Zone Secret Course 2": [(12, 6), (85, 8)], + "Pumpkin Zone 4": [(83, 38), (94, 40), (104, 23)], + "Mario Zone 1": [(18, 44), (98, 38), (145, 42), (164, 20)], + "Mario Zone 2": [(81, 27)], + "Mario Zone 3": [(54, 25), (100, 27), (134, 18), (189, 44), (214, 27)], + "Mario Zone 4": [(20, 25), (124, 12), (179, 12)], + "Turtle Zone 1": [(22, 34), (56, 33), (57, 33), (143, 34), (189, 44)], + "Turtle Zone 2": [(82, 35), (139, 39), ], + "Turtle Zone Secret Course": [(19, 27), (53, 27), (64, 26), (87, 27), (121, 28), (122, 28), (123, 28)], + "Turtle Zone 3": [(39, 24), (94, 26), (152, 23)], + "Hippo Zone": [(3, 3), (2, 20), (15, 26), (16, 26), (29, 21), (86, 22), (137, 13)], + "Space Zone 1": [(75, 24), (114, 22)], + # "Space Zone Secret Course": [], + "Space Zone 2": [(64, 3), (102, 8), (120, 5), (207, 6), (210, 9), (248, 10)], + "Macro Zone 1": [(49, 43), (87, 39), (177, 40), (164, 21), (166, 21), (170, 21), (234, 43)], + "Macro Zone 2": [(25, 26), (99, 40), (137, 37), (180, 40)], + "Macro Zone 3": [(24, 23), (85, 43), (129, 19), (203, 25)], + "Macro Zone 4": [(16, 28), (87, 28), (144, 22)], + "Macro Zone Secret Course": [(21, 24), (88, 25), (108, 30)], + "Mario's Castle": [(7, 25), (160, 44), (167, 28), (247, 26)] +} +for zone, coords_list in powerup_coords.items(): + for coords in coords_list: + coins_coords[zone].remove(coords) + +location_name_to_id = {location_name: ID for ID, location_name in enumerate(locations, START_IDS)} +loc_id = START_IDS + len(locations) +for level, coin_coords in coins_coords.items(): + for i in range(1, len(coin_coords) + 1): + location_name_to_id[f"{level} - {i} Coin{'s' if i > 1 else ''}"] = loc_id + loc_id += 1 + +level_id_to_name = { + 0: 'Mushroom Zone', 25: 'Scenic Course', 1: 'Tree Zone 1', 2: 'Tree Zone 2', 4: 'Tree Zone 3', 3: 'Tree Zone 4', + 5: 'Tree Zone 5', 29: 'Tree Zone Secret Course', 17: 'Hippo Zone', 18: 'Space Zone 1', + 28: 'Space Zone Secret Course', 19: 'Space Zone 2', 20: 'Macro Zone 1', 21: 'Macro Zone 2', 22: 'Macro Zone 3', + 23: 'Macro Zone 4', 30: 'Macro Zone Secret Course', 6: 'Pumpkin Zone 1', 7: 'Pumpkin Zone 2', + 8: 'Pumpkin Zone 3', 9: 'Pumpkin Zone 4', 27: 'Pumpkin Zone Secret Course 1', 31: 'Pumpkin Zone Secret Course 2', + 10: 'Mario Zone 1', 11: 'Mario Zone 2', 12: 'Mario Zone 3', 13: 'Mario Zone 4', 14: 'Turtle Zone 1', + 15: 'Turtle Zone 2', 16: 'Turtle Zone 3', 26: 'Turtle Zone Secret Course', 24: "Mario's Castle" +} + +level_name_to_id = {name: level_id for level_id, name in level_id_to_name.items()} + +auto_scroll_max = { + "Mushroom Zone": 84, + "Tree Zone 1": 87, + "Tree Zone 2": 68, + "Tree Zone 4": 64, + "Tree Zone 5": 22, + "Space Zone 2": 113, + "Macro Zone 1": 74, + "Macro Zone 4": 59, + "Pumpkin Zone 3": 50, + "Pumpkin Zone 4": 45, + "Turtle Zone 1": 66, +} diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 35043c09f8b6..016f33dbb1fb 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -2,21 +2,69 @@ from dataclasses import dataclass -class GoldenCoins(Toggle): - """Shuffle the Golden Coins into the item pool. +class ShuffleGoldenCoins(Choice): + """Vanilla: Golden Coins are not in the item pool, you receive them when defeating bosses as in vanilla. + Shuffle: Shuffle the Golden Coins into the item pool and open location checks on bosses. + Mario Coin Fragment Hunt: You start with all Golden Coins except the Mario Coin, which has been fragmented into + many pieces. You will see a Golden Coin being received when defeating bosses regardless of whether you are actually getting a coin.""" - display_name = "Coinsanity" + display_name = "Shuffle Golden Coins" default = 0 + option_vanilla = 0 + option_shuffle = 1 + option_mario_coin_fragment_hunt = 2 class GoldenCoinsRequired(Range): - """Number of Golden Coins required to enter Wario's Castle.""" + """Number of Golden Coins required to enter Mario's Castle. Ignored on Mario Coin Fragment Hunt.""" display_name = "Golden Coins Required" range_start = 0 range_end = 6 default = 6 +class MarioCoinFragmentPercentage(Range): + """Percentage of filler items to be replaced with Mario Coin Fragments. Note that the Coinsanity and Coinsanity + Checks settings will greatly impact the number of replaceable filler items. There may be as few as 6 available + slots for Mario Coin Fragments if Coinsanity is off.""" + display_name = "Mario Coin Fragment Percentage" + range_start = 1 + range_end = 100 + default = 20 + + +class MarioCoinFragmentsRequiredPercentage(Range): + """Percentage of the Mario Coins in the item pool that are required to put the Mario Coin together.""" + display_name = "Mario Coin Fragments Required Percentage" + range_start = 1 + range_end = 100 + default = 75 + + +class ShuffleMidwayBells(Toggle): + """Shuffle the Midway Bells into the item pool. Ringing a bell will trigger a location check. + Obtaining a Midway Bell will be permanent, and some levels will require backtracking from the midway point to reach + secret exits.""" + display_name = "Shuffle Midway Bells" + + +class Coinsanity(Toggle): + """Shuffles the singular coins found freestanding and in question mark blocks into the item pool, and adds location + checks made by obtaining a sufficient number of coins in particular levels within a single playthrough.""" + default_name = "Coinsanity" + + +class CoinsanityChecks(Range): + """Number of Coinsanity checks. + A higher number means more checks, and smaller coin amounts per coin item in the item pool. + If Accessibility is set to Locations, auto-scroll levels may have a lower maximum count, which may lead to this + value being limited.""" + default_name = "Coinsanity Checks" + range_start = 31 + range_end = 2599 + default = 150 + + class DifficultyMode(Choice): """Play in normal or easy mode. You can also start in Normal Mode with an "upgrade" to Easy Mode in the item pool, or start in Easy Mode with a Normal Mode "trap" in the item pool.""" @@ -28,13 +76,6 @@ class DifficultyMode(Choice): default = 0 -class ShuffleMidwayBells(Toggle): - """Shuffle the Midway Bells into the item pool. Ringing a bell will trigger a location check. - Obtaining a Midway Bell will be permanent, and some levels will require backtracking from the midway point to reach - secret exits.""" - display_name = "Shuffle Midway Bells" - - class ShufflePipeTraversal(Choice): """Single: Shuffle a Pipe Traversal item into the item pool, which is required to enter any pipes. Split: Shuffle 4 Pipe Traversal items, one required for entering pipes from each direction. @@ -61,8 +102,8 @@ class AutoScrollLevels(NamedRange): Certain levels are excluded.""" display_name = "Auto Scroll Levels" range_start = 0 - range_end = 18 - special_range_names = {"vanilla": -1, "none": 0, "all": 18} + range_end = 17 + special_range_names = {"vanilla": -1, "none": 0, "all": 17} default = -1 @@ -82,12 +123,17 @@ class EnergyLink(Toggle): display_name = "Energy Link" default = 1 + @dataclass class SML2Options(PerGameCommonOptions): - coinsanity: GoldenCoins + shuffle_golden_coins: ShuffleGoldenCoins required_golden_coins: GoldenCoinsRequired - difficulty_mode: DifficultyMode + mario_coin_fragment_percentage: MarioCoinFragmentPercentage + mario_coin_fragments_required_percentage: MarioCoinFragmentsRequiredPercentage + coinsanity: Coinsanity + coinsanity_checks: CoinsanityChecks shuffle_midway_bells: ShuffleMidwayBells + difficulty_mode: DifficultyMode shuffle_pipe_traversal: ShufflePipeTraversal randomize_enemies: RandomizeEnemies randomize_platforms: RandomizePlatforms diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index d8f24d020bfa..d8cc2e602d09 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -127,13 +127,6 @@ def randomize_enemies(data, random): i += 3 -def randomize_auto_scroll_levels(data, random, n): - eligible_levels = [0, 1, 2, 3, 5, 8, 9, 11, 13, 14, 16, 17, 19, 20, 23, 25, 30, 31] - auto_scroll_levels = random.sample(eligible_levels, n) - for i in eligible_levels: - data[rom_addresses["Auto_Scroll_Levels"] + i] = 1 if i in auto_scroll_levels else 0 - - def randomize_platforms(data, random): level_list = [ {"platforms": [0x28, 0x29, 0x2A, 0x2B, 0x2D, 0x2E], "start": 0xE1EF, "end": 0xE249}, @@ -171,25 +164,46 @@ def generate_output(self, output_directory: str): data = bytearray(bsdiff4.patch(data, base_patch)) - random = self.multiworld.per_slot_randoms[self.player] + random = self.random if self.options.randomize_enemies: randomize_enemies(data, random) if self.options.randomize_platforms: randomize_platforms(data, random) - if self.options.auto_scroll_levels > -1: - randomize_auto_scroll_levels(data, random, self.options.auto_scroll_levels.value) if self.options.randomize_music: randomize_music(data, random) if self.options.auto_scroll_trap: data[rom_addresses["Auto_Scroll_Disable"]] = 0xAF - if self.options.coinsanity: + if self.options.shuffle_golden_coins: data[rom_addresses["Coin_Shuffle"]] = 0x40 if self.options.shuffle_midway_bells: data[rom_addresses["Disable_Midway_Bell"]] = 0xC9 - data[rom_addresses["Required_Golden_Coins"]] = self.options.required_golden_coins.value + if self.options.coinsanity: + for section in ("A", "B"): + for i in range(0, 30): + data[rom_addresses[f"Coinsanity_{section}"] + i] = 0x00 + + star_count = max(len([loc for loc in self.multiworld.get_filled_locations() if loc.item.player == self.player + and loc.item.name == "Super Star Duration Increase"]), 1) + data[rom_addresses["Star_Count"]] = star_count // 256 + data[rom_addresses["Star_Count"] + 1] = star_count - (star_count // 256) + if self.options.shuffle_golden_coins == "mario_coin_fragment_hunt": + data[rom_addresses["Coins_Required"]] = self.coin_fragments_required // 256 + data[rom_addresses["Coins_Required"] + 1] = self.coin_fragments_required % 256 + data[rom_addresses["Required_Golden_Coins"]] = 6 + else: + data[rom_addresses["Coins_Required"] + 1] = self.options.required_golden_coins.value + data[rom_addresses["Required_Golden_Coins"]] = self.options.required_golden_coins.value + data[rom_addresses["Midway_Bells"]] = self.options.shuffle_midway_bells.value + data[rom_addresses["Energy_Link"]] = self.options.energy_link.value + data[rom_addresses["Difficulty_Mode"]] = self.options.difficulty_mode.value + data[rom_addresses["Coin_Mode"]] = self.options.shuffle_golden_coins.value + + for i in range(32): + data[rom_addresses["Auto_Scroll_Levels"] + i] = 1 if i in self.auto_scroll_levels else 0 + if self.options.energy_link: # start with 1 life if Energy Link is on so that you don't deposit lives at the start of the game. diff --git a/worlds/marioland2/rom_addresses.py b/worlds/marioland2/rom_addresses.py index f8146d65202b..1980c28a6209 100644 --- a/worlds/marioland2/rom_addresses.py +++ b/worlds/marioland2/rom_addresses.py @@ -10,6 +10,7 @@ "Pipe_Traversal_D": 0x1256, "Pipe_Traversal_SFX_D": 0x125b, "Enable_Swim": 0x1d17, + "Coinsanity_B": 0x1d86, "Auto_Scroll_Levels": 0x1f71, "Starting_Lives": 0x2920, "Get_Hurt_To_Big_Mario": 0x31c7, @@ -19,6 +20,7 @@ "Invincibility_Star_A": 0x349e, "Invincibility_Star_B": 0x34a3, "Enable_Bubble": 0x34e5, + "Coinsanity_A": 0x591f, "Coin_Shuffle": 0x304ce, "Required_Golden_Coins": 0x306e9, "Disable_Midway_Bell": 0x3ef1e, @@ -28,4 +30,10 @@ "Get_Mushroom_B": 0x60ddb, "Get_Carrot_B": 0x60de7, "Get_Fire_Flower_B": 0x60df3, + "Coins_Required": 0x8012c, + "Difficulty_Mode": 0x8012e, + "Star_Count": 0x8012f, + "Midway_Bells": 0x80131, + "Energy_Link": 0x80132, + "Coin_Mode": 0x80133, } From 195c93ac758ea52452a2900640ff85ec58fd3157 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 1 Apr 2024 10:10:57 -0400 Subject: [PATCH 040/113] Re-order options --- worlds/marioland2/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 016f33dbb1fb..0679fee45ec2 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -133,8 +133,8 @@ class SML2Options(PerGameCommonOptions): coinsanity: Coinsanity coinsanity_checks: CoinsanityChecks shuffle_midway_bells: ShuffleMidwayBells - difficulty_mode: DifficultyMode shuffle_pipe_traversal: ShufflePipeTraversal + difficulty_mode: DifficultyMode randomize_enemies: RandomizeEnemies randomize_platforms: RandomizePlatforms auto_scroll_levels: AutoScrollLevels From 3d87fc0e10303e1039b6db43275167c3315b985b Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 1 Apr 2024 10:42:38 -0400 Subject: [PATCH 041/113] Random coinsanity checks instead of exact divisions --- worlds/marioland2/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index f95b3cd11aab..024bc413ae39 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -140,20 +140,21 @@ def create_regions(self): 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] self.max_coin_locations = {region: len(coins_coords[region]) for region in created_regions} if self.options.accessibility == "locations": for level in self.max_coin_locations: if level in auto_scroll_max and level_name_to_id[level] in self.auto_scroll_levels: - self.max_coin_locations[level] = min(self.max_coin_locations[level], auto_scroll_max[level]) - for i in range(self.options.coinsanity_checks - 31): + 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: - coin_locations += [f"{level} - " + str(int(self.max_coin_locations[level] / coins * i)) - + f" Coin{'s' if int(self.max_coin_locations[level] / coins * i) > 1 else ''}" - for i in range(1, coins + 1)] + 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, From d6815ea9cc3538c14e2056129021d49344c3f9b0 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 1 Apr 2024 10:44:27 -0400 Subject: [PATCH 042/113] Rename Coinsanity to Shuffle Golden Coins --- worlds/marioland2/__init__.py | 2 +- worlds/marioland2/options.py | 4 ++-- worlds/marioland2/rom.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index b0b34f278fe4..b526eb3cd35b 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -291,7 +291,7 @@ def create_items(self): "Super Star Duration Increase": 6, } - if self.options.coinsanity: + if self.options.shuffle_golden_coins: for item in self.item_name_groups["Coins"]: item_counts[item] = 1 else: diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 35043c09f8b6..697d9edf0d95 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -5,7 +5,7 @@ class GoldenCoins(Toggle): """Shuffle the Golden Coins into the item pool. You will see a Golden Coin being received when defeating bosses regardless of whether you are actually getting a coin.""" - display_name = "Coinsanity" + display_name = "Shuffle Golden Coins" default = 0 @@ -84,7 +84,7 @@ class EnergyLink(Toggle): @dataclass class SML2Options(PerGameCommonOptions): - coinsanity: GoldenCoins + shuffle_golden_coins: GoldenCoins required_golden_coins: GoldenCoinsRequired difficulty_mode: DifficultyMode shuffle_midway_bells: ShuffleMidwayBells diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index d8f24d020bfa..cd2186cff9e9 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -184,7 +184,7 @@ def generate_output(self, output_directory: str): if self.options.auto_scroll_trap: data[rom_addresses["Auto_Scroll_Disable"]] = 0xAF - if self.options.coinsanity: + if self.options.shuffle_golden_coins: data[rom_addresses["Coin_Shuffle"]] = 0x40 if self.options.shuffle_midway_bells: data[rom_addresses["Disable_Midway_Bell"]] = 0xC9 From a4c78d6ca14bda155aaef333bbdedc80d47e8c21 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 1 Apr 2024 10:48:28 -0400 Subject: [PATCH 043/113] Item Name Group fix --- worlds/marioland2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 024bc413ae39..5939d8887699 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -58,7 +58,7 @@ class MarioLand2World(World): or item_name.endswith("Secret")}, "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, 410)}}, + "Coins": {"1 Coin", *{f"{i} Coins" for i in range(2, 169)}}, "Powerups": {"Mushroom", "Fire Flower", "Carrot"}, "Difficulties": {"Easy Mode", "Normal Mode"} } From 2882669758a88d80f6e25c1ec2a1c6fa3fbcf872 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 1 Apr 2024 12:49:31 -0400 Subject: [PATCH 044/113] Location Name Group fix --- worlds/marioland2/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 5939d8887699..d510c6ae2d76 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -65,8 +65,8 @@ class MarioLand2World(World): location_name_groups = { "Bosses": { - "Tree Zone 5 - The Big Bird", "Space Zone 2 - Star Stage", "Macro Zone 4 - One Mighty Mouse", - "Pumpkin Zone 4 - Witch's Mansion", "Mario Zone 4 - Three Mean Pigs!", "Turtle Zone 3 - Whale Course" + "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"}, From a72bd173b5f286649a777648301aea143b56e781 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 8 Apr 2024 23:46:54 -0400 Subject: [PATCH 045/113] Auto Scroll Mode --- worlds/marioland2/__init__.py | 87 ++++--- worlds/marioland2/client.py | 28 ++- worlds/marioland2/coin_logic.py | 294 ----------------------- worlds/marioland2/items.py | 10 +- worlds/marioland2/locations.py | 14 ++ worlds/marioland2/logic.py | 365 ++++++++++++++++++++++++++++- worlds/marioland2/options.py | 32 ++- worlds/marioland2/rom.py | 6 +- worlds/marioland2/rom_addresses.py | 1 + 9 files changed, 488 insertions(+), 349 deletions(-) delete mode 100644 worlds/marioland2/coin_logic.py diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index d510c6ae2d76..5d191b3b7d0c 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -9,10 +9,11 @@ from . import client from .rom import generate_output, SuperMarioLand2DeltaPatch from .options import SML2Options -from .locations import locations, location_name_to_id, level_name_to_id, START_IDS, coins_coords, auto_scroll_max +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 .logic import has_pipe_up, has_pipe_down, has_pipe_left, has_pipe_right, has_level_progression -from . import coin_logic +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): @@ -87,11 +88,19 @@ def __init__(self, world, player: int): self.coin_fragments_required = 0 def generate_early(self): - if self.options.auto_scroll_levels > -1: - eligible_levels = [0, 1, 2, 3, 5, 8, 9, 11, 13, 14, 16, 19, 20, 23, 25, 30, 31] - self.auto_scroll_levels = self.random.sample(eligible_levels, self.options.auto_scroll_levels.value) - else: + if self.options.auto_scroll_chances == -1: self.auto_scroll_levels = [19, 25, 30] + else: + self.auto_scroll_levels = [] + ineligible_levels = ["Mario's Castle"] + if self.options.auto_scroll_mode == "always" or (self.options.accessibility == "locations" and + self.options.auto_scroll_mode in ("trap_item", + "trap_items")): + ineligible_levels += ["Tree Zone 3", "Macro Zone 2", "Space Zone 1", "Turtle Zone 2", "Pumpkin Zone 2"] + for i in range(32): + if (level_id_to_name[i] not in ineligible_levels + and self.random.randint(1, 100) < self.options.auto_scroll_chances): + self.auto_scroll_levels.append(i) def create_regions(self): menu_region = Region("Menu", self.player, self.multiworld) @@ -143,7 +152,7 @@ def create_regions(self): coinsanity_checks = self.options.coinsanity_checks.value self.num_coin_locations = [[region, 1] for region in created_regions] self.max_coin_locations = {region: len(coins_coords[region]) for region in created_regions} - if self.options.accessibility == "locations": + if self.options.accessibility == "locations" or self.options.auto_scroll_mode == "always": for level in self.max_coin_locations: if level in auto_scroll_max and level_name_to_id[level] in self.auto_scroll_levels: self.max_coin_locations[level] = min(auto_scroll_max[level], self.max_coin_locations[level]) @@ -162,7 +171,9 @@ def create_regions(self): def set_rules(self): entrance_rules = { - "Menu -> Space Zone 1": lambda state: state.has_any(["Hippo Bubble", "Carrot"], self.player), + "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), @@ -202,7 +213,8 @@ def set_rules(self): ].count(True) >= self.options.required_golden_coins) location_rules = { - "Hippo Zone - Normal or Secret Exit": lambda state: state.has_any(["Hippo Bubble", "Carrot", "Swim"], self.player), + "Hippo Zone - Normal or Secret Exit": lambda state: (state.has_any(["Hippo Bubble", "Swim"], self.player) + or (state.has("Carrot", self.player) and not is_auto_scroll(state, self.player, "Hippo Zone"))), # It is possible, however tricky, to beat the Moon Stage without Carrot or Space Physics. # However, it requires somewhat precisely jumping off enemies. Enemy shuffle may make this impossible. # I have not done any testing there. Instead, I will just always make one or the other required, since @@ -210,7 +222,7 @@ def set_rules(self): "Space Zone 1 - Normal Exit": lambda state: state.has_any(["Space Physics", "Carrot"], self.player), # One or the other is actually necessary for the secret exit. "Space Zone 1 - Secret Exit": lambda state: state.has_any( - ["Space Physics", "Carrot"], self.player), + ["Space Physics", "Carrot"], self.player) and not is_auto_scroll(state, self.player, "Space Zone 1"), # Without Space Physics, you must be able to take damage once to reach the bell, and again after the bell. # If bells are not shuffled, then any one powerup will do, as you can get the bell and come back. # Otherwise, you need the bell item from the item pool, or you need to be able to take damage twice in one @@ -231,6 +243,7 @@ def set_rules(self): "Tree Zone 2 Midway Bell", self.player), "Tree Zone 2 - Secret Exit": lambda state: has_pipe_right(state, self.player) and state.has("Carrot", self.player), + "Tree Zone 3 - Normal Exit": lambda state: not is_auto_scroll(state, self.player, "Tree Zone 3"), "Tree Zone 4 - Normal Exit": lambda state: has_pipe_down(state, self.player) and ((has_pipe_right(state, self.player) and has_pipe_up(state, self.player)) or state.has("Tree Zone 4 Midway Bell", self.player)), @@ -247,7 +260,8 @@ def set_rules(self): and state.has("Fire Flower", self.player) and has_pipe_up(state, self.player), "Macro Zone 2 - Normal Exit": lambda state: (has_pipe_down(state, self.player) or state.has( "Macro Zone 2 Midway Bell", self.player)) - and state.has("Swim", self.player) and has_pipe_up(state, self.player), + and state.has("Swim", self.player) and has_pipe_up(state, + self.player) and not is_auto_scroll(state, self.player, "Macro Zone 2"), "Macro Zone 2 - Midway Bell": lambda state: (has_pipe_down( state, self.player) and state.has("Swim", self.player)) or state.has( "Macro Zone 2 Midway Bell", self.player), @@ -256,20 +270,24 @@ def set_rules(self): "Macro Zone 3 - Midway Bell": lambda state: (has_pipe_down(state, self.player) and has_pipe_down(state, self.player)) or state.has("Macro Zone 3 Midway Bell", self.player), "Macro Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), - "Pumpkin Zone 1 - Normal Exit": lambda state: has_pipe_down(state, self.player) or state.has( + "Pumpkin Zone 1 - Normal Exit": lambda state: (has_pipe_down(state, self.player) + and not is_auto_scroll(state, self.player, "Pumpkin Zone 1")) or state.has( "Pumpkin Zone 1 Midway Bell", self.player), - "Pumpkin Zone 1 - Midway Bell": lambda state: has_pipe_down(state, self.player) or state.has( + "Pumpkin Zone 1 - Midway Bell": lambda state: (has_pipe_down(state, self.player) + and not is_auto_scroll(state, self.player, "Pumpkin Zone 1")) or state.has( "Pumpkin Zone 1 Midway Bell", self.player), "Pumpkin Zone 2 - Normal Exit": lambda state: has_pipe_down(state, self.player) and has_pipe_up( - state, self.player) and has_pipe_right(state, self.player) and state.has("Swim", self.player), - # You can only spin jump as Big Mario or Fire Mario + state, self.player) and has_pipe_right(state, self.player) and state.has("Swim", + self.player) and not is_auto_scroll(state, self.player, "Pumpkin Zone 2"), "Pumpkin Zone 2 - Secret Exit": lambda state: has_pipe_down( state, self.player) and has_pipe_up(state, self.player) and has_pipe_right( state, self.player) and state.has("Swim", self.player) and state.has_any( - ["Mushroom", "Fire Flower"], self.player), + ["Mushroom", "Fire Flower"], self.player) and not is_auto_scroll(state, self.player, "Pumpkin Zone 2"), "Pumpkin Zone 3 - Secret Exit": lambda state: state.has("Carrot", self.player), "Pumpkin Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), - "Mario Zone 1 - Normal Exit": lambda state: has_pipe_right(state, self.player), + "Mario Zone 1 - Normal Exit": lambda state: has_pipe_right(state, self.player) and ( + state.has_any(["Mushroom", "Fire Flower", "Carrot", "Mario Zone 1 Midway Bell"], self.player) + or not is_auto_scroll(state, self.player, "Mario Zone 1")), # It is possible to get as small mario, but it is a very precise jump and you will die afterward. "Mario Zone 1 - Midway Bell": lambda state: (state.has_any( ["Mushroom", "Fire Flower", "Carrot"], self.player) and has_pipe_right(state, self.player)) @@ -277,11 +295,13 @@ def set_rules(self): "Mario Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), "Turtle Zone 2 - Normal Exit": lambda state: has_pipe_up(state, self.player) and has_pipe_down( state, self.player) and has_pipe_right(state, self.player) and has_pipe_left(state, self.player) - and state.has("Swim", self.player), + and state.has("Swim", self.player) and not is_auto_scroll(state, self.player, "Turtle Zone 2"), "Turtle Zone 2 - Midway Bell": lambda state: state.has_any( - ["Swim", "Turtle Zone 2 Midway Bell"], self.player), + ["Swim", "Turtle Zone 2 Midway Bell"], self.player) and not is_auto_scroll(state, + self.player, "Turtle Zone 2"), "Turtle Zone 2 - Secret Exit": lambda state: has_pipe_up( - state, self.player) and state.has("Swim", self.player), #state.has_any(["Swim", "Turtle Zone 2 Midway Bell"], self.player), # hard logic option? + state, self.player) and state.has("Swim", self.player) and not is_auto_scroll(state, + self.player, "Turtle Zone 2"), #state.has_any(["Swim", "Turtle Zone 2 Midway Bell"], self.player), # hard logic option? "Turtle Zone Secret Course - Normal Exit": lambda state: state.has_any(["Fire Flower", "Carrot"], self.player), "Turtle Zone 3 - Boss": lambda state: has_pipe_right(state, self.player), @@ -296,12 +316,11 @@ def set_rules(self): if location.name in location_rules: location.access_rule = location_rules[location.name] elif location.name.endswith("Coins"): - rule = getattr(coin_logic, location.parent_region.name.lower().replace(" ", "_") + "_coins", None) + rule = getattr(logic, location.parent_region.name.lower().replace(" ", "_") + "_coins", None) if rule: coins = int(location.name.split(" ")[-2]) - auto_scroll = level_name_to_id[location.name.split(" -")[0]] in self.auto_scroll_levels - location.access_rule = lambda state, coin_rule=rule, \ - num_coins=coins, scroll=auto_scroll: coin_rule(state, self.player, num_coins, scroll) + location.access_rule = lambda state, coin_rule=rule, num_coins=coins: \ + coin_rule(state, self.player, num_coins) self.multiworld.completion_condition[self.player] = lambda state: state.has("Wario Defeated", self.player) def create_items(self): @@ -397,8 +416,18 @@ def create_items(self): else: self.multiworld.push_precollected(self.create_item("Pipe Traversal")) - if self.options.auto_scroll_trap: + if self.options.auto_scroll_mode == "trap_item": item_counts["Auto Scroll"] = 1 + elif self.options.auto_scroll_mode == "trap_items": + for level, level_id in level_name_to_id.items(): + if level_id in self.auto_scroll_levels: + item_counts[f"Auto Scroll - {level}"] = 1 + elif self.options.auto_scroll_mode == "cancel_item": + item_counts["Auto Scroll Cancel"] = 1 + elif self.options.auto_scroll_mode == "cancel_items": + for level, level_id in level_name_to_id.items(): + if level_id in self.auto_scroll_levels: + item_counts[f"Auto Scroll Cancel - {level}"] = 1 for item in self.multiworld.precollected_items[self.player]: if item.name in item_counts and item_counts[item.name] > 0: @@ -432,7 +461,11 @@ def create_items(self): item_counts[double_progression_item] -= 2 item_counts[double_progression_item + " x2"] = 1 continue - raise Exception(f"Super Mario Land 2: Too many items for locations. Player: {self.player}") + 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) diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 5725652382d6..4befb9ef27ec 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -41,11 +41,10 @@ async def game_watcher(self, ctx: BizHawkClientContext): from .items import items from .locations import locations, level_id_to_name, coins_coords, location_name_to_id - (game_loaded_check, level_data, music, auto_scroll_enabled, auto_scroll_levels, current_level, midway_point, - bcd_lives, num_items_received, coins, options) = \ + (game_loaded_check, level_data, music, auto_scroll_levels, current_level, + midway_point, bcd_lives, num_items_received, coins, options) = \ await read(ctx.bizhawk_ctx, [(0x0046, 10, "CartRAM"), (0x0848, 42, "CartRAM"), (0x0469, 1, "CartRAM"), - (rom_addresses["Auto_Scroll_Disable"], 1, "ROM"), - (rom_addresses["Auto_Scroll_Levels"], 32, "ROM"), + (rom_addresses["Auto_Scroll_Levels_B"], 32, "ROM"), (0x0269, 1, "CartRAM"), (0x02A0, 1, "CartRAM"), (0x022C, 1, "CartRAM"), (0x00F0, 2, "CartRAM"), (0x0262, 2, "CartRAM"), (rom_addresses["Coins_Required"], 8, "ROM")]) @@ -58,10 +57,9 @@ async def game_watcher(self, ctx: BizHawkClientContext): coin_mode = options[7] current_level = int.from_bytes(current_level, "big") - + auto_scroll_levels = list(auto_scroll_levels) midway_point = int.from_bytes(midway_point, "big") music = int.from_bytes(music, "big") - auto_scroll_enabled = int.from_bytes(auto_scroll_enabled, "big") level_data = list(level_data) lives = bcd_lives.hex() num_items_received = int.from_bytes(num_items_received, "big") @@ -112,7 +110,6 @@ async def game_watcher(self, ctx: BizHawkClientContext): locations_checked = [location_name_to_id[f"{level_name} - {i} Coin{'s' if i > 1 else ''}"] for i in range(1, num_coins + 1)] - new_lives = int(lives) energy_link_add = None if energy_link: @@ -195,12 +192,19 @@ async def game_watcher(self, ctx: BizHawkClientContext): # after registering the check for the midway bell, clear the value just for safety. data_writes.append((0x02A0, [0], "CartRAM")) - if "Auto Scroll" in items_received: - if auto_scroll_enabled == 0xaf: # auto scroll has not yet been enabled - data_writes.append((rom_addresses["Auto_Scroll_Disable"], [0x7e], "ROM")) - # if the current level is an auto scroll level, turn on auto scrolling now - if auto_scroll_levels[current_level] == 1: + for i in range(32): + if i == 24: + continue + if "Auto Scroll" in items_received or f"Auto Scroll - {level_id_to_name[i]}" in items_received: + auto_scroll_levels[i] = 1 + if i == current_level: data_writes.append((0x02C8, [0x01], "CartRAM")) + elif ("Auto Scroll Cancel" in items_received + or f"Auto Scroll Cancel - {level_id_to_name[i]}" in items_received): + auto_scroll_levels[i] = 0 + if i == current_level: + data_writes.append((0x02C8, [0x00], "CartRAM")) + data_writes.append((rom_addresses["Auto_Scroll_Levels"], auto_scroll_levels, "ROM")) success = await guarded_write(ctx.bizhawk_ctx, data_writes, [(0x0848, level_data, "CartRAM"), (0x022C, [int.from_bytes(bcd_lives, "big")], diff --git a/worlds/marioland2/coin_logic.py b/worlds/marioland2/coin_logic.py deleted file mode 100644 index e42cc9b981b5..000000000000 --- a/worlds/marioland2/coin_logic.py +++ /dev/null @@ -1,294 +0,0 @@ -from .logic import has_pipe_down, has_pipe_up, has_pipe_right, has_pipe_left - - -def mushroom_zone_coins(state, player, coins, auto_scroll): - reachable_coins = 40 - if has_pipe_down(state, player): - # There's 24 in each of the underground sections. - # The first one requires missing some question mark blocks if auto scrolling (the last +4). - # If you go in the second without pipe up, you can get everything except the last 5 plus the ones in the first - # underground section. - reachable_coins += 19 - if has_pipe_up(state, player) or not auto_scroll: - reachable_coins += 5 - if has_pipe_up(state, player): - reachable_coins += 20 - if not auto_scroll: - reachable_coins += 4 - return coins <= reachable_coins - - -def tree_zone_1_coins(state, player, coins, auto_scroll): - return coins <= 87 or not auto_scroll - - -def tree_zone_2_coins(state, player, coins, auto_scroll): - reachable_coins = 18 - if has_pipe_right(state, player): - reachable_coins += 38 - if state.has("Carrot", player): - reachable_coins += 12 - if not auto_scroll: - reachable_coins += 30 - elif state.has("Tree Zone 2 Midway Bell", player): - reachable_coins = 30 - if not auto_scroll: - reachable_coins += 8 - return coins <= reachable_coins - - -def tree_zone_3_coins(state, player, coins, auto_scroll): - if coins <= 19: - return True - elif state.has_any(["Mushroom", "Fire Flower"], player) and coins <= 21: - return True - return state.has("Carrot", player) - - -def tree_zone_4_coins(state, player, coins, auto_scroll): - reachable_coins = 0 - if has_pipe_up(state, player): - reachable_coins += 14 - if has_pipe_right(state, player): - reachable_coins += 4 - if has_pipe_down(state, player): - reachable_coins += 46 - if not auto_scroll: - reachable_coins += 10 - elif state.has("Tree Zone 4 Midway Bell", player): - if not auto_scroll: - if has_pipe_left(state, player): - reachable_coins += 18 - if has_pipe_down(state, player): - reachable_coins += 46 - if has_pipe_up(state, player): - reachable_coins += 10 - elif has_pipe_down(state, player): - reachable_coins += 10 - return coins <= reachable_coins - - -def tree_zone_5_coins(state, player, coins, auto_scroll): - reachable_coins = 0 - # Not actually sure if these platforms can be randomized / can make the coin blocks unreachable from below - if ((not state.multiworld.worlds[player].options.randomize_platforms) - or state.has_any(["Mushroom", "Fire Flower"], player)): - reachable_coins += 2 - if state.has_any(["Mushroom", "Fire Flower"], player): - reachable_coins += 2 - if state.has("Carrot", player): - reachable_coins += 18 - if has_pipe_up(state, player) and not auto_scroll: - reachable_coins += 13 - elif has_pipe_up(state, player): - reachable_coins += 13 - return coins <= reachable_coins - - -def hippo_zone_coins(state, player, coins, auto_scroll): - reachable_coins = 4 - if state.has_any(["Swim", "Hippo Bubble", "Carrot"], player): - reachable_coins += 108 - if state.has_any(["Mushroom", "Fire Flower", "Hippo Bubble"], player): - reachable_coins += 6 - if state.has_all(["Fire Flower", "Swim"], player): - reachable_coins += 1 - if state.has("Hippo Bubble", player): - # Probably some of these are reachable with Carrot - reachable_coins += 52 - return coins <= reachable_coins - - -def pumpkin_zone_1_coins(state, player, coins, auto_scroll): - reachable_coins = 0 - if state.has("Pumpkin Zone 1 Midway Bell", player) or has_pipe_down(state, player): - reachable_coins += 38 - if has_pipe_up(state, player): - reachable_coins += 2 - return coins <= reachable_coins - - -def pumpkin_zone_2_coins(state, player, coins, auto_scroll): - reachable_coins = 17 - if has_pipe_down(state, player): - reachable_coins += 7 - if state.has("Swim", player): - reachable_coins += 6 - if has_pipe_up(state, player) and has_pipe_right(state, player): - reachable_coins += 1 - if state.has_any(["Mushroom", "Fire Flower"], player): - reachable_coins += 5 - return coins <= reachable_coins - - -def pumpkin_zone_secret_course_1_coins(state, player, coins, auto_scroll): - if coins <= 20: # I've gotten as high as 22 but only once. We'll be a bit forgiving. - return True - return state.has("Carrot", player) - - -def pumpkin_zone_3_coins(state, player, coins, auto_scroll): - reachable_coins = 38 - if has_pipe_up(state, player) and ((not auto_scroll) or has_pipe_down(state, player)): - reachable_coins += 12 - if has_pipe_down(state, player) and not auto_scroll: - reachable_coins += 11 - return coins <= reachable_coins - - -def pumpkin_zone_4_coins(state, player, coins, auto_scroll): - reachable_coins = 29 - if has_pipe_down(state, player): - if auto_scroll: - if has_pipe_up(state, player): - reachable_coins += 16 - else: - reachable_coins += 4 - else: - reachable_coins += 28 - # both sets of coins are down, but you need pipe up to return to go down to the next set in one playthrough - if has_pipe_up(state, player): - reachable_coins += 16 - return coins <= reachable_coins - - -def mario_zone_1_coins(state, player, coins, auto_scroll): - reachable_coins = 0 - if has_pipe_right(state, player) or (has_pipe_left(state, player) - and state.has("Mario Zone 1 Midway Bell", player)): - reachable_coins += 32 - if has_pipe_right(state, player): - reachable_coins += 8 - # coins from end section. I was able to get 13 as small mario, giving some leniency - if state.has("Carrot", player): - reachable_coins += 28 - else: - reachable_coins += 12 - if state.has("Fire Flower", player): - reachable_coins += 46 - return coins <= reachable_coins - - -def mario_zone_3_coins(state, player, coins, auto_scroll): - reachable_coins = 34 - if state.has("Fire Flower", player): - reachable_coins += 23 - return coins <= reachable_coins - - -def mario_zone_4_coins(state, player, coins, auto_scroll): - return coins <= 63 or not auto_scroll - - -def turtle_zone_1_coins(state, player, coins, auto_scroll): - reachable_coins = 37 - if auto_scroll: - reachable_coins -= 1 - if state.has("Swim", player): - reachable_coins += 16 - if state.has("Carrot", player): - reachable_coins += 24 - if auto_scroll: - reachable_coins -= 10 - return coins <= reachable_coins - - -def turtle_zone_2_coins(state, player, coins, auto_scroll): - reachable_coins = 4 - if state.has("Swim", player): - reachable_coins += 20 - elif state.has("Turtle Zone 2 Midway Bell", player): - reachable_coins += 4 - if (has_pipe_right(state, player) and has_pipe_down(state, player) - and state.has_any(["Swim", "Turtle Zone 2 Midway Bell"], player)): - reachable_coins += 1 - if has_pipe_left(state, player) and has_pipe_up(state, player): - reachable_coins += 1 - if state.has("Swim", player): - reachable_coins += 1 - return coins <= reachable_coins - - -def turtle_zone_secret_course_coins(state, player, coins, auto_scroll): - reachable_coins = 53 - if state.has("Carrot", player): - reachable_coins += 44 - elif state.has("Fire Flower", player): - reachable_coins += 36 # was able to get 38, some leniency - return coins <= reachable_coins - - -def turtle_zone_4_coins(state, player, coins, auto_scroll): - reachable_coins = 42 - if state.has("Swim", player): - reachable_coins += 17 - return coins <= reachable_coins - - -def space_zone_1_coins(state, player, coins, auto_scroll): - return (coins <= 21 or (coins <= 50 and state.has_any(["Mushroom", "Fire Flower"], player)) - or state.has_any(["Carrot", "Space Physics"], player)) - - -def space_zone_2_coins(state, player, coins, auto_scroll): - reachable_coins = 12 - if state.has_any(["Mushroom", "Fire Flower", "Carrot", "Space Physics"], player): - reachable_coins += 15 - if state.has("Space Physics", player) or not auto_scroll: - reachable_coins += 4 # last few bottom row question mark blocks that are hard to get when auto scrolling. - if (state.has("Space Physics", player) or ( - state.has("Mushroom", player) and state.has_any(["Fire Flower", "Carrot"], player))): - reachable_coins += 3 - if state.has("Space Physics", player): - reachable_coins += 79 - if not auto_scroll: - reachable_coins += 21 - return coins <= reachable_coins - - -def macro_zone_1_coins(state, player, coins, auto_scroll): - reachable_coins = 0 - if has_pipe_down(state, player): - reachable_coins += 74 - if not auto_scroll: - reachable_coins += 4 - if state.has("Fire Flower", player): - reachable_coins += 19 - elif (not auto_scroll) and state.has("Macro Zone 1 Midway Bell", player): - reachable_coins += 67 - return coins <= reachable_coins - - -def macro_zone_secret_course_coins(state, player, coins, auto_scroll): - return state.has_any(["Mushroom", "Fire Flower"], player) - - -def macro_zone_2_coins(state, player, coins, auto_scroll): - if coins <= 27: - return True - if has_pipe_up(state, player) and state.has("Swim", player): - if has_pipe_down(state, player): - return True - if state.has("Macro Zone 2 Midway Bell", player): - # Cannot return to the first section from the bell - return coins <= 42 - - -def macro_zone_3_coins(state, player, coins, auto_scroll): - if has_pipe_up(state, player) and has_pipe_down(state, player): - return True - reachable_coins = 31 - if has_pipe_up(state, player): - reachable_coins += 36 - if has_pipe_down(state, player): - reachable_coins += 18 - return coins <= reachable_coins - - -def macro_zone_4_coins(state, player, coins, auto_scroll): - reachable_coins = 61 - if auto_scroll: - reachable_coins -= 8 - if state.has("Carrot", player): - reachable_coins += 6 - return coins <= reachable_coins diff --git a/worlds/marioland2/items.py b/worlds/marioland2/items.py index 6a601c9a0abf..74b0b8eb6bc4 100644 --- a/worlds/marioland2/items.py +++ b/worlds/marioland2/items.py @@ -1,4 +1,5 @@ from BaseClasses import ItemClassification +from .locations import level_name_to_id items = { "Space Zone Progression": ItemClassification.progression, @@ -41,6 +42,10 @@ "Easy Mode": ItemClassification.useful, "Normal Mode": ItemClassification.trap, "Auto Scroll": ItemClassification.trap, + **{f"Auto Scroll - {level}": ItemClassification.trap for level in level_name_to_id if level != "Wario's Castle"}, + "Cancel Auto Scroll": ItemClassification.progression, + **{f"Cancel Auto Scroll - {level}": ItemClassification.progression for level in level_name_to_id + if level != "Wario's Castle"}, "Mushroom Zone Midway Bell": ItemClassification.filler, "Tree Zone 1 Midway Bell": ItemClassification.filler, "Tree Zone 2 Midway Bell": ItemClassification.progression_skip_balancing, @@ -65,4 +70,7 @@ "Turtle Zone 3 Midway Bell": ItemClassification.filler, "1 Coin": ItemClassification.filler, **{f"{i} Coins": ItemClassification.filler for i in range(2, 169)} -} \ No newline at end of file +} + +for level in {"Turtle Zone Secret", "Macro Zone Secret", "Turtle Zone 3", "Scenic Course", "Mario Zone 2"}: + items[f"Cancel Auto Scroll - {level}"] = ItemClassification.useful diff --git a/worlds/marioland2/locations.py b/worlds/marioland2/locations.py index 96ba65d4c3d6..0e53abb69d12 100644 --- a/worlds/marioland2/locations.py +++ b/worlds/marioland2/locations.py @@ -455,6 +455,8 @@ location_name_to_id[f"{level} - {i} Coin{'s' if i > 1 else ''}"] = loc_id loc_id += 1 +# eligible_levels = [0, 1, 2, 3, 5, 8, 9, 11, 13, 14, 16, 19, 20, 22, 23, 25, 30, 31] + level_id_to_name = { 0: 'Mushroom Zone', 25: 'Scenic Course', 1: 'Tree Zone 1', 2: 'Tree Zone 2', 4: 'Tree Zone 3', 3: 'Tree Zone 4', 5: 'Tree Zone 5', 29: 'Tree Zone Secret Course', 17: 'Hippo Zone', 18: 'Space Zone 1', @@ -469,14 +471,26 @@ auto_scroll_max = { "Mushroom Zone": 84, + "Hippo Zone": 160, "Tree Zone 1": 87, "Tree Zone 2": 68, + "Tree Zone 3": 4, "Tree Zone 4": 64, "Tree Zone 5": 22, + "Space Zone 1": 72, "Space Zone 2": 113, "Macro Zone 1": 74, + "Macro Zone 2": 27, + "Macro Zone 3": 63, "Macro Zone 4": 59, + "Pumpkin Zone 1": 12, + "Pumpkin Zone 2": 23, "Pumpkin Zone 3": 50, "Pumpkin Zone 4": 45, + "Pumpkin Zone Secret Course 1": 172, + "Mario Zone 1": 68, + "Mario Zone 3": 29, + "Mario Zone 4": 63, "Turtle Zone 1": 66, + "Turtle Zone 2": 8, } diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index e39cd0115c86..5528e0922a7d 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -1,3 +1,13 @@ +from .locations import level_name_to_id + + +def is_auto_scroll(state, player, level): + level_id = level_name_to_id[level] + if state.has_any(["Cancel Auto Scroll", f"Cancel Auto Scroll - {level}"], player): + return False + return level_id in state.multiworld.worlds[player].auto_scroll_levels + + def has_pipe_right(state, player): return state.has_any(["Pipe Traversal - Right", "Pipe Traversal"], player) @@ -15,4 +25,357 @@ def has_pipe_up(state, player): def has_level_progression(state, item, player, count=1): - return state.count(item, player) + (state.count(item + " x2", player) * 2) >= count \ No newline at end of file + return state.count(item, player) + (state.count(item + " x2", player) * 2) >= count + + +def mushroom_zone_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Mushroom Zone") + reachable_coins = 40 + if has_pipe_down(state, player): + # There's 24 in each of the underground sections. + # The first one requires missing some question mark blocks if auto scrolling (the last +4). + # If you go in the second without pipe up, you can get everything except the last 5 plus the ones in the first + # underground section. + reachable_coins += 19 + if has_pipe_up(state, player) or not auto_scroll: + reachable_coins += 5 + if has_pipe_up(state, player): + reachable_coins += 20 + if not auto_scroll: + reachable_coins += 4 + return coins <= reachable_coins + + +def tree_zone_1_coins(state, player, coins): + return coins <= 87 or not is_auto_scroll(state, player, "Tree Zone 1") + + +def tree_zone_2_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Tree Zone 2") + reachable_coins = 18 + if has_pipe_right(state, player): + reachable_coins += 38 + if state.has("Carrot", player): + reachable_coins += 12 + if not auto_scroll: + reachable_coins += 30 + elif state.has("Tree Zone 2 Midway Bell", player): + reachable_coins = 30 + if not auto_scroll: + reachable_coins += 8 + return coins <= reachable_coins + + +def tree_zone_3_coins(state, player, coins): + if is_auto_scroll(state, player, "Tree Zone 3"): + return coins <= 4 + if coins <= 19: + return True + elif state.has_any(["Mushroom", "Fire Flower"], player) and coins <= 21: + return True + return state.has("Carrot", player) + + +def tree_zone_4_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Tree Zone 4") + reachable_coins = 0 + if has_pipe_up(state, player): + reachable_coins += 14 + if has_pipe_right(state, player): + reachable_coins += 4 + if has_pipe_down(state, player): + reachable_coins += 46 + if not auto_scroll: + reachable_coins += 10 + elif state.has("Tree Zone 4 Midway Bell", player): + if not auto_scroll: + if has_pipe_left(state, player): + reachable_coins += 18 + if has_pipe_down(state, player): + reachable_coins += 46 + if has_pipe_up(state, player): + reachable_coins += 10 + elif has_pipe_down(state, player): + reachable_coins += 10 + return coins <= reachable_coins + + +def tree_zone_5_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Tree Zone 5") + reachable_coins = 0 + # Not actually sure if these platforms can be randomized / can make the coin blocks unreachable from below + if ((not state.multiworld.worlds[player].options.randomize_platforms) + or state.has_any(["Mushroom", "Fire Flower"], player)): + reachable_coins += 2 + if state.has_any(["Mushroom", "Fire Flower"], player): + reachable_coins += 2 + if state.has("Carrot", player): + reachable_coins += 18 + if has_pipe_up(state, player) and not auto_scroll: + reachable_coins += 13 + elif has_pipe_up(state, player): + reachable_coins += 13 + return coins <= reachable_coins + + +def hippo_zone_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Hippo Zone") + # This is all somewhat forgiving. + reachable_coins = 4 + if auto_scroll: + if state.has("Hippo Bubble", player): + reachable_coins = 160 + elif state.has("Carrot", player): + reachable_coins = 90 + elif state.has("Swim", player): + reachable_coins = 28 + else: + if state.has_any(["Swim", "Hippo Bubble", "Carrot"], player): + reachable_coins += 108 + if state.has_any(["Mushroom", "Fire Flower", "Hippo Bubble"], player): + reachable_coins += 6 + if state.has_all(["Fire Flower", "Swim"], player): + reachable_coins += 1 + if state.has("Hippo Bubble", player): + reachable_coins += 52 + return coins <= reachable_coins + + +def pumpkin_zone_1_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Pumpkin Zone 1") + if auto_scroll: + return coins <= 12 and state.has("Pumpkin Zone 1 Midway Bell", player) + reachable_coins = 0 + if state.has("Pumpkin Zone 1 Midway Bell", player) or has_pipe_down(state, player): + reachable_coins += 38 + if has_pipe_up(state, player): + reachable_coins += 2 + return coins <= reachable_coins + + +def pumpkin_zone_2_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Pumpkin Zone 2") + reachable_coins = 17 + if has_pipe_down(state, player): + if not auto_scroll: + reachable_coins += 7 + if (has_pipe_up(state, player) or auto_scroll) and state.has("Swim", player): + reachable_coins += 6 + if has_pipe_right(state, player) and not auto_scroll: + reachable_coins += 1 + if state.has_any(["Mushroom", "Fire Flower"], player): + reachable_coins += 5 + return coins <= reachable_coins + + +def pumpkin_zone_secret_course_1_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Pumpkin Zone Secret Course 1") + # We'll be a bit forgiving. + if coins <= 20: + return True + if state.has("Carrot", player): + if auto_scroll: + return coins <= 172 + return True + + +def pumpkin_zone_3_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Pumpkin Zone 3") + reachable_coins = 38 + if has_pipe_up(state, player) and ((not auto_scroll) or has_pipe_down(state, player)): + reachable_coins += 12 + if has_pipe_down(state, player) and not auto_scroll: + reachable_coins += 11 + return coins <= reachable_coins + + +def pumpkin_zone_4_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Pumpkin Zone 4") + reachable_coins = 29 + if has_pipe_down(state, player): + if auto_scroll: + if has_pipe_up(state, player): + reachable_coins += 16 + else: + reachable_coins += 4 + else: + reachable_coins += 28 + # both sets of coins are down, but you need pipe up to return to go down to the next set in one playthrough + if has_pipe_up(state, player): + reachable_coins += 16 + return coins <= reachable_coins + + +def mario_zone_1_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Mario Zone 1") + reachable_coins = 0 + if has_pipe_right(state, player) or (has_pipe_left(state, player) + and state.has("Mario Zone 1 Midway Bell", player) and not auto_scroll): + reachable_coins += 32 + if has_pipe_right(state, player) and (state.has_any(["Mushroom", "Fire Flower", "Carrot"], player) + or not auto_scroll): + reachable_coins += 8 + # coins from end section. I was able to get 13 as small mario, giving some leniency + if state.has("Carrot", player): + reachable_coins += 28 + else: + reachable_coins += 12 + if state.has("Fire Flower", player) and not auto_scroll: + reachable_coins += 46 + return coins <= reachable_coins + + +def mario_zone_3_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Mario Zone 3") + reachable_coins = 24 + if not auto_scroll: + reachable_coins += 10 + if state.has("Fire Flower", player): + reachable_coins += 23 + if auto_scroll: + reachable_coins -= 18 + return coins <= reachable_coins + + +def mario_zone_4_coins(state, player, coins): + return coins <= 63 or not is_auto_scroll(state, player, "Mario Zone 4") + + +def turtle_zone_1_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Turtle Zone 1") + reachable_coins = 37 + if auto_scroll: + reachable_coins -= 1 + if state.has("Swim", player): + reachable_coins += 16 + if state.has("Carrot", player): + reachable_coins += 24 + if auto_scroll: + reachable_coins -= 10 + return coins <= reachable_coins + + +def turtle_zone_2_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Turtle Zone 2") + reachable_coins = 1 + if auto_scroll: + if state.has("Swim", player): + reachable_coins += 7 + else: + reachable_coins += 3 + if state.has("Swim", player): + reachable_coins += 20 + elif state.has("Turtle Zone 2 Midway Bell", player): + reachable_coins += 4 + if (has_pipe_right(state, player) and has_pipe_down(state, player) + and state.has_any(["Swim", "Turtle Zone 2 Midway Bell"], player)): + reachable_coins += 1 + if has_pipe_left(state, player) and has_pipe_up(state, player): + reachable_coins += 1 + if state.has("Swim", player): + reachable_coins += 1 + return coins <= reachable_coins + + +def turtle_zone_secret_course_coins(state, player, coins): + reachable_coins = 53 + if state.has("Carrot", player): + reachable_coins += 44 + elif state.has("Fire Flower", player): + reachable_coins += 36 # was able to get 38, some leniency + return coins <= reachable_coins + + +def space_zone_1_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Space Zone 1") + if auto_scroll: + reachable_coins = 12 + if state.has_any(["Carrot", "Space Physics"], 1): + reachable_coins += 20 + # If you have Space Physics, you can't make it up to the upper section. We have to assume you might have it, + # so the coins up there must be out of logic if there is auto scrolling. + if state.has("Space Physics", 1): + reachable_coins += 40 + return (coins <= 21 or (coins <= 50 and state.has_any(["Mushroom", "Fire Flower"], player)) + or state.has_any(["Carrot", "Space Physics"], player)) + + +def space_zone_2_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Space Zone 2") + reachable_coins = 12 + if state.has_any(["Mushroom", "Fire Flower", "Carrot", "Space Physics"], player): + reachable_coins += 15 + if state.has("Space Physics", player) or not auto_scroll: + reachable_coins += 4 # last few bottom row question mark blocks that are hard to get when auto scrolling. + if (state.has("Space Physics", player) or ( + state.has("Mushroom", player) and state.has_any(["Fire Flower", "Carrot"], player))): + reachable_coins += 3 + if state.has("Space Physics", player): + reachable_coins += 79 + if not auto_scroll: + reachable_coins += 21 + return coins <= reachable_coins + + +def space_zone_secret_coins(state, player, coins): + return coins <= 96 or not is_auto_scroll(state, player, "Space Zone Secret Course") + + +def macro_zone_1_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Macro Zone 1") + reachable_coins = 0 + if has_pipe_down(state, player): + reachable_coins += 74 + if not auto_scroll: + reachable_coins += 4 + if state.has("Fire Flower", player): + reachable_coins += 19 + elif (not auto_scroll) and state.has("Macro Zone 1 Midway Bell", player): + reachable_coins += 67 + return coins <= reachable_coins + + +def macro_zone_secret_course_coins(state, player, coins): + return state.has_any(["Mushroom", "Fire Flower"], player) + + +def macro_zone_2_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Macro Zone 2") + if coins <= 27: + return True + if has_pipe_up(state, player) and state.has("Swim", player) and not auto_scroll: + if has_pipe_down(state, player): + return True + if state.has("Macro Zone 2 Midway Bell", player): + # Cannot return to the first section from the bell + return coins <= 42 + + +def macro_zone_3_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Macro Zone 3") + reachable_coins = 7 + if not auto_scroll: + reachable_coins = 24 + if has_pipe_up(state, player) and has_pipe_down(state, player): + if auto_scroll: + reachable_coins += 56 + else: + return True + elif has_pipe_up(state, player): + if auto_scroll: + reachable_coins += 12 + else: + reachable_coins += 36 + elif has_pipe_down(state, player): + reachable_coins += 18 + return coins <= reachable_coins + + +def macro_zone_4_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Macro Zone 4") + reachable_coins = 61 + if auto_scroll: + reachable_coins -= 8 + if state.has("Carrot", player): + reachable_coins += 6 + return coins <= reachable_coins diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 0679fee45ec2..5d37a554a321 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -97,19 +97,29 @@ class RandomizePlatforms(Toggle): display_name = "Randomize Platforms" -class AutoScrollLevels(NamedRange): - """Keep auto scroll levels vanilla or choose a number of levels to be randomly selected to have auto-scrolling. - Certain levels are excluded.""" - display_name = "Auto Scroll Levels" +class AutoScrollChances(NamedRange): + """Chance per eligible level to be made into an auto scroll level. Can also set to Vanilla to leave them unchanged.""" + display_name = "Auto Scroll Chance" range_start = 0 - range_end = 17 - special_range_names = {"vanilla": -1, "none": 0, "all": 17} + range_end = 100 + special_range_names = {"vanilla": -1, "none": 0, "all": 100} default = -1 -class AutoScrollTrap(Toggle): - """If on, auto scroll levels will not auto scroll until you've received the Auto Scroll trap item.""" - display_name = "Auto Scroll Trap" +class AutoScrollMode(Choice): + """Always: Any auto scroll levels will always auto-scroll. + Trap Item: Auto scroll levels will only auto-scroll after obtaining the Auto Scroll trap item. + Trap Items: As with Trap Item, but there is a separate trap item for each auto scroll level. + Cancel Item: Auto Scroll levels will stop auto-scrolling after obtaining the Auto Scroll Cancel item. + Cancel Items: As with Cancel Item, but there is a separate cancel item for each auto scroll level. + The effects of Trap and Cancel items are permanent!""" + display_name = "Auto Scroll Mode" + option_always = 0 + option_trap_item = 1 + option_trap_items = 2 + option_cancel_item = 3 + option_cancel_items = 4 + default = 0 class RandomizeMusic(Toggle): @@ -134,10 +144,10 @@ class SML2Options(PerGameCommonOptions): coinsanity_checks: CoinsanityChecks shuffle_midway_bells: ShuffleMidwayBells shuffle_pipe_traversal: ShufflePipeTraversal + auto_scroll_mode: AutoScrollMode + auto_scroll_chances: AutoScrollChances difficulty_mode: DifficultyMode randomize_enemies: RandomizeEnemies randomize_platforms: RandomizePlatforms - auto_scroll_levels: AutoScrollLevels - auto_scroll_trap: AutoScrollTrap randomize_music: RandomizeMusic energy_link: EnergyLink diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index d8cc2e602d09..10586f5055f6 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -173,8 +173,8 @@ def generate_output(self, output_directory: str): if self.options.randomize_music: randomize_music(data, random) - if self.options.auto_scroll_trap: - data[rom_addresses["Auto_Scroll_Disable"]] = 0xAF + # if self.options.auto_scroll_trap: + # data[rom_addresses["Auto_Scroll_Disable"]] = 0xAF if self.options.shuffle_golden_coins: data[rom_addresses["Coin_Shuffle"]] = 0x40 if self.options.shuffle_midway_bells: @@ -203,7 +203,7 @@ def generate_output(self, output_directory: str): for i in range(32): data[rom_addresses["Auto_Scroll_Levels"] + i] = 1 if i in self.auto_scroll_levels else 0 - + # data[rom_addresses["Auto_Scroll_Levels_B"] + i] = 1 if i in self.auto_scroll_levels else 0 if self.options.energy_link: # start with 1 life if Energy Link is on so that you don't deposit lives at the start of the game. diff --git a/worlds/marioland2/rom_addresses.py b/worlds/marioland2/rom_addresses.py index 1980c28a6209..1bb6cb5feea6 100644 --- a/worlds/marioland2/rom_addresses.py +++ b/worlds/marioland2/rom_addresses.py @@ -12,6 +12,7 @@ "Enable_Swim": 0x1d17, "Coinsanity_B": 0x1d86, "Auto_Scroll_Levels": 0x1f71, + "Auto_Scroll_Levels_B": 0x80134, "Starting_Lives": 0x2920, "Get_Hurt_To_Big_Mario": 0x31c7, "Get_Mushroom_A": 0x345c, From 732dc12f66f2266a0ca9f674ad4afe72b718b2f6 Mon Sep 17 00:00:00 2001 From: Alchav Date: Wed, 10 Apr 2024 23:11:25 -0400 Subject: [PATCH 046/113] Macro Zone 1 logic --- worlds/marioland2/logic.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 5528e0922a7d..4d41b175d637 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -325,13 +325,21 @@ def macro_zone_1_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Macro Zone 1") reachable_coins = 0 if has_pipe_down(state, player): - reachable_coins += 74 - if not auto_scroll: - reachable_coins += 4 + reachable_coins += 69 + if auto_scroll: + if state.has_any(["Mushroom", "Fire Flower"], player): + reachable_coins += 5 + else: + reachable_coins += 9 if state.has("Fire Flower", player): reachable_coins += 19 - elif (not auto_scroll) and state.has("Macro Zone 1 Midway Bell", player): - reachable_coins += 67 + elif state.has("Macro Zone 1 Midway Bell", player): + if auto_scroll: + reachable_coins += 16 + if state.has_any(["Mushroom", "Fire Flower"], player): + reachable_coins += 5 + else: + reachable_coins += 67 return coins <= reachable_coins From d1ff939a9c588641248e37c779241346afec6ae4 Mon Sep 17 00:00:00 2001 From: Alchav Date: Fri, 12 Apr 2024 10:37:23 -0400 Subject: [PATCH 047/113] Move space zone 2 boss logic to logic file --- worlds/marioland2/__init__.py | 12 +----------- worlds/marioland2/logic.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 5d191b3b7d0c..72705160bbbc 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -223,17 +223,7 @@ def set_rules(self): # One or the other is actually necessary for the secret exit. "Space Zone 1 - Secret Exit": lambda state: state.has_any( ["Space Physics", "Carrot"], self.player) and not is_auto_scroll(state, self.player, "Space Zone 1"), - # Without Space Physics, you must be able to take damage once to reach the bell, and again after the bell. - # If bells are not shuffled, then any one powerup will do, as you can get the bell and come back. - # Otherwise, you need the bell item from the item pool, or you need to be able to take damage twice in one - # visit. - "Space Zone 2 - Boss": lambda state: has_pipe_right(state, self.player) and (state.has( - "Space Physics", self.player) or ((not - state.multiworld.worlds[self.player].options.shuffle_midway_bells) and state.has_any(["Mushroom", - "Fire Flower", "Carrot"], self.player)) or (state.has("Mushroom", self.player) - and state.has_any(["Fire Flower", "Carrot"], self.player)) - or (state.has("Space Zone 2 Midway Bell", self.player) and state.has_any(["Mushroom", - "Fire Flower", "Carrot"], self.player))), + "Space Zone 2 - Boss": logic.space_zone_2_boss, "Space Zone 2 - Midway Bell": lambda state: state.has_any( ["Space Physics", "Space Zone 2 Midway Bell", "Mushroom", "Fire Flower", "Carrot"], self.player), diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 4d41b175d637..dc49decf91d7 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -300,6 +300,23 @@ def space_zone_1_coins(state, player, coins): or state.has_any(["Carrot", "Space Physics"], player)) +def space_zone_2_boss(state, player): + if has_pipe_right(state, player): + if state.has("Space Physics", player): + return True + if (state.has("Space Zone 2 Midway Bell", player) + or not state.multiworld.worlds[player].options.shuffle_midway_bells): + # Reaching the midway bell without space physics requires taking damage once. Reaching the end pipe from the + # midway bell also requires taking damage once. + if state.has_any(["Mushroom", "Fire Flower", "Carrot"], player): + return True + else: + # With no midway bell, you'll have to be able to take damage twice. + if state.has("Mushroom", player) and state.has_any(["Fire Flower", "Carrot"], player): + return True + return False + + def space_zone_2_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Space Zone 2") reachable_coins = 12 From 7c7c40619152a2fae3301d28bb416040783c0835 Mon Sep 17 00:00:00 2001 From: Alchav Date: Fri, 12 Apr 2024 10:37:42 -0400 Subject: [PATCH 048/113] Write second auto scroll table if not trapped --- worlds/marioland2/rom.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 10586f5055f6..f726e2da1a79 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -203,7 +203,8 @@ def generate_output(self, output_directory: str): for i in range(32): data[rom_addresses["Auto_Scroll_Levels"] + i] = 1 if i in self.auto_scroll_levels else 0 - # data[rom_addresses["Auto_Scroll_Levels_B"] + i] = 1 if i in self.auto_scroll_levels else 0 + if self.options.auto_scroll_mode not in ("trap_item", "trap_items"): + data[rom_addresses["Auto_Scroll_Levels_B"] + i] = 1 if i in self.auto_scroll_levels else 0 if self.options.energy_link: # start with 1 life if Energy Link is on so that you don't deposit lives at the start of the game. From bb1284d05815b9bbdc58bc701f6b4fcfd900f8c9 Mon Sep 17 00:00:00 2001 From: Alchav Date: Fri, 12 Apr 2024 11:07:24 -0400 Subject: [PATCH 049/113] Add item name groups --- worlds/marioland2/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 72705160bbbc..9233fe4b1b6e 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -61,7 +61,10 @@ class MarioLand2World(World): "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"} + "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 = { From 2444096e40f7f1908f6ae47e582c65778dfe82ca Mon Sep 17 00:00:00 2001 From: Alchav Date: Fri, 12 Apr 2024 13:18:23 -0400 Subject: [PATCH 050/113] Rename options and fix up some things --- worlds/marioland2/__init__.py | 34 ++++++++++++++++++++-------------- worlds/marioland2/client.py | 4 ++-- worlds/marioland2/options.py | 27 +++++++++++++++------------ worlds/marioland2/rom.py | 2 +- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 9233fe4b1b6e..f1dfd1ccb9cd 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -97,8 +97,7 @@ def generate_early(self): self.auto_scroll_levels = [] ineligible_levels = ["Mario's Castle"] if self.options.auto_scroll_mode == "always" or (self.options.accessibility == "locations" and - self.options.auto_scroll_mode in ("trap_item", - "trap_items")): + "trap" in self.options.auto_scroll_mode): ineligible_levels += ["Tree Zone 3", "Macro Zone 2", "Space Zone 1", "Turtle Zone 2", "Pumpkin Zone 2"] for i in range(32): if (level_id_to_name[i] not in ineligible_levels @@ -226,7 +225,7 @@ def set_rules(self): # One or the other is actually necessary for the secret exit. "Space Zone 1 - Secret Exit": lambda state: state.has_any( ["Space Physics", "Carrot"], self.player) and not is_auto_scroll(state, self.player, "Space Zone 1"), - "Space Zone 2 - Boss": logic.space_zone_2_boss, + "Space Zone 2 - Boss": lambda state: logic.space_zone_2_boss(state, self.player), "Space Zone 2 - Midway Bell": lambda state: state.has_any( ["Space Physics", "Space Zone 2 Midway Bell", "Mushroom", "Fire Flower", "Carrot"], self.player), @@ -409,18 +408,18 @@ def create_items(self): else: self.multiworld.push_precollected(self.create_item("Pipe Traversal")) - if self.options.auto_scroll_mode == "trap_item": + if self.options.auto_scroll_mode == "global_trap_item": item_counts["Auto Scroll"] = 1 - elif self.options.auto_scroll_mode == "trap_items": + elif self.options.auto_scroll_mode == "level_trap_items": for level, level_id in level_name_to_id.items(): if level_id in self.auto_scroll_levels: item_counts[f"Auto Scroll - {level}"] = 1 - elif self.options.auto_scroll_mode == "cancel_item": - item_counts["Auto Scroll Cancel"] = 1 - elif self.options.auto_scroll_mode == "cancel_items": + elif self.options.auto_scroll_mode == "global_cancel_item": + item_counts["Cancel Auto Scroll"] = 1 + elif self.options.auto_scroll_mode == "level_cancel_items": for level, level_id in level_name_to_id.items(): if level_id in self.auto_scroll_levels: - item_counts[f"Auto Scroll Cancel - {level}"] = 1 + item_counts[f"Cancel Auto Scroll - {level}"] = 1 for item in self.multiworld.precollected_items[self.player]: if item.name in item_counts and item_counts[item.name] > 0: @@ -454,11 +453,18 @@ def create_items(self): item_counts[double_progression_item] -= 2 item_counts[double_progression_item + " x2"] = 1 continue - 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)) + if self.options.auto_scroll_mode in ("level_trap_items", "level_cancel_items"): + 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.remove(level_name_to_id[level]) + del item_counts[auto_scroll_item] + continue + raise Exception(f"Too many items in the item pool for Super Mario Land 2 player {self.multiworld.player_name[self.player]}") + # 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) diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 4befb9ef27ec..272069b86aaa 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -199,8 +199,8 @@ async def game_watcher(self, ctx: BizHawkClientContext): auto_scroll_levels[i] = 1 if i == current_level: data_writes.append((0x02C8, [0x01], "CartRAM")) - elif ("Auto Scroll Cancel" in items_received - or f"Auto Scroll Cancel - {level_id_to_name[i]}" in items_received): + elif ("Cancel Auto Scroll" in items_received + or f"Cancel Auto Scroll - {level_id_to_name[i]}" in items_received): auto_scroll_levels[i] = 0 if i == current_level: data_writes.append((0x02C8, [0x00], "CartRAM")) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 5d37a554a321..db013db87766 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -56,9 +56,9 @@ class Coinsanity(Toggle): class CoinsanityChecks(Range): """Number of Coinsanity checks. - A higher number means more checks, and smaller coin amounts per coin item in the item pool. - If Accessibility is set to Locations, auto-scroll levels may have a lower maximum count, which may lead to this - value being limited.""" + A higher number means more checks, and smaller coin amounts per coin item in the item pool. + If Accessibility is set to Locations, auto-scroll levels may have a lower maximum count, which may lead to this + value being limited.""" default_name = "Coinsanity Checks" range_start = 31 range_end = 2599 @@ -108,17 +108,20 @@ class AutoScrollChances(NamedRange): class AutoScrollMode(Choice): """Always: Any auto scroll levels will always auto-scroll. - Trap Item: Auto scroll levels will only auto-scroll after obtaining the Auto Scroll trap item. - Trap Items: As with Trap Item, but there is a separate trap item for each auto scroll level. - Cancel Item: Auto Scroll levels will stop auto-scrolling after obtaining the Auto Scroll Cancel item. - Cancel Items: As with Cancel Item, but there is a separate cancel item for each auto scroll level. - The effects of Trap and Cancel items are permanent!""" + Global Trap Item: Auto scroll levels will only auto-scroll after obtaining the Auto Scroll trap item. + Level Trap Items: As with Trap Item, but there is a separate trap item for each auto scroll level. + Global Cancel Item: Auto Scroll levels will stop auto-scrolling after obtaining the Auto Scroll Cancel item. + Level Cancel Items: As with Cancel Item, but there is a separate cancel item for each auto scroll level. + The effects of Trap and Cancel items are permanent! If Accessibility is not set to Locations, + Traps may cause locations to become permanently unreachable. + With Trap Items or Cancel Items, the number of auto scroll levels may be limited by the available space in the item + pool.""" display_name = "Auto Scroll Mode" option_always = 0 - option_trap_item = 1 - option_trap_items = 2 - option_cancel_item = 3 - option_cancel_items = 4 + option_global_trap_item = 1 + option_level_trap_items = 2 + option_global_cancel_item = 3 + option_level_cancel_items = 4 default = 0 diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index f726e2da1a79..413b72c5c954 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -203,7 +203,7 @@ def generate_output(self, output_directory: str): for i in range(32): data[rom_addresses["Auto_Scroll_Levels"] + i] = 1 if i in self.auto_scroll_levels else 0 - if self.options.auto_scroll_mode not in ("trap_item", "trap_items"): + if "trap" not in self.options.auto_scroll_mode: data[rom_addresses["Auto_Scroll_Levels_B"] + i] = 1 if i in self.auto_scroll_levels else 0 if self.options.energy_link: From c512b5cd3f62c314c3d8c0cfe1c3204ee6dc3691 Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 13 Apr 2024 09:55:19 -0400 Subject: [PATCH 051/113] Traps or Cancel Items --- worlds/marioland2/__init__.py | 55 ++++++++++++++++++++++------------- worlds/marioland2/client.py | 26 ++++++++++------- worlds/marioland2/logic.py | 2 +- worlds/marioland2/options.py | 4 ++- worlds/marioland2/rom.py | 8 ++--- 5 files changed, 57 insertions(+), 38 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index f1dfd1ccb9cd..1830b9e11b6a 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -92,17 +92,27 @@ def __init__(self, world, player: int): def generate_early(self): if self.options.auto_scroll_chances == -1: - self.auto_scroll_levels = [19, 25, 30] + self.auto_scroll_levels = [int(i in [19, 25, 30]) for i in range(32)] else: - self.auto_scroll_levels = [] - ineligible_levels = ["Mario's Castle"] - if self.options.auto_scroll_mode == "always" or (self.options.accessibility == "locations" and - "trap" in self.options.auto_scroll_mode): - ineligible_levels += ["Tree Zone 3", "Macro Zone 2", "Space Zone 1", "Turtle Zone 2", "Pumpkin Zone 2"] - for i in range(32): - if (level_id_to_name[i] not in ineligible_levels - and self.random.randint(1, 100) < self.options.auto_scroll_chances): - self.auto_scroll_levels.append(i) + 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"] + 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 == "level_cancel_or_trap_items": + if ((self.options.accessibility == "locations" + and level_id_to_name[level] in unbeatable_scroll_levels) + or self.random.randint(0, 1)): + self.auto_scroll_levels[level] = 2 + elif (self.options.accessibility == "locations" + 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) @@ -156,7 +166,7 @@ def create_regions(self): self.max_coin_locations = {region: len(coins_coords[region]) for region in created_regions} if self.options.accessibility == "locations" or self.options.auto_scroll_mode == "always": for level in self.max_coin_locations: - if level in auto_scroll_max and level_name_to_id[level] in self.auto_scroll_levels: + if level in auto_scroll_max and self.auto_scroll_levels[level_name_to_id[level]] in (1, 3): 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): @@ -315,6 +325,10 @@ def set_rules(self): coin_rule(state, self.player, num_coins) self.multiworld.completion_condition[self.player] = lambda state: state.has("Wario Defeated", self.player) + from worlds.generic.Rules import add_item_rule + for location in self.multiworld.get_locations(self.player): + add_item_rule(location, lambda i, loc=location.name: "Auto Scroll" not in i.name or i.name.split("- ")[1] in loc) + def create_items(self): item_counts = { "Space Zone Progression": 1, @@ -410,16 +424,14 @@ def create_items(self): if self.options.auto_scroll_mode == "global_trap_item": item_counts["Auto Scroll"] = 1 - elif self.options.auto_scroll_mode == "level_trap_items": - for level, level_id in level_name_to_id.items(): - if level_id in self.auto_scroll_levels: - item_counts[f"Auto Scroll - {level}"] = 1 elif self.options.auto_scroll_mode == "global_cancel_item": item_counts["Cancel Auto Scroll"] = 1 - elif self.options.auto_scroll_mode == "level_cancel_items": - for level, level_id in level_name_to_id.items(): - if level_id in self.auto_scroll_levels: - item_counts[f"Cancel Auto Scroll - {level}"] = 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: @@ -453,10 +465,11 @@ def create_items(self): 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"): + if self.options.auto_scroll_mode in ("level_trap_items", "level_cancel_items", + "level_cancel_or_trap_items"): 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.remove(level_name_to_id[level]) + 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.multiworld.player_name[self.player]}") diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 272069b86aaa..dd49aed230a0 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -193,17 +193,21 @@ async def game_watcher(self, ctx: BizHawkClientContext): data_writes.append((0x02A0, [0], "CartRAM")) for i in range(32): - if i == 24: - continue - if "Auto Scroll" in items_received or f"Auto Scroll - {level_id_to_name[i]}" in items_received: - auto_scroll_levels[i] = 1 - if i == current_level: - data_writes.append((0x02C8, [0x01], "CartRAM")) - elif ("Cancel Auto Scroll" in items_received - or f"Cancel Auto Scroll - {level_id_to_name[i]}" in items_received): - auto_scroll_levels[i] = 0 - if i == current_level: - data_writes.append((0x02C8, [0x00], "CartRAM")) + if auto_scroll_levels[i] == 3: + if "Auto Scroll" in items_received or f"Auto Scroll - {level_id_to_name[i]}" in items_received: + auto_scroll_levels[i] = 1 + if i == current_level: + data_writes.append((0x02C8, [0x01], "CartRAM")) + else: + auto_scroll_levels[i] = 0 + elif auto_scroll_levels[i] == 2: + if ("Cancel Auto Scroll" in items_received + or f"Cancel Auto Scroll - {level_id_to_name[i]}" in items_received): + auto_scroll_levels[i] = 0 + if i == current_level: + data_writes.append((0x02C8, [0x00], "CartRAM")) + else: + auto_scroll_levels[i] = 1 data_writes.append((rom_addresses["Auto_Scroll_Levels"], auto_scroll_levels, "ROM")) success = await guarded_write(ctx.bizhawk_ctx, data_writes, [(0x0848, level_data, "CartRAM"), diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index dc49decf91d7..a9b307d7b93f 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -5,7 +5,7 @@ def is_auto_scroll(state, player, level): level_id = level_name_to_id[level] if state.has_any(["Cancel Auto Scroll", f"Cancel Auto Scroll - {level}"], player): return False - return level_id in state.multiworld.worlds[player].auto_scroll_levels + return state.multiworld.worlds[player].auto_scroll_levels[level_id] > 0 def has_pipe_right(state, player): diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index db013db87766..0a8019ab965b 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -112,9 +112,10 @@ class AutoScrollMode(Choice): Level Trap Items: As with Trap Item, but there is a separate trap item for each auto scroll level. Global Cancel Item: Auto Scroll levels will stop auto-scrolling after obtaining the Auto Scroll Cancel item. Level Cancel Items: As with Cancel Item, but there is a separate cancel item for each auto scroll level. + Level Cancel Or Trap Items: Levels will randomly have Auto Scroll Trap or Auto Scroll Cancel items. The effects of Trap and Cancel items are permanent! If Accessibility is not set to Locations, Traps may cause locations to become permanently unreachable. - With Trap Items or Cancel Items, the number of auto scroll levels may be limited by the available space in the item + With individual level items, the number of auto scroll levels may be limited by the available space in the item pool.""" display_name = "Auto Scroll Mode" option_always = 0 @@ -122,6 +123,7 @@ class AutoScrollMode(Choice): option_level_trap_items = 2 option_global_cancel_item = 3 option_level_cancel_items = 4 + option_level_cancel_or_trap_items = 5 default = 0 diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 413b72c5c954..95b330438eec 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -201,10 +201,10 @@ def generate_output(self, output_directory: str): data[rom_addresses["Difficulty_Mode"]] = self.options.difficulty_mode.value data[rom_addresses["Coin_Mode"]] = self.options.shuffle_golden_coins.value - for i in range(32): - data[rom_addresses["Auto_Scroll_Levels"] + i] = 1 if i in self.auto_scroll_levels else 0 - if "trap" not in self.options.auto_scroll_mode: - data[rom_addresses["Auto_Scroll_Levels_B"] + i] = 1 if i in self.auto_scroll_levels else 0 + for level, i in enumerate(self.auto_scroll_levels): + # We set 0 if no auto scroll or auto scroll trap, so it defaults to no auto scroll. 1 if always or cancel items. + data[rom_addresses["Auto_Scroll_Levels"] + level] = max(0, i - 1) + data[rom_addresses["Auto_Scroll_Levels_B"] + level] = i if self.options.energy_link: # start with 1 life if Energy Link is on so that you don't deposit lives at the start of the game. From 21edc9f58da721f7c96e38d8607b26bf12bcd0ed Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 13 Apr 2024 12:18:53 -0400 Subject: [PATCH 052/113] Adjust Mushroom Zone logic --- worlds/marioland2/logic.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index a9b307d7b93f..1d3a26bb4096 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -30,7 +30,10 @@ def has_level_progression(state, item, player, count=1): def mushroom_zone_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Mushroom Zone") - reachable_coins = 40 + reachable_coins = 38 + if state.has_any(["Mushroom", "Fire Flower"], player) or not auto_scroll: + # Was able to get all but 1, being lenient. + reachable_coins += 2 if has_pipe_down(state, player): # There's 24 in each of the underground sections. # The first one requires missing some question mark blocks if auto scrolling (the last +4). From a1ec2cdd0b4e4a927b8a7bdb61f9209665e64eef Mon Sep 17 00:00:00 2001 From: Alchav Date: Tue, 16 Apr 2024 10:24:18 -0400 Subject: [PATCH 053/113] Begin sprite randomizer rewrite --- worlds/marioland2/__init__.py | 13 +- worlds/marioland2/rom.py | 54 +- worlds/marioland2/sprite_randomizer.py | 101 +++ worlds/marioland2/sprites.py | 1016 ++++++++++++++++++++++++ 4 files changed, 1174 insertions(+), 10 deletions(-) create mode 100644 worlds/marioland2/sprite_randomizer.py create mode 100644 worlds/marioland2/sprites.py diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 1830b9e11b6a..81ad71a0a1c0 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -1,7 +1,7 @@ import base64 - import Utils import settings +from copy import deepcopy from worlds.AutoWorld import World, WebWorld from BaseClasses import Region, Location, Item, ItemClassification, Tutorial @@ -12,6 +12,8 @@ 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_sprites from .logic import has_pipe_up, has_pipe_down, has_pipe_left, has_pipe_right, has_level_progression, is_auto_scroll from . import logic @@ -88,9 +90,14 @@ def __init__(self, world, player: int): 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) + randomize_sprites(self.sprite_data, self.random) + self.sprite_data["Mario's Castle"][35]["sprite"] = "Midway Bell" + if self.options.auto_scroll_chances == -1: self.auto_scroll_levels = [int(i in [19, 25, 30]) for i in range(32)] else: @@ -325,10 +332,6 @@ def set_rules(self): coin_rule(state, self.player, num_coins) self.multiworld.completion_condition[self.player] = lambda state: state.has("Wario Defeated", self.player) - from worlds.generic.Rules import add_item_rule - for location in self.multiworld.get_locations(self.player): - add_item_rule(location, lambda i, loc=location.name: "Auto Scroll" not in i.name or i.name.split("- ")[1] in loc) - def create_items(self): item_counts = { "Space Zone Progression": 1, diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 95b330438eec..06863cef8370 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -9,6 +9,7 @@ from settings import get_settings from .rom_addresses import rom_addresses +from .sprites import sprite_name_to_id # Enemy and Platform randomizer ported directly from SML2R # https://github.com/slashinfty/sml2r-node/blob/862128c73d336d6cbfbf6290c09f3eff103688e8/src/index.ts#L284 @@ -37,8 +38,29 @@ def randomize_sprite(data, random, arr, i): selected_sprite = sprite_insert(data[i], data[i + 1], random.choice(arr)) copy_sprite(data, selected_sprite, i) - +from .locations import level_id_to_name +from .sprites import sprite_id_to_name def randomize_enemies(data, random): + i = 0xe077 + level = 0 + d = {} + d[level_id_to_name[level]] = [] + while True: + if data[i] == 0xFF: + level += 1 + i += 1 + if level > len(level_id_to_name) - 1: + break + d[level_id_to_name[level]] = [] + sprite = data[i:i+3] + screen = sprite[0] & 0b00001111 + x = sprite[1] & 0b00011111 + y = sprite[2] & 0b00011111 + sprite_id = sprite_id_to_name[((sprite[0] & 0b11100000) >> 2) | ((sprite[0] & 0b00010000) << 2) | ((sprite[1] & 0b11100000) >> 5)] + ezmode = sprite[2] & 0b11100000 + d[level_id_to_name[level]].append({"screen": screen, "sprite_id": sprite_id, "x": x, "y": y, "misc": ezmode}) + i += 3 + breakpoint() level_list = [ {"enemies": [0x01, 0x08, 0x09, 0x3A], "start": 0xE077, "end": 0xE0BC}, # lv00 {"enemies": [0x01, 0x08, 0x09, 0x3A], "start": 0xE955, "end": 0xE99D}, # lv17 @@ -166,10 +188,32 @@ def generate_output(self, output_directory: str): random = self.random - if self.options.randomize_enemies: - randomize_enemies(data, random) - if self.options.randomize_platforms: - randomize_platforms(data, random) + # if self.options.randomize_enemies: + # randomize_enemies(data, random) + # if self.options.randomize_platforms: + # randomize_platforms(data, random) + + data[0x4F012] = 0x5D # Midway bell Mario's Castle + + if self.options.coinsanity: + # Add platform to return to start of Pumpkin Zone Secret Course 1 + data[0x258B6] = 0x3B + data[0x258F8] = 0x7a + data[0x2594D] = 0x67 + data[0x259A8] = 0x68 + data[0x259A9] = 0x60 + + i = 0xe077 + for level, sprites in self.sprite_data.items(): + for sprite_data in sprites: + sprite_id = sprite_name_to_id[sprite_data["sprite"]] + data[i] = ((sprite_id & 0b01000000) >> 2) | ((sprite_id & 0b00111000) << 2) | sprite_data["screen"] + data[i + 1] = ((sprite_id & 0b00000111) << 5) | sprite_data["x"] + data[i + 2] = sprite_data["misc"] | sprite_data["y"] + i += 3 + data[i] = 255 + i += 1 + if self.options.randomize_music: randomize_music(data, random) diff --git a/worlds/marioland2/sprite_randomizer.py b/worlds/marioland2/sprite_randomizer.py new file mode 100644 index 000000000000..9bf054969081 --- /dev/null +++ b/worlds/marioland2/sprite_randomizer.py @@ -0,0 +1,101 @@ + + +def randomize_sprites(sprite_data, random): + for level, level_sprite_data in sprite_data.items(): + shuffle = () + if level in ("Mushroom Zone", "Macro Zone 4"): + shuffle = ("Koopa Troopa", "Goomba", "Paragoomba (Vertical)", "Paragoomba (Diagonal)") + elif level in ("Scenic Course", "Pumpkin Zone Secret Course 1"): + shuffle = ("Goomba", "Paragoomba (Vertical)", "Paragoomba (Diagonal)") + elif level == "Tree Zone 1": + shuffle = ("Money Bag/Bopping Toady", "Ragumo/Aqua Kuribo", "Pencil/Spikey", "Kyotonbo") + elif level == "Tree Zone 2": + shuffle = ("Noko Bombette/Bear", "No 48/Mogyo") + elif level == "Tree Zone 4": + shuffle = ("Runaway Heart Block/Bibi", "Neiji/Buichi", "Piranha Plant (Downward)/Grubby", + "Spinning Platform (Horizontal)/Skeleton Bee", "Spinning Spike (Horizontal)/Unera") + elif level == "Tree Zone 3": + shuffle = ("Battle Beetle", "Be", "Ant") + elif level == "Tree Zone 5": + shuffle = ("Paragoomba (Diagonal)", "Dondon", "Paragoomba (Vertical)") + elif level == "Pumpkin Zone 2": + shuffle = ("Boo/Bomubomu", "Kyororo", "Honebon/F Boy", "Karakara", "Star (Vertical)/Blurp (Horizontal)", + "Star (Horizontal)/Blurp (Vertical)") + elif level == "Pumpkin Zone 3": + shuffle = ("Boo/Bomubomu", "Unibo/Terekuribo") + elif level == "Mario Zone 1": + shuffle = ("Koopa Troopa", "Neiji/Buichi", "Tatenoko") + elif level == "Mario Zone 2": + shuffle = ("Paragoomba (Diagonal)", "Goomba", "Paragoomba (Vertical)", "Noko Bombette/Bear", + "Boo/Bomubomu") + elif level == "Turtle Zone 1": + shuffle = ("Horizontal Blurp", "Shark", "Cheep Cheep (Vertical)", "Paragoomba (Diagonal)", "Goomba", + "Spiny Cheep Cheep", "Paragoomba (Vertical)", + "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)") + elif level == "Hippo Zone": + shuffle = ("Horizontal Blurp", "Dondon", "Unibo/Terekuribo", "Toriuo") + elif level == "Space Zone 2": + shuffle = ("Tosenbo/Pikku", "Star (Vertical)/Blurp (Horizontal)", "Star (Horizontal)/Blurp (Vertical)") + elif level == "Macro Zone 1": + shuffle = ("Kyotonbo", "Goronto", "Dokanto", "Chikunto") + elif level == "Macro Zone 2": + shuffle = ("Cheep Cheep (Vertical)", "Battle Beetle", "Be", + "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)", "Ant") + elif level == "Macro Zone 3": + shuffle = ("Koopa Troopa", "Paragoomba (Diagonal)", "Goomba", "Be", "Paragoomba (Vertical)", + "Honebon/F Boy") + elif level == "Pumpkin Zone Secret Course 2": + shuffle = ("Koopa Troopa", "Goomba") + for sprite in level_sprite_data: + if level == "Pumpkin Zone 1": + if sprite["sprite"] == "Falling Spike": + shuffle = ("Boo/Bomubomu", "Falling Spike", "Kurokyura/Jack-in-the-Box", "Masked Ghoul/Bullet Bill") + elif sprite["sprite"] == "Falling Spike on Chain": + shuffle = ("Boo/Bomubomu", "Falling Spike on Chain", "Kurokyura/Jack-in-the-Box", + "Masked Ghoul/Bullet Bill") + else: + shuffle = ("Boo/Bomubomu", "Kurokyura/Jack-in-the-Box", "Masked Ghoul/Bullet Bill") + elif level == "Pumpkin Zone 4": + if sprite["sprite"] == "Falling Spike on Chain": + shuffle = ("Boo/Bomubomu", "Falling Spike on Chain", "Masked Ghoul/Bullet Bill", "Rerere/Poro", + "Tosenbo/Pikku") + else: + shuffle = ("Boo/Bomubomu", "Masked Ghoul/Bullet Bill", "Rerere/Poro", "Tosenbo/Pikku") + elif level == "Mario Zone 3": + if sprite["sprite"] == "Claw Grabber": + shuffle = ("Koopa Troopa", "Diagonal Ball on Chain", "Kiddokatto", "Claw Grabber", + "Masked Ghoul/Bullet Bill") + else: + shuffle = ("Koopa Troopa", "Diagonal Ball on Chain", "Kiddokatto", "Masked Ghoul/Bullet Bill") + elif level == "Mario Zone 4": + if sprite["sprite"] == "Spinning Spike/Tamara": + shuffle = ("Goomba", "Spinning Spike/Tamara", "Boo/Bomubomu", "Masked Ghoul/Bullet Bill") + elif sprite["sprite"] == "Moving Saw (Floor)": + shuffle = ("Goomba", "Moving Saw (Floor)", "Boo/Bomubomu", "Masked Ghoul/Bullet Bill") + else: + shuffle = ("Goomba", "Boo/Bomubomu", "Masked Ghoul/Bullet Bill") + elif level == "Turtle Zone 3": + if sprite["sprite"] == "Pencil/Spikey": + shuffle = ("Koopa Troopa", "Paragoomba (Diagonal)", "Ragumo/Aqua Kuribo", "Pencil/Spikey", + "Paragoomba (Vertical)", "Honebon/F Boy") + else: + shuffle = ("Koopa Troopa", "Paragoomba (Diagonal)", "Ragumo/Aqua Kuribo", + "Paragoomba (Vertical)", "Honebon/F Boy") + elif level == "Space Zone 1": + if sprite["sprite"] == "Boo/Bomubomu": + shuffle = ("Boo/Bomubomu", "No 48/Mogyo") + else: + shuffle = ("Boo/Bomubomu", "No 48/Mogyo", "Rerere/Poro") + elif level == "Mario's Castle": + if sprite["sprite"] in ("Fire Pakkun Zo (Large)", "Fire Pakkun Zo (Left)"): + shuffle = ("Fire Pakkun Zo (Large)", "Fire Pakkun Zo (Left)") + else: + shuffle = ("Spike Ball (Large)", "Spike Ball (Small)") + if sprite["sprite"] in ("Piranha Plant", "Fire Piranha Plant"): + shuffle = ("Piranha Plant", "Fire Piranha Plant") + if sprite["sprite"] in shuffle: + old_sprite = sprite["sprite"] + sprite["sprite"] = random.choice(shuffle) + print(f"Level: {level} - {old_sprite} changed to {sprite['sprite']}") + elif level == "Mario's Castle" and sprite["sprite"] == "Karamenbo" and not random.randint(0, 9): + sprite["y"] += 1 diff --git a/worlds/marioland2/sprites.py b/worlds/marioland2/sprites.py new file mode 100644 index 000000000000..33a14731295e --- /dev/null +++ b/worlds/marioland2/sprites.py @@ -0,0 +1,1016 @@ +sprite_name_to_id = { + "Ant": 93, "Ragumo/Aqua Kuribo": 32, "Battle Beetle": 51, "Be": 52, "Noko Bombette/Bear": 68, + "Runaway Heart Block/Bibi": 53, "Star (Vertical)/Blurp (Horizontal)": 94, + "Star (Horizontal)/Blurp (Vertical)": 95, "Boo/Bomubomu": 77, + "Money Bag/Bopping Toady": 31, "Neiji/Buichi": 64, "Masked Ghoul/Bullet Bill": 83, + "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)": 61, "Cheep Cheep (Vertical)": 7, + "Chikunto": 39, "Dokanto": 37, "Dondon": 57, "Honebon/F Boy": 85, "Fire Pakkun Zo (Large)": 104, + "Fire Pakkun Zo (Left)": 105, "Fire Pakkun Zo (Right)": 106, "Fire Piranha Plant": 13, "Floating Face": 100, + "Genkottsu (1.5 Tiles)": 99, "Genkottsu (2 Tiles)": 98, "Goomba": 9, "Goronto": 35, + "Piranha Plant (Downward)/Grubby": 66, "Kurokyura/Jack-in-the-Box": 81, + "Karakara": 86, "Karamenbo": 102, "Kiddokatto": 72, "Koopa Troopa": 1, "Kyororo": 84, "Kyotonbo": 34, + "No 48/Mogyo": 88, "Paragoomba (Diagonal)": 8, "Paragoomba (Vertical)": 58, "Tosenbo/Pikku": 92, + "Piranha Plant": 12, "Rerere/Poro": 90, "Shark": 6, + "Spinning Platform (Horizontal)/Skeleton Bee": 62, "Pencil/Spikey": 33, + "Spiny Cheep Cheep": 11, "Spinning Spike/Tamara": 67, "Tatenoko": 75, "Unibo/Terekuribo": 87, + "Toriuo": 91, "Spinning Spike (Horizontal)/Unera": 65, "Claw Grabber": 73, + "Diagonal Ball on Chain": 71, "Falling Spike": 78, "Falling Spike on Chain": 79, "Spinning Spike (Vertical)": 80, + "Spike Ball (Large)": 110, "Spike Ball (Small)": 111, "Moving Saw (Ceiling)": 74, "Moving Saw (Floor)": 76, + "Moving Platform (Small, Vertical)": 40, "Moving Platform (Large, Vertical)": 41, + "Moving Platform (Small, Horizontal)": 42, "Moving Platform (Large, Horizontal)": 43, + "Moving Platform (Large, Diagonal)": 45, "Falling Platform": 46, "Rising Platform": 47, + "Rotating Platform (Small)": 48, "Owl Platform (Vertical)": 55, "Cloud Platform (Horizontal)": 56, + "Spinning Platform (Vertical)": 63, "Falling Bone Platform": 96, "Rising Bone Platform": 97, "Skull Platform": 103, + "Propeller Platform": 107, "Heart": 15, "Mushroom": 27, "Flower": 28, "Carrot": 29, "Star": 30, + "Mushroom Block": 17, "Flower Block": 18, "Carrot Block": 19, "Star Block": 20, "Heart Block": 21, + "Money Bag Block": 25, "Bubble": 24, "Midway Bell": 23, "Bonus Bell": 22, "Horizontal Blurp": 5, + "Big Diagonal Moving Platform": 44} + +sprite_id_to_name = {a: b for b, a in sprite_name_to_id.items()} + +level_sprites = { + "Mushroom Zone": [ + {"screen": 1, "sprite": "Goomba", "x": 27, "y": 30, "misc": 32}, + {"screen": 2, "sprite": "Koopa Troopa", "x": 9, "y": 24, "misc": 32}, + {"screen": 2, "sprite": "Mushroom Block", "x": 21, "y": 23, "misc": 32}, + {"screen": 4, "sprite": "Goomba", "x": 7, "y": 26, "misc": 32}, + {"screen": 5, "sprite": "Koopa Troopa", "x": 11, "y": 18, "misc": 32}, + {"screen": 6, "sprite": "Paragoomba (Diagonal)", "x": 19, "y": 22, "misc": 160}, + {"screen": 8, "sprite": "Piranha Plant", "x": 6, "y": 30, "misc": 160}, + {"screen": 8, "sprite": "Piranha Plant", "x": 22, "y": 30, "misc": 160}, + {"screen": 9, "sprite": "Heart Block", "x": 15, "y": 23, "misc": 32}, + {"screen": 9, "sprite": "Star Block", "x": 17, "y": 23, "misc": 32}, + {"screen": 10, "sprite": "Goomba", "x": 5, "y": 30, "misc": 32}, + {"screen": 10, "sprite": "Goomba", "x": 17, "y": 30, "misc": 32}, + {"screen": 10, "sprite": "Midway Bell", "x": 29, "y": 20, "misc": 32}, + {"screen": 11, "sprite": "Goomba", "x": 1, "y": 30, "misc": 32}, + {"screen": 11, "sprite": "Koopa Troopa", "x": 13, "y": 24, "misc": 32}, + {"screen": 11, "sprite": "Koopa Troopa", "x": 20, "y": 24, "misc": 32}, + {"screen": 11, "sprite": "Flower Block", "x": 25, "y": 23, "misc": 32}, + {"screen": 12, "sprite": "Heart", "x": 15, "y": 30, "misc": 32}, + {"screen": 13, "sprite": "Goomba", "x": 3, "y": 30, "misc": 32}, + {"screen": 13, "sprite": "Piranha Plant", "x": 16, "y": 30, "misc": 32}, + {"screen": 14, "sprite": "Goomba", "x": 11, "y": 22, "misc": 32}, + {"screen": 14, "sprite": "Koopa Troopa", "x": 20, "y": 22, "misc": 160}, + {"screen": 15, "sprite": "Bonus Bell", "x": 21, "y": 13, "misc": 32}], + "Tree Zone 1": [ + {"screen": 1, "sprite": "Star Block", "x": 17, "y": 21, "misc": 32}, + {"screen": 2, "sprite": "Ragumo/Aqua Kuribo", "x": 5, "y": 30, "misc": 32}, + {"screen": 2, "sprite": "Heart", "x": 19, "y": 30, "misc": 0}, + {"screen": 2, "sprite": "Ragumo/Aqua Kuribo", "x": 23, "y": 30, "misc": 32}, + {"screen": 3, "sprite": "Money Bag/Bopping Toady", "x": 1, "y": 30, "misc": 160}, + {"screen": 3, "sprite": "Money Bag/Bopping Toady", "x": 20, "y": 24, "misc": 160}, + {"screen": 4, "sprite": "Money Bag/Bopping Toady", "x": 9, "y": 24, "misc": 32}, + {"screen": 4, "sprite": "Heart", "x": 13, "y": 30, "misc": 0}, + {"screen": 4, "sprite": "Money Bag/Bopping Toady", "x": 31, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Pencil/Spikey", "x": 3, "y": 24, "misc": 160}, + {"screen": 5, "sprite": "Kyotonbo", "x": 21, "y": 22, "misc": 160}, + {"screen": 6, "sprite": "Money Bag/Bopping Toady", "x": 11, "y": 24, "misc": 160}, + {"screen": 6, "sprite": "Money Bag/Bopping Toady", "x": 23, "y": 30, "misc": 32}, + {"screen": 7, "sprite": "Mushroom Block", "x": 15, "y": 23, "misc": 32}, + {"screen": 7, "sprite": "Pencil/Spikey", "x": 28, "y": 24, "misc": 160}, + {"screen": 7, "sprite": "Heart Block", "x": 29, "y": 5, "misc": 32}, + {"screen": 8, "sprite": "Midway Bell", "x": 13, "y": 22, "misc": 32}, + {"screen": 8, "sprite": "Money Bag/Bopping Toady", "x": 29, "y": 24, "misc": 32}, + {"screen": 9, "sprite": "Ragumo/Aqua Kuribo", "x": 10, "y": 30, "misc": 32}, + {"screen": 9, "sprite": "Kyotonbo", "x": 15, "y": 10, "misc": 160}, + {"screen": 9, "sprite": "Mushroom Block", "x": 17, "y": 23, "misc": 32}, + {"screen": 10, "sprite": "Kyotonbo", "x": 3, "y": 10, "misc": 160}, + {"screen": 10, "sprite": "Pencil/Spikey", "x": 19, "y": 24, "misc": 160}, + {"screen": 10, "sprite": "Star Block", "x": 25, "y": 21, "misc": 32}, + {"screen": 11, "sprite": "Money Bag/Bopping Toady", "x": 7, "y": 24, "misc": 32}, + {"screen": 11, "sprite": "Money Bag/Bopping Toady", "x": 27, "y": 24, "misc": 32}, + {"screen": 11, "sprite": "Money Bag/Bopping Toady", "x": 31, "y": 24, "misc": 32}, + {"screen": 12, "sprite": "Money Bag/Bopping Toady", "x": 15, "y": 24, "misc": 32}, + {"screen": 13, "sprite": "Ragumo/Aqua Kuribo", "x": 3, "y": 30, "misc": 32}, + {"screen": 13, "sprite": "Money Bag/Bopping Toady", "x": 23, "y": 24, "misc": 32}, + {"screen": 14, "sprite": "Money Bag/Bopping Toady", "x": 15, "y": 30, "misc": 160}, + {"screen": 14, "sprite": "Kyotonbo", "x": 31, "y": 24, "misc": 160}, + {"screen": 15, "sprite": "Bonus Bell", "x": 19, "y": 13, "misc": 32}, + {"screen": 15, "sprite": "Heart Block", "x": 25, "y": 15, "misc": 32}], + "Tree Zone 2": [ + {"screen": 2, "sprite": "No 48/Mogyo", "x": 9, "y": 30, "misc": 0}, + {"screen": 2, "sprite": "Mushroom Block", "x": 23, "y": 19, "misc": 0}, + {"screen": 2, "sprite": "No 48/Mogyo", "x": 27, "y": 30, "misc": 0}, + {"screen": 3, "sprite": "Mushroom Block", "x": 7, "y": 23, "misc": 32}, + {"screen": 3, "sprite": "No 48/Mogyo", "x": 15, "y": 28, "misc": 160}, + {"screen": 3, "sprite": "Heart Block", "x": 27, "y": 17, "misc": 0}, + {"screen": 4, "sprite": "No 48/Mogyo", "x": 3, "y": 30, "misc": 32}, + {"screen": 4, "sprite": "No 48/Mogyo", "x": 17, "y": 28, "misc": 160}, + {"screen": 5, "sprite": "Noko Bombette/Bear", "x": 1, "y": 30, "misc": 128}, + {"screen": 5, "sprite": "No 48/Mogyo", "x": 7, "y": 28, "misc": 32}, + {"screen": 6, "sprite": "Falling Platform", "x": 22, "y": 22, "misc": 0}, + {"screen": 7, "sprite": "Midway Bell", "x": 3, "y": 14, "misc": 0}, + {"screen": 7, "sprite": "No 48/Mogyo", "x": 15, "y": 30, "misc": 0}, + {"screen": 7, "sprite": "No 48/Mogyo", "x": 17, "y": 8, "misc": 32}, + {"screen": 7, "sprite": "Money Bag Block", "x": 19, "y": 19, "misc": 32}, + {"screen": 7, "sprite": "No 48/Mogyo", "x": 19, "y": 0, "misc": 32}, + {"screen": 8, "sprite": "Mushroom Block", "x": 5, "y": 13, "misc": 0}, + {"screen": 8, "sprite": "Noko Bombette/Bear", "x": 11, "y": 24, "misc": 0}, + {"screen": 8, "sprite": "Carrot Block", "x": 17, "y": 21, "misc": 64}, + {"screen": 9, "sprite": "Noko Bombette/Bear", "x": 3, "y": 30, "misc": 64}, + {"screen": 9, "sprite": "Noko Bombette/Bear", "x": 23, "y": 30, "misc": 64}, + {"screen": 10, "sprite": "Noko Bombette/Bear", "x": 11, "y": 30, "misc": 64}, + {"screen": 10, "sprite": "Noko Bombette/Bear", "x": 31, "y": 30, "misc": 64}, + {"screen": 11, "sprite": "Money Bag Block", "x": 5, "y": 19, "misc": 0}, + {"screen": 11, "sprite": "Mushroom Block", "x": 13, "y": 5, "misc": 0}, + {"screen": 11, "sprite": "Heart", "x": 19, "y": 24, "misc": 64}, + {"screen": 13, "sprite": "Noko Bombette/Bear", "x": 3, "y": 28, "misc": 192}, + {"screen": 13, "sprite": "Noko Bombette/Bear", "x": 5, "y": 16, "misc": 0}, + {"screen": 13, "sprite": "Noko Bombette/Bear", "x": 13, "y": 28, "misc": 192}, + {"screen": 13, "sprite": "Heart", "x": 27, "y": 8, "misc": 32}, + {"screen": 14, "sprite": "Bonus Bell", "x": 21, "y": 15, "misc": 32}], + "Tree Zone 4": [ + {"screen": 1, "sprite": "Spinning Spike (Horizontal)/Unera", "x": 11, "y": 26, "misc": 160}, + {"screen": 1, "sprite": "Runaway Heart Block/Bibi", "x": 20, "y": 24, "misc": 0}, + {"screen": 1, "sprite": "Flower Block", "x": 25, "y": 21, "misc": 32}, + {"screen": 2, "sprite": "Spinning Platform (Horizontal)/Skeleton Bee", "x": 16, "y": 24, "misc": 128}, + {"screen": 3, "sprite": "Neiji/Buichi", "x": 18, "y": 18, "misc": 0}, + {"screen": 3, "sprite": "Spinning Spike (Horizontal)/Unera", "x": 28, "y": 30, "misc": 0}, + {"screen": 4, "sprite": "Neiji/Buichi", "x": 4, "y": 18, "misc": 0}, + {"screen": 4, "sprite": "Spinning Spike (Horizontal)/Unera", "x": 12, "y": 30, "misc": 0}, + {"screen": 4, "sprite": "Neiji/Buichi", "x": 22, "y": 18, "misc": 0}, + {"screen": 4, "sprite": "Spinning Spike (Horizontal)/Unera", "x": 28, "y": 30, "misc": 0}, + {"screen": 5, "sprite": "Neiji/Buichi", "x": 8, "y": 18, "misc": 128}, + {"screen": 6, "sprite": "Runaway Heart Block/Bibi", "x": 12, "y": 24, "misc": 0}, + {"screen": 6, "sprite": "Piranha Plant (Downward)/Grubby", "x": 13, "y": 12, "misc": 160}, + {"screen": 6, "sprite": "Mushroom Block", "x": 19, "y": 17, "misc": 32}, + {"screen": 6, "sprite": "Piranha Plant (Downward)/Grubby", "x": 23, "y": 28, "misc": 128}, + {"screen": 6, "sprite": "Piranha Plant (Downward)/Grubby", "x": 25, "y": 18, "misc": 160}, + {"screen": 6, "sprite": "Piranha Plant (Downward)/Grubby", "x": 31, "y": 24, "misc": 160}, + {"screen": 7, "sprite": "Spinning Platform (Horizontal)/Skeleton Bee", "x": 2, "y": 24, "misc": 0}, + {"screen": 7, "sprite": "Runaway Heart Block/Bibi", "x": 28, "y": 24, "misc": 128}, + {"screen": 8, "sprite": "Spinning Spike (Horizontal)/Unera", "x": 1, "y": 12, "misc": 32}, + {"screen": 8, "sprite": "Piranha Plant (Downward)/Grubby", "x": 5, "y": 28, "misc": 0}, + {"screen": 8, "sprite": "Spinning Spike (Horizontal)/Unera", "x": 5, "y": 18, "misc": 32}, + {"screen": 8, "sprite": "Runaway Heart Block/Bibi", "x": 14, "y": 24, "misc": 0}, + {"screen": 8, "sprite": "Star Block", "x": 17, "y": 11, "misc": 32}, + {"screen": 8, "sprite": "Spinning Spike (Horizontal)/Unera", "x": 19, "y": 24, "misc": 32}, + {"screen": 9, "sprite": "Star Block", "x": 17, "y": 19, "misc": 0}, + {"screen": 9, "sprite": "Piranha Plant (Downward)/Grubby", "x": 21, "y": 20, "misc": 0}, + {"screen": 9, "sprite": "Spinning Spike/Tamara", "x": 23, "y": 27, "misc": 0}, + {"screen": 10, "sprite": "Midway Bell", "x": 0, "y": 18, "misc": 0}, + {"screen": 10, "sprite": "Spinning Spike/Tamara", "x": 5, "y": 27, "misc": 0}, + {"screen": 10, "sprite": "Piranha Plant (Downward)/Grubby", "x": 11, "y": 20, "misc": 0}, + {"screen": 10, "sprite": "Spinning Spike/Tamara", "x": 17, "y": 27, "misc": 0}, + {"screen": 10, "sprite": "Mushroom Block", "x": 23, "y": 19, "misc": 0}, + {"screen": 12, "sprite": "Runaway Heart Block/Bibi", "x": 2, "y": 24, "misc": 32}, + {"screen": 12, "sprite": "Spinning Platform (Horizontal)/Skeleton Bee", "x": 28, "y": 24, "misc": 32}, + {"screen": 13, "sprite": "Bonus Bell", "x": 21, "y": 17, "misc": 32}], + "Tree Zone 3": [ + {"screen": 0, "sprite": "Moving Platform (Large, Vertical)", "x": 19, "y": 10, "misc": 32}, + {"screen": 0, "sprite": "Carrot Block", "x": 25, "y": 25, "misc": 0}, + {"screen": 0, "sprite": "Ant", "x": 28, "y": 18, "misc": 32}, + {"screen": 1, "sprite": "Ant", "x": 0, "y": 22, "misc": 64}, + {"screen": 1, "sprite": "Battle Beetle", "x": 10, "y": 24, "misc": 64}, + {"screen": 1, "sprite": "Moving Platform (Large, Diagonal)", "x": 11, "y": 22, "misc": 32}, + {"screen": 1, "sprite": "Fire Piranha Plant", "x": 18, "y": 20, "misc": 128}, + {"screen": 1, "sprite": "Cheep Cheep (Vertical)", "x": 18, "y": 21, "misc": 0}, + {"screen": 1, "sprite": "Mushroom Block", "x": 21, "y": 15, "misc": 64}, + {"screen": 1, "sprite": "Heart Block", "x": 21, "y": 7, "misc": 64}, + {"screen": 1, "sprite": "Be", "x": 30, "y": 26, "misc": 32}, + {"screen": 2, "sprite": "Piranha Plant", "x": 4, "y": 24, "misc": 192}, + {"screen": 2, "sprite": "Be", "x": 14, "y": 26, "misc": 32}, + {"screen": 2, "sprite": "Fire Piranha Plant", "x": 20, "y": 24, "misc": 192}, + {"screen": 2, "sprite": "Falling Platform", "x": 24, "y": 27, "misc": 0}, + {"screen": 2, "sprite": "Heart Block", "x": 25, "y": 19, "misc": 0}, + {"screen": 3, "sprite": "Falling Platform", "x": 0, "y": 25, "misc": 0}, + {"screen": 3, "sprite": "Falling Platform", "x": 0, "y": 31, "misc": 32}, + {"screen": 3, "sprite": "Cheep Cheep (Vertical)", "x": 4, "y": 15, "misc": 64}, + {"screen": 3, "sprite": "Falling Platform", "x": 8, "y": 31, "misc": 32}, + {"screen": 3, "sprite": "Ant", "x": 20, "y": 30, "misc": 0}, + {"screen": 3, "sprite": "Heart Block", "x": 27, "y": 17, "misc": 32}, + {"screen": 3, "sprite": "Heart Block", "x": 29, "y": 5, "misc": 32}, + {"screen": 4, "sprite": "Fire Piranha Plant", "x": 4, "y": 22, "misc": 192}, + {"screen": 4, "sprite": "Moving Platform (Small, Horizontal)", "x": 8, "y": 19, "misc": 0}, + {"screen": 4, "sprite": "Ant", "x": 12, "y": 20, "misc": 64}, + {"screen": 4, "sprite": "Moving Platform (Small, Vertical)", "x": 14, "y": 2, "misc": 64}, + {"screen": 4, "sprite": "Bonus Bell", "x": 21, "y": 17, "misc": 0}, + {"screen": 4, "sprite": "Money Bag Block", "x": 27, "y": 15, "misc": 32}, + {"screen": 4, "sprite": "Heart Block", "x": 27, "y": 17, "misc": 64}], + "Tree Zone 5": [ + {"screen": 1, "sprite": "Mushroom Block", "x": 15, "y": 17, "misc": 64}, + {"screen": 1, "sprite": "Rotating Platform (Small)", "x": 15, "y": 19, "misc": 64}, + {"screen": 2, "sprite": "Paragoomba (Vertical)", "x": 2, "y": 26, "misc": 192}, + {"screen": 2, "sprite": "Paragoomba (Vertical)", "x": 16, "y": 26, "misc": 192}, + {"screen": 2, "sprite": "Heart Block", "x": 17, "y": 13, "misc": 64}, + {"screen": 2, "sprite": "Paragoomba (Vertical)", "x": 26, "y": 26, "misc": 192}, + {"screen": 3, "sprite": "Paragoomba (Vertical)", "x": 6, "y": 22, "misc": 192}, + {"screen": 3, "sprite": "Owl Platform (Vertical)", "x": 23, "y": 28, "misc": 64}, + {"screen": 4, "sprite": "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)", "x": 11, "y": 19, "misc": 64}, + {"screen": 5, "sprite": "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)", "x": 1, "y": 21, "misc": 64}, + {"screen": 5, "sprite": "Cloud Platform (Horizontal)", "x": 18, "y": 20, "misc": 64}, + {"screen": 5, "sprite": "Cloud Platform (Horizontal)", "x": 26, "y": 26, "misc": 64}, + {"screen": 6, "sprite": "Cloud Platform (Horizontal)", "x": 2, "y": 20, "misc": 64}, + {"screen": 6, "sprite": "Midway Bell", "x": 18, "y": 20, "misc": 64}, + {"screen": 6, "sprite": "Heart Block", "x": 23, "y": 21, "misc": 64}, + {"screen": 6, "sprite": "Dondon", "x": 30, "y": 14, "misc": 64}, + {"screen": 7, "sprite": "Carrot Block", "x": 9, "y": 19, "misc": 64}, + {"screen": 7, "sprite": "Rotating Platform (Small)", "x": 9, "y": 21, "misc": 64}, + {"screen": 7, "sprite": "Heart Block", "x": 23, "y": 13, "misc": 64}, + {"screen": 7, "sprite": "Rotating Platform (Small)", "x": 23, "y": 23, "misc": 64}, + {"screen": 8, "sprite": "Mushroom Block", "x": 21, "y": 13, "misc": 64}, + {"screen": 9, "sprite": "Star Block", "x": 5, "y": 7, "misc": 64}, + {"screen": 9, "sprite": "Paragoomba (Vertical)", "x": 7, "y": 14, "misc": 64}, + {"screen": 9, "sprite": "Paragoomba (Vertical)", "x": 27, "y": 20, "misc": 64}, + {"screen": 9, "sprite": "Paragoomba (Vertical)", "x": 31, "y": 16, "misc": 64}, + {"screen": 10, "sprite": "Cloud Platform (Horizontal)", "x": 20, "y": 22, "misc": 64}, + {"screen": 10, "sprite": "Paragoomba (Vertical)", "x": 31, "y": 18, "misc": 64}, + {"screen": 11, "sprite": "Cloud Platform (Horizontal)", "x": 8, "y": 22, "misc": 64}, + {"screen": 11, "sprite": "Paragoomba (Vertical)", "x": 16, "y": 25, "misc": 64}], + "Pumpkin Zone 1": [ + {"screen": 1, "sprite": "Falling Spike on Chain", "x": 9, "y": 14, "misc": 160}, + {"screen": 1, "sprite": "Mushroom Block", "x": 15, "y": 23, "misc": 0}, + {"screen": 1, "sprite": "Masked Ghoul/Bullet Bill", "x": 19, "y": 30, "misc": 0}, + {"screen": 1, "sprite": "Falling Spike on Chain", "x": 27, "y": 22, "misc": 32}, + {"screen": 2, "sprite": "Falling Spike on Chain", "x": 17, "y": 22, "misc": 32}, + {"screen": 2, "sprite": "Falling Spike on Chain", "x": 23, "y": 22, "misc": 32}, + {"screen": 2, "sprite": "Falling Spike on Chain", "x": 29, "y": 22, "misc": 32}, + {"screen": 3, "sprite": "Masked Ghoul/Bullet Bill", "x": 1, "y": 30, "misc": 160}, + {"screen": 3, "sprite": "Masked Ghoul/Bullet Bill", "x": 21, "y": 30, "misc": 32}, + {"screen": 4, "sprite": "Masked Ghoul/Bullet Bill", "x": 5, "y": 30, "misc": 32}, + {"screen": 4, "sprite": "Carrot Block", "x": 17, "y": 21, "misc": 32}, + {"screen": 4, "sprite": "Masked Ghoul/Bullet Bill", "x": 21, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Falling Spike", "x": 27, "y": 20, "misc": 32}, + {"screen": 6, "sprite": "Boo/Bomubomu", "x": 1, "y": 28, "misc": 0}, + {"screen": 6, "sprite": "Masked Ghoul/Bullet Bill", "x": 2, "y": 30, "misc": 32}, + {"screen": 6, "sprite": "Heart Block", "x": 5, "y": 7, "misc": 0}, + {"screen": 6, "sprite": "Boo/Bomubomu", "x": 9, "y": 28, "misc": 128}, + {"screen": 6, "sprite": "Fire Piranha Plant", "x": 16, "y": 2, "misc": 192}, + {"screen": 6, "sprite": "Midway Bell", "x": 24, "y": 22, "misc": 32}, + {"screen": 7, "sprite": "Falling Spike", "x": 1, "y": 20, "misc": 32}, + {"screen": 7, "sprite": "Masked Ghoul/Bullet Bill", "x": 9, "y": 30, "misc": 160}, + {"screen": 7, "sprite": "Falling Spike", "x": 27, "y": 20, "misc": 32}, + {"screen": 7, "sprite": "Masked Ghoul/Bullet Bill", "x": 31, "y": 30, "misc": 160}, + {"screen": 8, "sprite": "Piranha Plant", "x": 16, "y": 28, "misc": 32}, + {"screen": 8, "sprite": "Masked Ghoul/Bullet Bill", "x": 17, "y": 24, "misc": 160}, + {"screen": 8, "sprite": "Fire Piranha Plant", "x": 24, "y": 30, "misc": 160}, + {"screen": 9, "sprite": "Masked Ghoul/Bullet Bill", "x": 7, "y": 22, "misc": 160}, + {"screen": 9, "sprite": "Falling Spike on Chain", "x": 11, "y": 10, "misc": 160}, + {"screen": 9, "sprite": "Piranha Plant", "x": 20, "y": 14, "misc": 32}, + {"screen": 9, "sprite": "Falling Spike on Chain", "x": 23, "y": 30, "misc": 128}, + {"screen": 10, "sprite": "Cheep Cheep (Vertical)", "x": 30, "y": 11, "misc": 160}, + {"screen": 11, "sprite": "Cheep Cheep (Vertical)", "x": 6, "y": 19, "misc": 32}, + {"screen": 11, "sprite": "Kurokyura/Jack-in-the-Box", "x": 11, "y": 30, "misc": 0}, + {"screen": 11, "sprite": "Bonus Bell", "x": 21, "y": 3, "misc": 32}, + {"screen": 11, "sprite": "Heart Block", "x": 27, "y": 11, "misc": 0}], + "Pumpkin Zone 2": [ + {"screen": 1, "sprite": "Karakara", "x": 7, "y": 30, "misc": 160}, + {"screen": 1, "sprite": "Karakara", "x": 15, "y": 30, "misc": 32}, + {"screen": 1, "sprite": "Karakara", "x": 23, "y": 30, "misc": 160}, + {"screen": 2, "sprite": "Piranha Plant", "x": 20, "y": 30, "misc": 160}, + {"screen": 3, "sprite": "Honebon/F Boy", "x": 4, "y": 11, "misc": 160}, + {"screen": 3, "sprite": "Cheep Cheep (Vertical)", "x": 4, "y": 23, "misc": 32}, + {"screen": 3, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 13, "y": 28, "misc": 192}, + {"screen": 3, "sprite": "Mushroom Block", "x": 17, "y": 21, "misc": 64}, + {"screen": 4, "sprite": "Kyororo", "x": 29, "y": 30, "misc": 160}, + {"screen": 5, "sprite": "Honebon/F Boy", "x": 6, "y": 24, "misc": 32}, + {"screen": 5, "sprite": "Kyororo", "x": 13, "y": 30, "misc": 160}, + {"screen": 5, "sprite": "Kyororo", "x": 29, "y": 30, "misc": 32}, + {"screen": 6, "sprite": "Midway Bell", "x": 1, "y": 16, "misc": 32}, + {"screen": 6, "sprite": "Star (Horizontal)/Blurp (Vertical)", "x": 21, "y": 30, "misc": 192}, + {"screen": 6, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 28, "y": 25, "misc": 64}, + {"screen": 7, "sprite": "Mushroom", "x": 3, "y": 30, "misc": 32}, + {"screen": 7, "sprite": "Boo/Bomubomu", "x": 9, "y": 13, "misc": 160}, + {"screen": 7, "sprite": "Boo/Bomubomu", "x": 13, "y": 21, "misc": 160}, + {"screen": 7, "sprite": "Heart", "x": 29, "y": 14, "misc": 32}, + {"screen": 8, "sprite": "Carrot", "x": 7, "y": 22, "misc": 32}, + {"screen": 8, "sprite": "Flower", "x": 13, "y": 14, "misc": 32}, + {"screen": 8, "sprite": "Heart", "x": 19, "y": 20, "misc": 32}, + {"screen": 9, "sprite": "Star Block", "x": 1, "y": 13, "misc": 32}, + {"screen": 9, "sprite": "Kyororo", "x": 19, "y": 30, "misc": 64}, + {"screen": 9, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 20, "y": 30, "misc": 32}, + {"screen": 9, "sprite": "Kyororo", "x": 21, "y": 16, "misc": 32}, + {"screen": 10, "sprite": "Kyororo", "x": 5, "y": 14, "misc": 32}, + {"screen": 10, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 7, "y": 28, "misc": 32}, + {"screen": 10, "sprite": "Kyororo", "x": 22, "y": 14, "misc": 32}, + {"screen": 11, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 0, "y": 26, "misc": 192}, + {"screen": 11, "sprite": "Falling Spike", "x": 5, "y": 24, "misc": 64}, + {"screen": 11, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 7, "y": 30, "misc": 32}, + {"screen": 11, "sprite": "Kyororo", "x": 10, "y": 16, "misc": 32}, + {"screen": 11, "sprite": "Falling Spike", "x": 15, "y": 24, "misc": 64}, + {"screen": 11, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 19, "y": 28, "misc": 192}, + {"screen": 12, "sprite": "Karakara", "x": 9, "y": 30, "misc": 32}, + {"screen": 12, "sprite": "Karakara", "x": 17, "y": 30, "misc": 32}, + {"screen": 12, "sprite": "Karakara", "x": 25, "y": 30, "misc": 32}, + {"screen": 13, "sprite": "Karakara", "x": 19, "y": 30, "misc": 32}, + {"screen": 13, "sprite": "Bonus Bell", "x": 21, "y": 11, "misc": 32}], + "Pumpkin Zone 3": [ + {"screen": 1, "sprite": "Unibo/Terekuribo", "x": 21, "y": 26, "misc": 160}, + {"screen": 2, "sprite": "Unibo/Terekuribo", "x": 21, "y": 20, "misc": 160}, + {"screen": 3, "sprite": "Carrot Block", "x": 9, "y": 7, "misc": 32}, + {"screen": 3, "sprite": "Boo/Bomubomu", "x": 19, "y": 23, "misc": 32}, + {"screen": 4, "sprite": "Boo/Bomubomu", "x": 1, "y": 21, "misc": 32}, + {"screen": 4, "sprite": "Boo/Bomubomu", "x": 19, "y": 14, "misc": 160}, + {"screen": 6, "sprite": "Moving Platform (Small, Horizontal)", "x": 0, "y": 8, "misc": 64}, + {"screen": 6, "sprite": "Moving Platform (Small, Horizontal)", "x": 0, "y": 14, "misc": 64}, + {"screen": 6, "sprite": "Flower Block", "x": 17, "y": 19, "misc": 64}, + {"screen": 7, "sprite": "Unibo/Terekuribo", "x": 1, "y": 26, "misc": 32}, + {"screen": 7, "sprite": "Boo/Bomubomu", "x": 17, "y": 18, "misc": 64}, + {"screen": 8, "sprite": "Fire Piranha Plant", "x": 16, "y": 28, "misc": 32}, + {"screen": 8, "sprite": "Heart Block", "x": 23, "y": 5, "misc": 64}, + {"screen": 8, "sprite": "Flower Block", "x": 25, "y": 17, "misc": 64}, + {"screen": 9, "sprite": "Midway Bell", "x": 5, "y": 22, "misc": 32}, + {"screen": 9, "sprite": "Boo/Bomubomu", "x": 19, "y": 22, "misc": 160}, + {"screen": 10, "sprite": "Boo/Bomubomu", "x": 2, "y": 27, "misc": 32}, + {"screen": 10, "sprite": "Boo/Bomubomu", "x": 25, "y": 14, "misc": 160}, + {"screen": 11, "sprite": "Carrot Block", "x": 7, "y": 13, "misc": 32}, + {"screen": 11, "sprite": "Boo/Bomubomu", "x": 23, "y": 18, "misc": 160}, + {"screen": 13, "sprite": "Boo/Bomubomu", "x": 3, "y": 28, "misc": 32}, + {"screen": 13, "sprite": "Boo/Bomubomu", "x": 12, "y": 5, "misc": 64}, + {"screen": 14, "sprite": "Unibo/Terekuribo", "x": 3, "y": 28, "misc": 160}, + {"screen": 14, "sprite": "Boo/Bomubomu", "x": 5, "y": 18, "misc": 192}, + {"screen": 14, "sprite": "Unibo/Terekuribo", "x": 14, "y": 24, "misc": 32}, + {"screen": 14, "sprite": "Bonus Bell", "x": 21, "y": 13, "misc": 64}], + "Pumpkin Zone 4": [ + {"screen": 1, "sprite": "Tosenbo/Pikku", "x": 19, "y": 30, "misc": 160}, + {"screen": 1, "sprite": "Piranha Plant", "x": 24, "y": 0, "misc": 64}, + {"screen": 2, "sprite": "Boo/Bomubomu", "x": 9, "y": 22, "misc": 32}, + {"screen": 2, "sprite": "Boo/Bomubomu", "x": 24, "y": 21, "misc": 160}, + {"screen": 3, "sprite": "Boo/Bomubomu", "x": 23, "y": 26, "misc": 160}, + {"screen": 3, "sprite": "Falling Spike on Chain", "x": 29, "y": 22, "misc": 32}, + {"screen": 4, "sprite": "Falling Spike on Chain", "x": 3, "y": 22, "misc": 32}, + {"screen": 4, "sprite": "Falling Spike on Chain", "x": 9, "y": 22, "misc": 32}, + {"screen": 4, "sprite": "Tosenbo/Pikku", "x": 28, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Mushroom Block", "x": 7, "y": 11, "misc": 64}, + {"screen": 5, "sprite": "Boo/Bomubomu", "x": 17, "y": 26, "misc": 192}, + {"screen": 5, "sprite": "Heart Block", "x": 29, "y": 15, "misc": 64}, + {"screen": 6, "sprite": "Carrot Block", "x": 17, "y": 13, "misc": 32}, + {"screen": 7, "sprite": "Midway Bell", "x": 15, "y": 24, "misc": 32}, + {"screen": 8, "sprite": "Falling Spike on Chain", "x": 3, "y": 22, "misc": 32}, + {"screen": 8, "sprite": "Rerere/Poro", "x": 10, "y": 30, "misc": 160}, + {"screen": 8, "sprite": "Falling Spike on Chain", "x": 17, "y": 22, "misc": 160}, + {"screen": 8, "sprite": "Falling Spike on Chain", "x": 27, "y": 22, "misc": 160}, + {"screen": 9, "sprite": "Rerere/Poro", "x": 3, "y": 30, "misc": 32}, + {"screen": 9, "sprite": "Falling Spike on Chain", "x": 9, "y": 22, "misc": 160}, + {"screen": 10, "sprite": "Masked Ghoul/Bullet Bill", "x": 19, "y": 30, "misc": 32}, + {"screen": 10, "sprite": "Masked Ghoul/Bullet Bill", "x": 25, "y": 26, "misc": 160}, + {"screen": 10, "sprite": "Masked Ghoul/Bullet Bill", "x": 31, "y": 22, "misc": 32}, + {"screen": 11, "sprite": "Masked Ghoul/Bullet Bill", "x": 5, "y": 18, "misc": 32}, + {"screen": 12, "sprite": "Masked Ghoul/Bullet Bill", "x": 14, "y": 16, "misc": 192}, + {"screen": 12, "sprite": "Masked Ghoul/Bullet Bill", "x": 25, "y": 16, "misc": 192}, + {"screen": 13, "sprite": "Masked Ghoul/Bullet Bill", "x": 6, "y": 16, "misc": 192}, + {"screen": 13, "sprite": "Flower Block", "x": 13, "y": 23, "misc": 32}, + {"screen": 13, "sprite": "Masked Ghoul/Bullet Bill", "x": 18, "y": 16, "misc": 192}, + {"screen": 13, "sprite": "Money Bag Block", "x": 25, "y": 23, "misc": 32}, + {"screen": 14, "sprite": "Boo/Bomubomu", "x": 13, "y": 30, "misc": 160}], + "Mario Zone 1": [ + {"screen": 1, "sprite": "Mushroom Block", "x": 5, "y": 23, "misc": 64}, + {"screen": 1, "sprite": "Spinning Platform (Horizontal)/Skeleton Bee", "x": 7, "y": 9, "misc": 64}, + {"screen": 1, "sprite": "Spinning Platform (Horizontal)/Skeleton Bee", "x": 13, "y": 13, "misc": 32}, + {"screen": 1, "sprite": "Koopa Troopa", "x": 13, "y": 30, "misc": 192}, + {"screen": 1, "sprite": "Spinning Spike (Vertical)", "x": 19, "y": 22, "misc": 0}, + {"screen": 1, "sprite": "Spinning Platform (Horizontal)/Skeleton Bee", "x": 19, "y": 15, "misc": 32}, + {"screen": 2, "sprite": "Spinning Platform (Horizontal)/Skeleton Bee", "x": 13, "y": 27, "misc": 64}, + {"screen": 2, "sprite": "Koopa Troopa", "x": 25, "y": 18, "misc": 64}, + {"screen": 3, "sprite": "Spinning Platform (Vertical)", "x": 1, "y": 18, "misc": 64}, + {"screen": 3, "sprite": "Spinning Platform (Vertical)", "x": 7, "y": 18, "misc": 64}, + {"screen": 3, "sprite": "Koopa Troopa", "x": 19, "y": 26, "misc": 192}, + {"screen": 3, "sprite": "Koopa Troopa", "x": 31, "y": 14, "misc": 192}, + {"screen": 4, "sprite": "Spinning Platform (Vertical)", "x": 23, "y": 20, "misc": 64}, + {"screen": 4, "sprite": "Tatenoko", "x": 26, "y": 15, "misc": 192}, + {"screen": 4, "sprite": "Spinning Platform (Vertical)", "x": 29, "y": 20, "misc": 64}, + {"screen": 5, "sprite": "Koopa Troopa", "x": 11, "y": 16, "misc": 192}, + {"screen": 5, "sprite": "Spinning Platform (Vertical)", "x": 19, "y": 22, "misc": 64}, + {"screen": 5, "sprite": "Spinning Platform (Vertical)", "x": 23, "y": 22, "misc": 64}, + {"screen": 6, "sprite": "Carrot Block", "x": 5, "y": 11, "misc": 64}, + {"screen": 6, "sprite": "Midway Bell", "x": 16, "y": 12, "misc": 64}, + {"screen": 7, "sprite": "Koopa Troopa", "x": 19, "y": 20, "misc": 64}, + {"screen": 7, "sprite": "Koopa Troopa", "x": 21, "y": 24, "misc": 192}, + {"screen": 8, "sprite": "Spinning Spike (Horizontal)/Unera", "x": 12, "y": 30, "misc": 64}, + {"screen": 8, "sprite": "Heart Block", "x": 13, "y": 17, "misc": 64}, + {"screen": 9, "sprite": "Mushroom Block", "x": 3, "y": 19, "misc": 64}, + {"screen": 9, "sprite": "Neiji/Buichi", "x": 3, "y": 0, "misc": 96}, + {"screen": 9, "sprite": "Neiji/Buichi", "x": 11, "y": 0, "misc": 224}, + {"screen": 9, "sprite": "Neiji/Buichi", "x": 19, "y": 0, "misc": 224}, + {"screen": 9, "sprite": "Heart Block", "x": 27, "y": 7, "misc": 32}, + {"screen": 9, "sprite": "Spinning Platform (Vertical)", "x": 29, "y": 30, "misc": 32}, + {"screen": 10, "sprite": "Tatenoko", "x": 4, "y": 13, "misc": 32}, + {"screen": 10, "sprite": "Spinning Platform (Horizontal)/Skeleton Bee", "x": 7, "y": 21, "misc": 64}, + {"screen": 10, "sprite": "Carrot Block", "x": 9, "y": 7, "misc": 32}, + {"screen": 10, "sprite": "Spinning Platform (Vertical)", "x": 9, "y": 18, "misc": 32}, + {"screen": 10, "sprite": "Bonus Bell", "x": 19, "y": 17, "misc": 64}], + "Mario Zone 2": [ + {"screen": 1, "sprite": "Boo/Bomubomu", "x": 9, "y": 28, "misc": 32}, + {"screen": 1, "sprite": "Boo/Bomubomu", "x": 31, "y": 22, "misc": 160}, + {"screen": 3, "sprite": "Paragoomba (Vertical)", "x": 6, "y": 18, "misc": 160}, + {"screen": 3, "sprite": "Paragoomba (Vertical)", "x": 15, "y": 21, "misc": 160}, + {"screen": 4, "sprite": "Paragoomba (Vertical)", "x": 3, "y": 20, "misc": 160}, + {"screen": 4, "sprite": "Paragoomba (Vertical)", "x": 12, "y": 18, "misc": 160}, + {"screen": 4, "sprite": "Paragoomba (Vertical)", "x": 28, "y": 21, "misc": 32}, + {"screen": 5, "sprite": "Mushroom Block", "x": 3, "y": 21, "misc": 32}, + {"screen": 5, "sprite": "Heart", "x": 8, "y": 28, "misc": 32}, + {"screen": 5, "sprite": "Goomba", "x": 9, "y": 20, "misc": 192}, + {"screen": 5, "sprite": "Carrot Block", "x": 17, "y": 11, "misc": 64}, + {"screen": 5, "sprite": "Goomba", "x": 25, "y": 26, "misc": 192}, + {"screen": 5, "sprite": "Money Bag Block", "x": 27, "y": 19, "misc": 32}, + {"screen": 6, "sprite": "Goomba", "x": 3, "y": 26, "misc": 64}, + {"screen": 6, "sprite": "Star Block", "x": 7, "y": 11, "misc": 64}, + {"screen": 7, "sprite": "Noko Bombette/Bear", "x": 6, "y": 28, "misc": 32}, + {"screen": 8, "sprite": "Midway Bell", "x": 5, "y": 18, "misc": 32}, + {"screen": 8, "sprite": "Boo/Bomubomu", "x": 21, "y": 28, "misc": 160}, + {"screen": 8, "sprite": "Boo/Bomubomu", "x": 31, "y": 16, "misc": 32}, + {"screen": 9, "sprite": "Mushroom Block", "x": 11, "y": 15, "misc": 32}, + {"screen": 9, "sprite": "Boo/Bomubomu", "x": 19, "y": 20, "misc": 160}, + {"screen": 10, "sprite": "Boo/Bomubomu", "x": 5, "y": 24, "misc": 32}, + {"screen": 11, "sprite": "Mushroom", "x": 1, "y": 14, "misc": 32}, + {"screen": 11, "sprite": "Goomba", "x": 9, "y": 14, "misc": 32}, + {"screen": 11, "sprite": "Flower", "x": 17, "y": 14, "misc": 32}, + {"screen": 11, "sprite": "Carrot", "x": 25, "y": 14, "misc": 32}, + {"screen": 12, "sprite": "Boo/Bomubomu", "x": 12, "y": 16, "misc": 32}, + {"screen": 12, "sprite": "Boo/Bomubomu", "x": 24, "y": 20, "misc": 160}, + {"screen": 13, "sprite": "Noko Bombette/Bear", "x": 16, "y": 28, "misc": 32}, + {"screen": 14, "sprite": "Noko Bombette/Bear", "x": 12, "y": 28, "misc": 32}, + {"screen": 14, "sprite": "Bonus Bell", "x": 21, "y": 17, "misc": 32}], + "Mario Zone 3": [ + {"screen": 1, "sprite": "Kurokyura/Jack-in-the-Box", "x": 15, "y": 23, "misc": 160}, + {"screen": 1, "sprite": "Kiddokatto", "x": 21, "y": 30, "misc": 32}, + {"screen": 2, "sprite": "Masked Ghoul/Bullet Bill", "x": 4, "y": 26, "misc": 160}, + {"screen": 2, "sprite": "Diagonal Ball on Chain", "x": 9, "y": 30, "misc": 32}, + {"screen": 3, "sprite": "Diagonal Ball on Chain", "x": 1, "y": 30, "misc": 32}, + {"screen": 3, "sprite": "Mushroom Block", "x": 13, "y": 17, "misc": 32}, + {"screen": 3, "sprite": "Diagonal Ball on Chain", "x": 25, "y": 30, "misc": 32}, + {"screen": 4, "sprite": "Masked Ghoul/Bullet Bill", "x": 14, "y": 24, "misc": 160}, + {"screen": 4, "sprite": "Diagonal Ball on Chain", "x": 27, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Masked Ghoul/Bullet Bill", "x": 14, "y": 24, "misc": 160}, + {"screen": 6, "sprite": "Carrot Block", "x": 9, "y": 21, "misc": 32}, + {"screen": 6, "sprite": "Kiddokatto", "x": 25, "y": 30, "misc": 160}, + {"screen": 7, "sprite": "Masked Ghoul/Bullet Bill", "x": 26, "y": 28, "misc": 32}, + {"screen": 7, "sprite": "Midway Bell", "x": 27, "y": 22, "misc": 32}, + {"screen": 8, "sprite": "Kurokyura/Jack-in-the-Box", "x": 11, "y": 21, "misc": 32}, + {"screen": 8, "sprite": "Heart Block", "x": 13, "y": 3, "misc": 32}, + {"screen": 8, "sprite": "Kiddokatto", "x": 31, "y": 30, "misc": 160}, + {"screen": 9, "sprite": "Claw Grabber", "x": 23, "y": 28, "misc": 32}, + {"screen": 10, "sprite": "Claw Grabber", "x": 13, "y": 28, "misc": 32}, + {"screen": 11, "sprite": "Masked Ghoul/Bullet Bill", "x": 4, "y": 28, "misc": 64}, + {"screen": 11, "sprite": "Koopa Troopa", "x": 5, "y": 20, "misc": 160}, + {"screen": 11, "sprite": "Masked Ghoul/Bullet Bill", "x": 8, "y": 12, "misc": 192}, + {"screen": 11, "sprite": "Kiddokatto", "x": 26, "y": 20, "misc": 32}, + {"screen": 11, "sprite": "Heart Block", "x": 27, "y": 23, "misc": 64}, + {"screen": 11, "sprite": "Masked Ghoul/Bullet Bill", "x": 28, "y": 18, "misc": 192}, + {"screen": 12, "sprite": "Claw Grabber", "x": 11, "y": 28, "misc": 32}, + {"screen": 12, "sprite": "Kiddokatto", "x": 21, "y": 20, "misc": 32}, + {"screen": 13, "sprite": "Mushroom Block", "x": 13, "y": 21, "misc": 32}, + {"screen": 13, "sprite": "Masked Ghoul/Bullet Bill", "x": 18, "y": 28, "misc": 160}, + {"screen": 13, "sprite": "Kurokyura/Jack-in-the-Box", "x": 25, "y": 21, "misc": 32}, + {"screen": 14, "sprite": "Diagonal Ball on Chain", "x": 9, "y": 30, "misc": 32}, + {"screen": 14, "sprite": "Masked Ghoul/Bullet Bill", "x": 26, "y": 24, "misc": 160}, + {"screen": 15, "sprite": "Claw Grabber", "x": 3, "y": 18, "misc": 32}, + {"screen": 15, "sprite": "Bonus Bell", "x": 21, "y": 17, "misc": 32}], + "Mario Zone 4": [ + {"screen": 1, "sprite": "Mushroom Block", "x": 9, "y": 17, "misc": 32}, + {"screen": 1, "sprite": "Spinning Spike/Tamara", "x": 9, "y": 25, "misc": 160}, + {"screen": 1, "sprite": "Spinning Spike/Tamara", "x": 29, "y": 25, "misc": 32}, + {"screen": 2, "sprite": "Masked Ghoul/Bullet Bill", "x": 12, "y": 26, "misc": 160}, + {"screen": 2, "sprite": "Spinning Spike/Tamara", "x": 29, "y": 21, "misc": 160}, + {"screen": 3, "sprite": "Spinning Spike/Tamara", "x": 15, "y": 21, "misc": 32}, + {"screen": 4, "sprite": "Masked Ghoul/Bullet Bill", "x": 14, "y": 26, "misc": 160}, + {"screen": 4, "sprite": "Masked Ghoul/Bullet Bill", "x": 24, "y": 21, "misc": 32}, + {"screen": 5, "sprite": "Masked Ghoul/Bullet Bill", "x": 2, "y": 26, "misc": 32}, + {"screen": 5, "sprite": "Goomba", "x": 22, "y": 20, "misc": 32}, + {"screen": 6, "sprite": "Goomba", "x": 2, "y": 14, "misc": 32}, + {"screen": 6, "sprite": "Goomba", "x": 14, "y": 8, "misc": 32}, + {"screen": 6, "sprite": "Goomba", "x": 26, "y": 2, "misc": 32}, + {"screen": 7, "sprite": "Spinning Spike/Tamara", "x": 9, "y": 25, "misc": 160}, + {"screen": 7, "sprite": "Spinning Spike/Tamara", "x": 15, "y": 21, "misc": 32}, + {"screen": 7, "sprite": "Spinning Spike/Tamara", "x": 21, "y": 25, "misc": 160}, + {"screen": 7, "sprite": "Mushroom Block", "x": 25, "y": 23, "misc": 0}, + {"screen": 8, "sprite": "Boo/Bomubomu", "x": 17, "y": 26, "misc": 128}, + {"screen": 8, "sprite": "Masked Ghoul/Bullet Bill", "x": 30, "y": 24, "misc": 0}, + {"screen": 9, "sprite": "Boo/Bomubomu", "x": 11, "y": 30, "misc": 128}, + {"screen": 9, "sprite": "Midway Bell", "x": 16, "y": 24, "misc": 0}, + {"screen": 9, "sprite": "Goomba", "x": 26, "y": 30, "misc": 0}, + {"screen": 10, "sprite": "Moving Saw (Ceiling)", "x": 17, "y": 26, "misc": 0}, + {"screen": 10, "sprite": "Moving Saw (Floor)", "x": 19, "y": 22, "misc": 128}, + {"screen": 11, "sprite": "Mushroom Block", "x": 7, "y": 23, "misc": 0}, + {"screen": 11, "sprite": "Moving Saw (Ceiling)", "x": 25, "y": 26, "misc": 128}, + {"screen": 11, "sprite": "Moving Saw (Floor)", "x": 27, "y": 22, "misc": 0}, + {"screen": 11, "sprite": "Money Bag/Bopping Toady", "x": 29, "y": 30, "misc": 0}, + {"screen": 13, "sprite": "Moving Saw (Floor)", "x": 9, "y": 28, "misc": 128}, + {"screen": 13, "sprite": "Moving Saw (Floor)", "x": 19, "y": 28, "misc": 0}, + {"screen": 14, "sprite": "Spinning Spike/Tamara", "x": 13, "y": 25, "misc": 0}, + {"screen": 14, "sprite": "Goomba", "x": 21, "y": 30, "misc": 128}], + "Turtle Zone 1": [ + {"screen": 1, "sprite": "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)", "x": 2, "y": 16, "misc": 192}, + {"screen": 1, "sprite": "Horizontal Blurp", "x": 12, "y": 21, "misc": 64}, + {"screen": 1, "sprite": "Mushroom Block", "x": 13, "y": 3, "misc": 64}, + {"screen": 2, "sprite": "Horizontal Blurp", "x": 1, "y": 18, "misc": 64}, + {"screen": 2, "sprite": "Spiny Cheep Cheep", "x": 11, "y": 26, "misc": 64}, + {"screen": 2, "sprite": "Cheep Cheep (Vertical)", "x": 26, "y": 28, "misc": 192}, + {"screen": 3, "sprite": "Goomba", "x": 3, "y": 8, "misc": 64}, + {"screen": 3, "sprite": "Goomba", "x": 17, "y": 8, "misc": 64}, + {"screen": 3, "sprite": "Star Block", "x": 17, "y": 1, "misc": 64}, + {"screen": 3, "sprite": "Cheep Cheep (Vertical)", "x": 18, "y": 28, "misc": 192}, + {"screen": 3, "sprite": "Heart Block", "x": 19, "y": 1, "misc": 64}, + {"screen": 3, "sprite": "Shark", "x": 27, "y": 26, "misc": 192}, + {"screen": 4, "sprite": "Paragoomba (Diagonal)", "x": 1, "y": 4, "misc": 64}, + {"screen": 4, "sprite": "Goomba", "x": 10, "y": 8, "misc": 64}, + {"screen": 5, "sprite": "Horizontal Blurp", "x": 1, "y": 16, "misc": 64}, + {"screen": 5, "sprite": "Horizontal Blurp", "x": 9, "y": 22, "misc": 192}, + {"screen": 5, "sprite": "Horizontal Blurp", "x": 16, "y": 26, "misc": 64}, + {"screen": 6, "sprite": "Shark", "x": 12, "y": 21, "misc": 64}, + {"screen": 6, "sprite": "Midway Bell", "x": 21, "y": 18, "misc": 64}, + {"screen": 6, "sprite": "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)", "x": 25, "y": 16, "misc": 64}, + {"screen": 7, "sprite": "Spiny Cheep Cheep", "x": 21, "y": 17, "misc": 64}, + {"screen": 7, "sprite": "Horizontal Blurp", "x": 25, "y": 26, "misc": 64}, + {"screen": 8, "sprite": "Cheep Cheep (Vertical)", "x": 7, "y": 20, "misc": 64}, + {"screen": 8, "sprite": "Shark", "x": 15, "y": 14, "misc": 192}, + {"screen": 8, "sprite": "Spiny Cheep Cheep", "x": 25, "y": 24, "misc": 192}, + {"screen": 8, "sprite": "Mushroom Block", "x": 31, "y": 3, "misc": 64}, + {"screen": 9, "sprite": "Cheep Cheep (Vertical)", "x": 15, "y": 24, "misc": 64}, + {"screen": 9, "sprite": "Cheep Cheep (Vertical)", "x": 23, "y": 20, "misc": 192}, + {"screen": 9, "sprite": "Cheep Cheep (Vertical)", "x": 29, "y": 26, "misc": 192}, + {"screen": 10, "sprite": "Shark", "x": 10, "y": 18, "misc": 192}, + {"screen": 10, "sprite": "Money Bag Block", "x": 11, "y": 31, "misc": 32}, + {"screen": 11, "sprite": "Shark", "x": 5, "y": 22, "misc": 64}, + {"screen": 11, "sprite": "Horizontal Blurp", "x": 16, "y": 28, "misc": 192}, + {"screen": 11, "sprite": "Bonus Bell", "x": 21, "y": 9, "misc": 64}, + {"screen": 11, "sprite": "Heart Block", "x": 27, "y": 23, "misc": 64}], + "Turtle Zone 2": [ + {"screen": 0, "sprite": "Shark", "x": 17, "y": 12, "misc": 192}, + {"screen": 0, "sprite": "Carrot Block", "x": 21, "y": 19, "misc": 64}, + {"screen": 0, "sprite": "Masked Ghoul/Bullet Bill", "x": 22, "y": 28, "misc": 192}, + {"screen": 1, "sprite": "Honebon/F Boy", "x": 13, "y": 12, "misc": 192}, + {"screen": 1, "sprite": "Koopa Troopa", "x": 19, "y": 30, "misc": 32}, + {"screen": 1, "sprite": "Karakara", "x": 28, "y": 27, "misc": 64}, + {"screen": 2, "sprite": "Shark", "x": 7, "y": 10, "misc": 64}, + {"screen": 2, "sprite": "Karakara", "x": 17, "y": 27, "misc": 64}, + {"screen": 2, "sprite": "Karakara", "x": 23, "y": 22, "misc": 64}, + {"screen": 2, "sprite": "Koopa Troopa", "x": 25, "y": 14, "misc": 32}, + {"screen": 3, "sprite": "Karakara", "x": 2, "y": 1, "misc": 64}, + {"screen": 3, "sprite": "Karakara", "x": 6, "y": 14, "misc": 192}, + {"screen": 3, "sprite": "Masked Ghoul/Bullet Bill", "x": 14, "y": 24, "misc": 192}, + {"screen": 3, "sprite": "Flower Block", "x": 21, "y": 11, "misc": 64}, + {"screen": 4, "sprite": "Pencil/Spikey", "x": 7, "y": 26, "misc": 192}, + {"screen": 4, "sprite": "Koopa Troopa", "x": 10, "y": 14, "misc": 160}, + {"screen": 4, "sprite": "Pencil/Spikey", "x": 11, "y": 26, "misc": 192}, + {"screen": 4, "sprite": "Karakara", "x": 21, "y": 22, "misc": 64}, + {"screen": 4, "sprite": "Karakara", "x": 28, "y": 12, "misc": 192}, + {"screen": 5, "sprite": "Pencil/Spikey", "x": 1, "y": 24, "misc": 192}, + {"screen": 5, "sprite": "Star Block", "x": 5, "y": 5, "misc": 64}, + {"screen": 5, "sprite": "Masked Ghoul/Bullet Bill", "x": 26, "y": 24, "misc": 192}, + {"screen": 5, "sprite": "Koopa Troopa", "x": 30, "y": 10, "misc": 32}, + {"screen": 6, "sprite": "Masked Ghoul/Bullet Bill", "x": 6, "y": 28, "misc": 64}, + {"screen": 6, "sprite": "Koopa Troopa", "x": 10, "y": 22, "misc": 32}, + {"screen": 6, "sprite": "Koopa Troopa", "x": 14, "y": 26, "misc": 32}, + {"screen": 6, "sprite": "Midway Bell", "x": 15, "y": 4, "misc": 32}, + {"screen": 6, "sprite": "Koopa Troopa", "x": 18, "y": 10, "misc": 64}, + {"screen": 6, "sprite": "Koopa Troopa", "x": 26, "y": 2, "misc": 64}, + {"screen": 7, "sprite": "Karakara", "x": 8, "y": 14, "misc": 160}, + {"screen": 7, "sprite": "Karakara", "x": 19, "y": 27, "misc": 192}, + {"screen": 8, "sprite": "Flower", "x": 5, "y": 4, "misc": 64}, + {"screen": 8, "sprite": "Heart Block", "x": 7, "y": 3, "misc": 64}, + {"screen": 8, "sprite": "Pencil/Spikey", "x": 11, "y": 24, "misc": 32}, + {"screen": 8, "sprite": "Money Bag Block", "x": 23, "y": 13, "misc": 64}, + {"screen": 9, "sprite": "Pencil/Spikey", "x": 9, "y": 24, "misc": 192}, + {"screen": 9, "sprite": "Pencil/Spikey", "x": 19, "y": 24, "misc": 64}, + {"screen": 10, "sprite": "Rising Platform", "x": 4, "y": 22, "misc": 64}, + {"screen": 10, "sprite": "Rising Platform", "x": 10, "y": 16, "misc": 64}, + {"screen": 10, "sprite": "Bonus Bell", "x": 21, "y": 9, "misc": 64}, + {"screen": 12, "sprite": "Pencil/Spikey", "x": 5, "y": 24, "misc": 192}, + {"screen": 12, "sprite": "Pencil/Spikey", "x": 9, "y": 24, "misc": 192}, + {"screen": 12, "sprite": "Karakara", "x": 19, "y": 24, "misc": 64}, + {"screen": 12, "sprite": "Karakara", "x": 26, "y": 28, "misc": 192}, + {"screen": 13, "sprite": "Karakara", "x": 1, "y": 26, "misc": 192}, + {"screen": 13, "sprite": "Pencil/Spikey", "x": 15, "y": 24, "misc": 64}, + {"screen": 13, "sprite": "Honebon/F Boy", "x": 24, "y": 28, "misc": 192}, + {"screen": 14, "sprite": "Pencil/Spikey", "x": 1, "y": 24, "misc": 192}, + {"screen": 14, "sprite": "Shark", "x": 14, "y": 26, "misc": 64}], + "Turtle Zone 3": [ + {"screen": 1, "sprite": "Ragumo/Aqua Kuribo", "x": 1, "y": 26, "misc": 160}, + {"screen": 1, "sprite": "Ragumo/Aqua Kuribo", "x": 31, "y": 22, "misc": 160}, + {"screen": 2, "sprite": "Carrot Block", "x": 15, "y": 15, "misc": 32}, + {"screen": 2, "sprite": "Pencil/Spikey", "x": 29, "y": 14, "misc": 160}, + {"screen": 3, "sprite": "Pencil/Spikey", "x": 13, "y": 22, "misc": 160}, + {"screen": 3, "sprite": "Pencil/Spikey", "x": 21, "y": 26, "misc": 32}, + {"screen": 4, "sprite": "Ragumo/Aqua Kuribo", "x": 5, "y": 24, "misc": 32}, + {"screen": 5, "sprite": "Paragoomba (Vertical)", "x": 0, "y": 24, "misc": 160}, + {"screen": 5, "sprite": "Midway Bell", "x": 12, "y": 18, "misc": 32}, + {"screen": 5, "sprite": "Ragumo/Aqua Kuribo", "x": 27, "y": 20, "misc": 32}, + {"screen": 5, "sprite": "Mushroom Block", "x": 29, "y": 19, "misc": 32}, + {"screen": 6, "sprite": "Honebon/F Boy", "x": 1, "y": 30, "misc": 160}, + {"screen": 6, "sprite": "Ragumo/Aqua Kuribo", "x": 7, "y": 20, "misc": 160}, + {"screen": 6, "sprite": "Koopa Troopa", "x": 24, "y": 20, "misc": 160}, + {"screen": 7, "sprite": "Ragumo/Aqua Kuribo", "x": 6, "y": 28, "misc": 32}, + {"screen": 7, "sprite": "Koopa Troopa", "x": 27, "y": 28, "misc": 160}, + {"screen": 8, "sprite": "Ragumo/Aqua Kuribo", "x": 7, "y": 28, "misc": 160}, + {"screen": 8, "sprite": "Ragumo/Aqua Kuribo", "x": 19, "y": 28, "misc": 32}, + {"screen": 9, "sprite": "Mushroom Block", "x": 17, "y": 13, "misc": 32}, + {"screen": 9, "sprite": "Honebon/F Boy", "x": 27, "y": 22, "misc": 160}, + {"screen": 9, "sprite": "Money Bag Block", "x": 29, "y": 11, "misc": 32}, + {"screen": 11, "sprite": "Paragoomba (Diagonal)", "x": 13, "y": 24, "misc": 160}, + {"screen": 11, "sprite": "Koopa Troopa", "x": 19, "y": 30, "misc": 160}], + "Hippo Zone": [ + {"screen": 0, "sprite": "Money Bag Block", "x": 5, "y": 7, "misc": 32}, + {"screen": 0, "sprite": "Heart Block", "x": 7, "y": 5, "misc": 0}, + {"screen": 0, "sprite": "Heart Block", "x": 31, "y": 19, "misc": 32}, + {"screen": 1, "sprite": "Heart Block", "x": 1, "y": 19, "misc": 32}, + {"screen": 1, "sprite": "Bubble", "x": 6, "y": 24, "misc": 0}, + {"screen": 1, "sprite": "Mushroom Block", "x": 27, "y": 9, "misc": 32}, + {"screen": 2, "sprite": "Toriuo", "x": 11, "y": 8, "misc": 160}, + {"screen": 2, "sprite": "Unibo/Terekuribo", "x": 25, "y": 26, "misc": 128}, + {"screen": 2, "sprite": "Toriuo", "x": 29, "y": 8, "misc": 160}, + {"screen": 3, "sprite": "Toriuo", "x": 11, "y": 8, "misc": 32}, + {"screen": 3, "sprite": "Unibo/Terekuribo", "x": 17, "y": 26, "misc": 128}, + {"screen": 3, "sprite": "Heart Block", "x": 17, "y": 25, "misc": 0}, + {"screen": 4, "sprite": "Toriuo", "x": 3, "y": 8, "misc": 160}, + {"screen": 4, "sprite": "Unibo/Terekuribo", "x": 9, "y": 26, "misc": 128}, + {"screen": 4, "sprite": "Toriuo", "x": 21, "y": 8, "misc": 32}, + {"screen": 5, "sprite": "Horizontal Blurp", "x": 6, "y": 10, "misc": 160}, + {"screen": 5, "sprite": "Mushroom Block", "x": 13, "y": 11, "misc": 32}, + {"screen": 5, "sprite": "Horizontal Blurp", "x": 19, "y": 12, "misc": 32}, + {"screen": 5, "sprite": "Unibo/Terekuribo", "x": 25, "y": 10, "misc": 128}, + {"screen": 5, "sprite": "Unibo/Terekuribo", "x": 27, "y": 18, "misc": 0}, + {"screen": 6, "sprite": "Unibo/Terekuribo", "x": 7, "y": 22, "misc": 128}, + {"screen": 6, "sprite": "Unibo/Terekuribo", "x": 11, "y": 12, "misc": 128}, + {"screen": 6, "sprite": "Horizontal Blurp", "x": 13, "y": 14, "misc": 160}, + {"screen": 6, "sprite": "Bubble", "x": 14, "y": 30, "misc": 0}, + {"screen": 7, "sprite": "Dondon", "x": 13, "y": 10, "misc": 128}, + {"screen": 7, "sprite": "Dondon", "x": 15, "y": 18, "misc": 128}, + {"screen": 7, "sprite": "Dondon", "x": 17, "y": 26, "misc": 0}, + {"screen": 7, "sprite": "Toriuo", "x": 25, "y": 8, "misc": 160}, + {"screen": 8, "sprite": "Toriuo", "x": 5, "y": 8, "misc": 160}, + {"screen": 8, "sprite": "Flower Block", "x": 19, "y": 25, "misc": 0}, + {"screen": 8, "sprite": "Toriuo", "x": 21, "y": 8, "misc": 160}, + {"screen": 8, "sprite": "Toriuo", "x": 31, "y": 8, "misc": 32}, + {"screen": 9, "sprite": "Money Bag Block", "x": 11, "y": 5, "misc": 0}, + {"screen": 9, "sprite": "Horizontal Blurp", "x": 15, "y": 12, "misc": 160}, + {"screen": 9, "sprite": "Horizontal Blurp", "x": 27, "y": 10, "misc": 160}, + {"screen": 10, "sprite": "Dondon", "x": 9, "y": 14, "misc": 0}, + {"screen": 10, "sprite": "Dondon", "x": 13, "y": 24, "misc": 0}, + {"screen": 10, "sprite": "Toriuo", "x": 19, "y": 8, "misc": 32}, + {"screen": 11, "sprite": "Bonus Bell", "x": 21, "y": 5, "misc": 0}], + "Space Zone 1": [ + {"screen": 1, "sprite": "Boo/Bomubomu", "x": 19, "y": 26, "misc": 160}, + {"screen": 2, "sprite": "Boo/Bomubomu", "x": 3, "y": 26, "misc": 160}, + {"screen": 3, "sprite": "Boo/Bomubomu", "x": 5, "y": 26, "misc": 160}, + {"screen": 3, "sprite": "Rerere/Poro", "x": 25, "y": 5, "misc": 160}, + {"screen": 4, "sprite": "Money Bag Block", "x": 9, "y": 21, "misc": 0}, + {"screen": 4, "sprite": "Money Bag Block", "x": 11, "y": 29, "misc": 0}, + {"screen": 4, "sprite": "Heart", "x": 13, "y": 6, "misc": 32}, + {"screen": 4, "sprite": "Mushroom Block", "x": 23, "y": 15, "misc": 32}, + {"screen": 5, "sprite": "No 48/Mogyo", "x": 1, "y": 21, "misc": 32}, + {"screen": 5, "sprite": "Heart", "x": 5, "y": 16, "misc": 0}, + {"screen": 5, "sprite": "Boo/Bomubomu", "x": 23, "y": 28, "misc": 160}, + {"screen": 6, "sprite": "Rerere/Poro", "x": 18, "y": 20, "misc": 32}, + {"screen": 7, "sprite": "Flower Block", "x": 5, "y": 11, "misc": 32}, + {"screen": 7, "sprite": "Rerere/Poro", "x": 6, "y": 20, "misc": 160}, + {"screen": 7, "sprite": "Midway Bell", "x": 15, "y": 22, "misc": 32}, + {"screen": 7, "sprite": "Rerere/Poro", "x": 19, "y": 13, "misc": 32}, + {"screen": 8, "sprite": "Boo/Bomubomu", "x": 1, "y": 28, "misc": 32}, + {"screen": 8, "sprite": "Money Bag Block", "x": 7, "y": 21, "misc": 32}, + {"screen": 8, "sprite": "Boo/Bomubomu", "x": 17, "y": 28, "misc": 32}, + {"screen": 9, "sprite": "Heart Block", "x": 3, "y": 15, "misc": 0}, + {"screen": 9, "sprite": "Boo/Bomubomu", "x": 8, "y": 24, "misc": 32}, + {"screen": 9, "sprite": "Rerere/Poro", "x": 31, "y": 15, "misc": 160}, + {"screen": 10, "sprite": "Boo/Bomubomu", "x": 31, "y": 20, "misc": 32}, + {"screen": 11, "sprite": "Boo/Bomubomu", "x": 3, "y": 28, "misc": 32}, + {"screen": 11, "sprite": "Bonus Bell", "x": 21, "y": 11, "misc": 32}], + "Space Zone 2": [ + {"screen": 2, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 5, "y": 22, "misc": 128}, + {"screen": 2, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 25, "y": 16, "misc": 128}, + {"screen": 2, "sprite": "Star (Horizontal)/Blurp (Vertical)", "x": 27, "y": 30, "misc": 128}, + {"screen": 3, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 11, "y": 10, "misc": 128}, + {"screen": 4, "sprite": "Mushroom", "x": 1, "y": 6, "misc": 0}, + {"screen": 4, "sprite": "Star (Horizontal)/Blurp (Vertical)", "x": 3, "y": 30, "misc": 128}, + {"screen": 4, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 23, "y": 26, "misc": 128}, + {"screen": 5, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 1, "y": 22, "misc": 128}, + {"screen": 5, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 9, "y": 26, "misc": 128}, + {"screen": 5, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 27, "y": 14, "misc": 0}, + {"screen": 6, "sprite": "Mushroom Block", "x": 13, "y": 15, "misc": 0}, + {"screen": 6, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 19, "y": 18, "misc": 128}, + {"screen": 7, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 13, "y": 22, "misc": 128}, + {"screen": 7, "sprite": "Mushroom Block", "x": 17, "y": 9, "misc": 0}, + {"screen": 7, "sprite": "Tosenbo/Pikku", "x": 27, "y": 24, "misc": 128}, + {"screen": 8, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 3, "y": 16, "misc": 128}, + {"screen": 9, "sprite": "Tosenbo/Pikku", "x": 3, "y": 16, "misc": 0}, + {"screen": 9, "sprite": "Tosenbo/Pikku", "x": 25, "y": 16, "misc": 0}, + {"screen": 10, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 13, "y": 16, "misc": 128}, + {"screen": 10, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 25, "y": 16, "misc": 128}, + {"screen": 11, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 15, "y": 20, "misc": 128}, + {"screen": 11, "sprite": "Star (Horizontal)/Blurp (Vertical)", "x": 25, "y": 22, "misc": 0}, + {"screen": 12, "sprite": "Star (Vertical)/Blurp (Horizontal)", "x": 5, "y": 16, "misc": 128}, + {"screen": 12, "sprite": "Mushroom Block", "x": 31, "y": 11, "misc": 0}, + {"screen": 13, "sprite": "Star Block", "x": 5, "y": 17, "misc": 0}, + {"screen": 13, "sprite": "Tosenbo/Pikku", "x": 7, "y": 17, "misc": 128}, + {"screen": 13, "sprite": "Midway Bell", "x": 19, "y": 20, "misc": 0}, + {"screen": 13, "sprite": "Mushroom", "x": 31, "y": 6, "misc": 0}, + {"screen": 15, "sprite": "Tosenbo/Pikku", "x": 1, "y": 26, "misc": 128}, + {"screen": 15, "sprite": "Mushroom Block", "x": 17, "y": 19, "misc": 0}], + "Macro Zone 1": [ + {"screen": 1, "sprite": "Kyotonbo", "x": 1, "y": 22, "misc": 0}, + {"screen": 1, "sprite": "Goronto", "x": 23, "y": 26, "misc": 128}, + {"screen": 2, "sprite": "Moving Platform (Large, Horizontal)", "x": 20, "y": 31, + "misc": 64}, {"screen": 3, "sprite": "Chikunto", "x": 1, "y": 16, "misc": 64}, + {"screen": 3, "sprite": "Mushroom Block", "x": 3, "y": 21, "misc": 64}, + {"screen": 4, "sprite": "Chikunto", "x": 19, "y": 28, "misc": 192}, + {"screen": 5, "sprite": "Big Diagonal Moving Platform", "x": 12, "y": 23, + "misc": 64}, + {"screen": 5, "sprite": "Moving Platform (Large, Horizontal)", "x": 14, "y": 31, + "misc": 64}, + {"screen": 5, "sprite": "Carrot Block", "x": 15, "y": 13, "misc": 64}, + {"screen": 6, "sprite": "Dokanto", "x": 25, "y": 28, "misc": 64}, + {"screen": 6, "sprite": "Carrot Block", "x": 31, "y": 27, "misc": 64}, + {"screen": 7, "sprite": "Kyotonbo", "x": 27, "y": 28, "misc": 64}, + {"screen": 8, "sprite": "Flower Block", "x": 1, "y": 27, "misc": 64}, + {"screen": 8, "sprite": "Chikunto", "x": 9, "y": 28, "misc": 64}, + {"screen": 9, "sprite": "Heart Block", "x": 11, "y": 27, "misc": 64}, + {"screen": 9, "sprite": "Chikunto", "x": 19, "y": 28, "misc": 192}, + {"screen": 9, "sprite": "Money Bag Block", "x": 25, "y": 27, "misc": 64}, + {"screen": 9, "sprite": "Goronto", "x": 29, "y": 28, "misc": 64}, + {"screen": 10, "sprite": "Heart Block", "x": 9, "y": 9, "misc": 32}, + {"screen": 10, "sprite": "Heart Block", "x": 13, "y": 9, "misc": 32}, + {"screen": 10, "sprite": "Midway Bell", "x": 15, "y": 26, "misc": 64}, + {"screen": 10, "sprite": "Heart Block", "x": 21, "y": 9, "misc": 32}, + {"screen": 10, "sprite": "Moving Platform (Large, Horizontal)", "x": 30, "y": 31, + "misc": 64}, + {"screen": 11, "sprite": "Carrot Block", "x": 3, "y": 15, "misc": 64}, + {"screen": 11, "sprite": "Falling Platform", "x": 26, "y": 17, "misc": 64}, + {"screen": 12, "sprite": "Moving Platform (Large, Horizontal)", "x": 28, "y": 31, + "misc": 64}, {"screen": 13, "sprite": "Kyotonbo", "x": 3, "y": 18, "misc": 192}, + {"screen": 13, "sprite": "Chikunto", "x": 17, "y": 28, "misc": 64}, + {"screen": 13, "sprite": "Dokanto", "x": 25, "y": 18, "misc": 64}, + {"screen": 14, "sprite": "Kyotonbo", "x": 0, "y": 28, "misc": 32}, + {"screen": 14, "sprite": "Moving Platform (Large, Horizontal)", "x": 0, "y": 31, + "misc": 64}, {"screen": 14, "sprite": "Goronto", "x": 12, "y": 26, "misc": 32}, + {"screen": 14, "sprite": "Dokanto", "x": 19, "y": 28, "misc": 192}, + {"screen": 14, "sprite": "Flower Block", "x": 21, "y": 21, "misc": 64}, + {"screen": 15, "sprite": "Big Diagonal Moving Platform", "x": 13, "y": 21, + "misc": 64}, + {"screen": 15, "sprite": "Bonus Bell", "x": 21, "y": 11, "misc": 64}], + "Macro Zone 2": [ + {"screen": 1, "sprite": "Ant", "x": 10, "y": 30, "misc": 32}, + {"screen": 1, "sprite": "Mushroom Block", "x": 19, "y": 19, "misc": 32}, + {"screen": 1, "sprite": "Piranha Plant", "x": 28, "y": 30, "misc": 32}, + {"screen": 2, "sprite": "Ant", "x": 21, "y": 30, "misc": 32}, + {"screen": 2, "sprite": "Battle Beetle", "x": 23, "y": 24, "misc": 32}, + {"screen": 3, "sprite": "Ant", "x": 5, "y": 30, "misc": 32}, + {"screen": 3, "sprite": "Fire Piranha Plant", "x": 22, "y": 2, "misc": 192}, + {"screen": 4, "sprite": "Ant", "x": 8, "y": 26, "misc": 160}, + {"screen": 4, "sprite": "Fire Piranha Plant", "x": 18, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Piranha Plant", "x": 6, "y": 30, "misc": 160}, + {"screen": 5, "sprite": "Battle Beetle", "x": 16, "y": 26, "misc": 32}, + {"screen": 6, "sprite": "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)", "x": 5, "y": 26, "misc": 192}, + {"screen": 6, "sprite": "Carrot Block", "x": 7, "y": 15, "misc": 64}, + {"screen": 6, "sprite": "Ant", "x": 16, "y": 18, "misc": 64}, + {"screen": 7, "sprite": "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)", "x": 0, "y": 23, "misc": 192}, + {"screen": 7, "sprite": "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)", "x": 11, "y": 28, "misc": 64}, + {"screen": 7, "sprite": "Fire Piranha Plant", "x": 30, "y": 20, "misc": 192}, + {"screen": 8, "sprite": "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)", "x": 15, "y": 28, "misc": 64}, + {"screen": 8, "sprite": "Star Block", "x": 19, "y": 9, "misc": 64}, + {"screen": 8, "sprite": "Piranha Plant", "x": 30, "y": 22, "misc": 64}, + {"screen": 9, "sprite": "Cheep Cheep (Vertical)", "x": 11, "y": 28, "misc": 64}, + {"screen": 9, "sprite": "Cheep Cheep (Vertical)", "x": 17, "y": 28, "misc": 64}, + {"screen": 9, "sprite": "Midway Bell", "x": 31, "y": 14, "misc": 64}, + {"screen": 10, "sprite": "Ant", "x": 1, "y": 18, "misc": 64}, + {"screen": 10, "sprite": "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)", "x": 31, "y": 28, "misc": 64}, + {"screen": 11, "sprite": "Ant", "x": 3, "y": 16, "misc": 64}, + {"screen": 11, "sprite": "Star Block", "x": 9, "y": 15, "misc": 64}, + {"screen": 11, "sprite": "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)", "x": 19, "y": 15, "misc": 160}, + {"screen": 11, "sprite": "Cheep Cheep (Vertical)", "x": 21, "y": 29, "misc": 64}, + {"screen": 12, "sprite": "Be", "x": 9, "y": 14, "misc": 32}, + {"screen": 12, "sprite": "Be", "x": 21, "y": 24, "misc": 32}, + {"screen": 12, "sprite": "Be", "x": 27, "y": 24, "misc": 32}, + {"screen": 13, "sprite": "Mushroom Block", "x": 7, "y": 23, "misc": 32}, + {"screen": 13, "sprite": "Bonus Bell", "x": 21, "y": 15, "misc": 32}], + "Macro Zone 3": [ + {"screen": 1, "sprite": "Koopa Troopa", "x": 0, "y": 26, "misc": 32}, + {"screen": 1, "sprite": "Goomba", "x": 9, "y": 22, "misc": 32}, + {"screen": 1, "sprite": "Carrot Block", "x": 17, "y": 13, "misc": 32}, + {"screen": 1, "sprite": "Goomba", "x": 23, "y": 22, "misc": 32}, + {"screen": 2, "sprite": "Goomba", "x": 4, "y": 26, "misc": 32}, + {"screen": 2, "sprite": "Goomba", "x": 29, "y": 26, "misc": 32}, + {"screen": 3, "sprite": "Piranha Plant", "x": 28, "y": 2, "misc": 64}, + {"screen": 4, "sprite": "Piranha Plant (Downward)/Grubby", "x": 4, "y": 25, "misc": 160}, + {"screen": 4, "sprite": "Goomba", "x": 14, "y": 30, "misc": 160}, + {"screen": 4, "sprite": "Goomba", "x": 30, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Piranha Plant (Downward)/Grubby", "x": 8, "y": 25, "misc": 32}, + {"screen": 5, "sprite": "Money Bag Block", "x": 11, "y": 21, "misc": 64}, + {"screen": 5, "sprite": "Honebon/F Boy", "x": 11, "y": 30, "misc": 64}, + {"screen": 5, "sprite": "Paragoomba (Diagonal)", "x": 23, "y": 28, "misc": 32}, + {"screen": 6, "sprite": "Koopa Troopa", "x": 15, "y": 26, "misc": 64}, + {"screen": 6, "sprite": "Koopa Troopa", "x": 23, "y": 26, "misc": 64}, + {"screen": 7, "sprite": "Mushroom Block", "x": 1, "y": 23, "misc": 64}, + {"screen": 7, "sprite": "Koopa Troopa", "x": 11, "y": 26, "misc": 64}, + {"screen": 7, "sprite": "Koopa Troopa", "x": 21, "y": 26, "misc": 64}, + {"screen": 8, "sprite": "Heart Block", "x": 3, "y": 5, "misc": 32}, + {"screen": 8, "sprite": "Koopa Troopa", "x": 7, "y": 26, "misc": 64}, + {"screen": 8, "sprite": "Koopa Troopa", "x": 17, "y": 26, "misc": 64}, + {"screen": 8, "sprite": "Star Block", "x": 29, "y": 23, "misc": 64}, + {"screen": 8, "sprite": "Midway Bell", "x": 31, "y": 22, "misc": 32}, + {"screen": 9, "sprite": "Be", "x": 31, "y": 18, "misc": 32}, + {"screen": 10, "sprite": "Be", "x": 11, "y": 18, "misc": 32}, + {"screen": 11, "sprite": "Piranha Plant", "x": 16, "y": 24, "misc": 160}, + {"screen": 12, "sprite": "Carrot Block", "x": 23, "y": 17, "misc": 32}, + {"screen": 13, "sprite": "Koopa Troopa", "x": 7, "y": 4, "misc": 160}, + {"screen": 13, "sprite": "Paragoomba (Vertical)", "x": 15, "y": 24, "misc": 128}, + {"screen": 13, "sprite": "Bonus Bell", "x": 23, "y": 5, "misc": 0}], + "Macro Zone 4": [ + {"screen": 1, "sprite": "Mushroom Block", "x": 1, "y": 23, "misc": 32}, + {"screen": 1, "sprite": "Goomba", "x": 7, "y": 28, "misc": 160}, + {"screen": 1, "sprite": "Koopa Troopa", "x": 17, "y": 24, "misc": 32}, + {"screen": 2, "sprite": "Runaway Heart Block/Bibi", "x": 5, "y": 23, "misc": 32}, + {"screen": 3, "sprite": "Goomba", "x": 11, "y": 24, "misc": 32}, + {"screen": 3, "sprite": "Goomba", "x": 21, "y": 30, "misc": 32}, + {"screen": 4, "sprite": "Piranha Plant", "x": 6, "y": 24, "misc": 32}, + {"screen": 5, "sprite": "Piranha Plant (Downward)/Grubby", "x": 4, "y": 25, "misc": 32}, + {"screen": 5, "sprite": "Money Bag Block", "x": 11, "y": 7, "misc": 32}, + {"screen": 5, "sprite": "Carrot Block", "x": 15, "y": 23, "misc": 32}, + {"screen": 5, "sprite": "Koopa Troopa", "x": 19, "y": 30, "misc": 160}, + {"screen": 6, "sprite": "Goomba", "x": 7, "y": 30, "misc": 160}, + {"screen": 6, "sprite": "Goomba", "x": 11, "y": 18, "misc": 32}, + {"screen": 6, "sprite": "Goomba", "x": 25, "y": 30, "misc": 160}, + {"screen": 7, "sprite": "Runaway Heart Block/Bibi", "x": 5, "y": 23, "misc": 32}, + {"screen": 7, "sprite": "Fire Piranha Plant", "x": 24, "y": 30, "misc": 160}, + {"screen": 8, "sprite": "Midway Bell", "x": 3, "y": 20, "misc": 32}, + {"screen": 8, "sprite": "Piranha Plant", "x": 8, "y": 30, "misc": 32}, + {"screen": 8, "sprite": "Goomba", "x": 26, "y": 24, "misc": 32}, + {"screen": 9, "sprite": "Mushroom Block", "x": 1, "y": 11, "misc": 32}, + {"screen": 9, "sprite": "Goomba", "x": 1, "y": 20, "misc": 32}, + {"screen": 9, "sprite": "Paragoomba (Diagonal)", "x": 13, "y": 16, "misc": 160}, + {"screen": 10, "sprite": "Paragoomba (Diagonal)", "x": 29, "y": 26, "misc": 160}, + {"screen": 11, "sprite": "Paragoomba (Diagonal)", "x": 13, "y": 26, "misc": 32}], + "Mario's Castle": [ + {"screen": 0, "sprite": "Mushroom Block", "x": 15, "y": 17, "misc": 32}, + {"screen": 1, "sprite": "Falling Bone Platform", "x": 14, "y": 27, "misc": 32}, + {"screen": 1, "sprite": "Falling Bone Platform", "x": 22, "y": 27, "misc": 32}, + {"screen": 1, "sprite": "Skull Platform", "x": 25, "y": 30, "misc": 64}, + {"screen": 1, "sprite": "Rising Bone Platform", "x": 30, "y": 28, "misc": 32}, + {"screen": 2, "sprite": "Skull Platform", "x": 5, "y": 30, "misc": 64}, + {"screen": 2, "sprite": "Falling Bone Platform", "x": 16, "y": 27, "misc": 32}, + {"screen": 2, "sprite": "Falling Bone Platform", "x": 24, "y": 27, "misc": 32}, + {"screen": 2, "sprite": "Skull Platform", "x": 29, "y": 30, "misc": 64}, + {"screen": 3, "sprite": "Rising Bone Platform", "x": 0, "y": 28, "misc": 32}, + {"screen": 3, "sprite": "Skull Platform", "x": 3, "y": 30, "misc": 64}, + {"screen": 3, "sprite": "Rising Bone Platform", "x": 18, "y": 28, "misc": 32}, + {"screen": 3, "sprite": "Skull Platform", "x": 25, "y": 30, "misc": 64}, + {"screen": 3, "sprite": "Falling Bone Platform", "x": 26, "y": 27, "misc": 32}, + {"screen": 3, "sprite": "Skull Platform", "x": 27, "y": 30, "misc": 64}, + {"screen": 4, "sprite": "Rising Bone Platform", "x": 2, "y": 28, "misc": 32}, + {"screen": 4, "sprite": "Genkottsu (1.5 Tiles)", "x": 22, "y": 31, "misc": 64}, + {"screen": 5, "sprite": "Genkottsu (2 Tiles)", "x": 6, "y": 31, "misc": 64}, + {"screen": 5, "sprite": "Karamenbo", "x": 14, "y": 20, "misc": 32}, + {"screen": 5, "sprite": "Karamenbo", "x": 20, "y": 20, "misc": 32}, + {"screen": 5, "sprite": "Genkottsu (2 Tiles)", "x": 22, "y": 31, "misc": 64}, + {"screen": 6, "sprite": "Karamenbo", "x": 1, "y": 20, "misc": 32}, + {"screen": 6, "sprite": "Karamenbo", "x": 8, "y": 20, "misc": 32}, + {"screen": 6, "sprite": "Karamenbo", "x": 21, "y": 20, "misc": 32}, + {"screen": 7, "sprite": "Propeller Platform", "x": 4, "y": 30, "misc": 64}, + {"screen": 7, "sprite": "Propeller Platform", "x": 16, "y": 30, "misc": 64}, + {"screen": 7, "sprite": "Floating Face", "x": 19, "y": 26, "misc": 32}, + {"screen": 8, "sprite": "Propeller Platform", "x": 4, "y": 30, "misc": 64}, + {"screen": 8, "sprite": "Floating Face", "x": 17, "y": 29, "misc": 32}, + {"screen": 8, "sprite": "Floating Face", "x": 17, "y": 23, "misc": 32}, + {"screen": 8, "sprite": "Propeller Platform", "x": 18, "y": 30, "misc": 64}, + {"screen": 9, "sprite": "Floating Face", "x": 15, "y": 28, "misc": 32}, + {"screen": 9, "sprite": "Floating Face", "x": 16, "y": 22, "misc": 32}, + {"screen": 10, "sprite": "Mushroom Block", "x": 1, "y": 23, "misc": 64}, + {"screen": 10, "sprite": "Spike Ball (Large)", "x": 11, "y": 28, "misc": 64}, + {"screen": 10, "sprite": "Mushroom Block", "x": 15, "y": 23, "misc": 32}, + {"screen": 10, "sprite": "Spike Ball (Small)", "x": 29, "y": 30, "misc": 64}, + {"screen": 11, "sprite": "Fire Pakkun Zo (Left)", "x": 8, "y": 28, "misc": 192}, + {"screen": 11, "sprite": "Fire Pakkun Zo (Left)", "x": 14, "y": 26, "misc": 64}, + {"screen": 11, "sprite": "Spike Ball (Large)", "x": 31, "y": 30, "misc": 192}, + {"screen": 12, "sprite": "Fire Pakkun Zo (Large)", "x": 15, "y": 26, "misc": 64}, + {"screen": 12, "sprite": "Fire Pakkun Zo (Left)", "x": 24, "y": 28, "misc": 192}, + {"screen": 13, "sprite": "Fire Pakkun Zo (Left)", "x": 0, "y": 24, "misc": 64}, + {"screen": 13, "sprite": "Fire Pakkun Zo (Left)", "x": 8, "y": 24, "misc": 64}, + {"screen": 13, "sprite": "Spike Ball (Large)", "x": 29, "y": 30, "misc": 64}, + {"screen": 14, "sprite": "Spike Ball (Small)", "x": 12, "y": 26, "misc": 192}, + {"screen": 15, "sprite": "Fire Pakkun Zo (Right)", "x": 4, "y": 16, "misc": 64}, + {"screen": 15, "sprite": "Mushroom Block", "x": 15, "y": 19, "misc": 32}], + "Scenic Course": [ + {"screen": 1, "sprite": "Paragoomba (Diagonal)", "x": 1, "y": 30, "misc": 32}, + {"screen": 1, "sprite": "Goomba", "x": 11, "y": 30, "misc": 32}, + {"screen": 1, "sprite": "Paragoomba (Diagonal)", "x": 21, "y": 30, "misc": 32}, + {"screen": 1, "sprite": "Goomba", "x": 31, "y": 30, "misc": 32}, + {"screen": 2, "sprite": "Paragoomba (Diagonal)", "x": 9, "y": 30, "misc": 32}, + {"screen": 2, "sprite": "Mushroom Block", "x": 15, "y": 23, "misc": 32}, + {"screen": 2, "sprite": "Goomba", "x": 19, "y": 30, "misc": 32}, + {"screen": 2, "sprite": "Paragoomba (Diagonal)", "x": 29, "y": 30, "misc": 32}, + {"screen": 3, "sprite": "Paragoomba (Diagonal)", "x": 7, "y": 30, "misc": 32}, + {"screen": 3, "sprite": "Goomba", "x": 17, "y": 30, "misc": 32}, + {"screen": 3, "sprite": "Star Block", "x": 19, "y": 23, "misc": 32}, + {"screen": 3, "sprite": "Paragoomba (Diagonal)", "x": 27, "y": 30, "misc": 32}, + {"screen": 4, "sprite": "Goomba", "x": 5, "y": 30, "misc": 32}, + {"screen": 4, "sprite": "Goomba", "x": 15, "y": 30, "misc": 32}, + {"screen": 4, "sprite": "Mushroom Block", "x": 17, "y": 23, "misc": 32}, + {"screen": 4, "sprite": "Paragoomba (Diagonal)", "x": 25, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Paragoomba (Diagonal)", "x": 3, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Paragoomba (Diagonal)", "x": 13, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Goomba", "x": 17, "y": 30, "misc": 32}, + {"screen": 6, "sprite": "Goomba", "x": 1, "y": 30, "misc": 32}, + {"screen": 6, "sprite": "Paragoomba (Diagonal)", "x": 11, "y": 30, "misc": 32}, + {"screen": 6, "sprite": "Goomba", "x": 21, "y": 30, "misc": 32}, + {"screen": 6, "sprite": "Paragoomba (Diagonal)", "x": 31, "y": 30, "misc": 32}, + {"screen": 7, "sprite": "Paragoomba (Diagonal)", "x": 9, "y": 30, "misc": 32}, + {"screen": 7, "sprite": "Flower Block", "x": 11, "y": 23, "misc": 32}, + {"screen": 7, "sprite": "Paragoomba (Diagonal)", "x": 19, "y": 30, "misc": 32}], + "Turtle Zone Secret Course": [ + {"screen": 1, "sprite": "Mushroom Block", "x": 7, "y": 21, "misc": 32}, + {"screen": 1, "sprite": "Heart Block", "x": 27, "y": 15, "misc": 32}, + {"screen": 2, "sprite": "Koopa Troopa", "x": 29, "y": 28, "misc": 32}, + {"screen": 3, "sprite": "Flower Block", "x": 11, "y": 21, "misc": 32}, + {"screen": 4, "sprite": "Money Bag Block", "x": 1, "y": 19, "misc": 32}, + {"screen": 4, "sprite": "Koopa Troopa", "x": 31, "y": 28, "misc": 32}, + {"screen": 5, "sprite": "Carrot Block", "x": 15, "y": 21, "misc": 32}, + {"screen": 7, "sprite": "Koopa Troopa", "x": 1, "y": 30, "misc": 32}, + {"screen": 7, "sprite": "Piranha Plant", "x": 6, "y": 30, "misc": 32}, + {"screen": 7, "sprite": "Mushroom Block", "x": 19, "y": 23, "misc": 32}, + {"screen": 7, "sprite": "Flower Block", "x": 21, "y": 23, "misc": 32}, + {"screen": 7, "sprite": "Carrot Block", "x": 23, "y": 23, "misc": 32}], + "Pumpkin Zone Secret Course 1": [ + {"screen": 0, "sprite": "Carrot Block", "x": 29, "y": 29, "misc": 0}, + {"screen": 1, "sprite": "Paragoomba (Vertical)", "x": 22, "y": 0, "misc": 32}, + {"screen": 2, "sprite": "Paragoomba (Vertical)", "x": 14, "y": 2, "misc": 32}, + {"screen": 3, "sprite": "Paragoomba (Vertical)", "x": 6, "y": 4, "misc": 32}, + {"screen": 3, "sprite": "Paragoomba (Vertical)", "x": 30, "y": 30, "misc": 0}, + {"screen": 4, "sprite": "Paragoomba (Vertical)", "x": 23, "y": 4, "misc": 32}, + {"screen": 5, "sprite": "Goomba", "x": 15, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Goomba", "x": 25, "y": 30, "misc": 32}, + {"screen": 6, "sprite": "Goomba", "x": 19, "y": 30, "misc": 32}, + {"screen": 6, "sprite": "Goomba", "x": 29, "y": 30, "misc": 32}, + {"screen": 7, "sprite": "Goomba", "x": 11, "y": 30, "misc": 32}, + {"screen": 7, "sprite": "Goomba", "x": 29, "y": 30, "misc": 32}, + {"screen": 8, "sprite": "Goomba", "x": 15, "y": 30, "misc": 32}, + {"screen": 8, "sprite": "Star", "x": 25, "y": 24, "misc": 32}], + "Space Zone Secret Course": [ + {"screen": 2, "sprite": "Rerere/Poro", "x": 2, "y": 2, "misc": 32}, + {"screen": 3, "sprite": "Rerere/Poro", "x": 14, "y": 20, "misc": 0}, + {"screen": 4, "sprite": "Heart", "x": 3, "y": 22, "misc": 32}, + {"screen": 4, "sprite": "Heart", "x": 5, "y": 22, "misc": 32}, + {"screen": 4, "sprite": "Rerere/Poro", "x": 26, "y": 2, "misc": 32}, + {"screen": 5, "sprite": "Heart Block", "x": 21, "y": 19, "misc": 0}], + "Tree Zone Secret Course": [ + {"screen": 1, "sprite": "Mushroom Block", "x": 3, "y": 13, "misc": 32}, + {"screen": 1, "sprite": "Koopa Troopa", "x": 9, "y": 24, "misc": 32}, + {"screen": 1, "sprite": "Koopa Troopa", "x": 13, "y": 14, "misc": 32}, + {"screen": 1, "sprite": "Koopa Troopa", "x": 25, "y": 16, "misc": 32}, + {"screen": 2, "sprite": "Koopa Troopa", "x": 3, "y": 20, "misc": 32}, + {"screen": 2, "sprite": "Koopa Troopa", "x": 19, "y": 22, "misc": 32}, + {"screen": 2, "sprite": "Koopa Troopa", "x": 27, "y": 22, "misc": 32}, + {"screen": 3, "sprite": "Koopa Troopa", "x": 11, "y": 24, "misc": 32}, + {"screen": 3, "sprite": "Koopa Troopa", "x": 19, "y": 24, "misc": 32}, + {"screen": 3, "sprite": "Koopa Troopa", "x": 31, "y": 26, "misc": 32}, + {"screen": 4, "sprite": "Koopa Troopa", "x": 9, "y": 30, "misc": 32}, + {"screen": 4, "sprite": "Koopa Troopa", "x": 17, "y": 30, "misc": 32}, + {"screen": 4, "sprite": "Koopa Troopa", "x": 25, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Koopa Troopa", "x": 1, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Koopa Troopa", "x": 11, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Koopa Troopa", "x": 17, "y": 30, "misc": 32}, + {"screen": 5, "sprite": "Koopa Troopa", "x": 29, "y": 30, "misc": 32}, + {"screen": 6, "sprite": "Heart Block", "x": 9, "y": 13, "misc": 32}, + {"screen": 6, "sprite": "Koopa Troopa", "x": 17, "y": 26, "misc": 32}, + {"screen": 6, "sprite": "Koopa Troopa", "x": 27, "y": 26, "misc": 32}, + {"screen": 7, "sprite": "Koopa Troopa", "x": 1, "y": 30, "misc": 32}, + {"screen": 7, "sprite": "Koopa Troopa", "x": 11, "y": 30, "misc": 32}, + {"screen": 7, "sprite": "Koopa Troopa", "x": 21, "y": 30, "misc": 32}, + {"screen": 7, "sprite": "Koopa Troopa", "x": 31, "y": 30, "misc": 32}, + {"screen": 8, "sprite": "Koopa Troopa", "x": 7, "y": 30, "misc": 32}, + {"screen": 8, "sprite": "Koopa Troopa", "x": 17, "y": 30, "misc": 32}, + {"screen": 8, "sprite": "Koopa Troopa", "x": 27, "y": 30, "misc": 32}, + {"screen": 9, "sprite": "Koopa Troopa", "x": 5, "y": 30, "misc": 32}, + {"screen": 9, "sprite": "Koopa Troopa", "x": 13, "y": 30, "misc": 32}, + {"screen": 9, "sprite": "Koopa Troopa", "x": 21, "y": 30, "misc": 32}, + {"screen": 9, "sprite": "Heart Block", "x": 31, "y": 13, "misc": 32}], + "Macro Zone Secret Course": [ + {"screen": 1, "sprite": "Mushroom Block", "x": 11, "y": 15, "misc": 32}, + {"screen": 1, "sprite": "Falling Platform", "x": 28, "y": 26, "misc": 32}, + {"screen": 2, "sprite": "Falling Platform", "x": 12, "y": 26, "misc": 32}, + {"screen": 2, "sprite": "Falling Platform", "x": 18, "y": 26, "misc": 32}, + {"screen": 3, "sprite": "Heart Block", "x": 11, "y": 21, "misc": 32}, + {"screen": 5, "sprite": "Flower Block", "x": 17, "y": 17, "misc": 32}, + {"screen": 6, "sprite": "Heart Block", "x": 25, "y": 27, "misc": 32}], + "Pumpkin Zone Secret Course 2": [ + {"screen": 0, "sprite": "Flower Block", "x": 25, "y": 11, "misc": 0}, + {"screen": 1, "sprite": "Heart Block", "x": 9, "y": 17, "misc": 0}, + {"screen": 1, "sprite": "Koopa Troopa", "x": 16, "y": 20, "misc": 0}, + {"screen": 1, "sprite": "Heart Block", "x": 23, "y": 15, "misc": 0}, + {"screen": 1, "sprite": "Koopa Troopa", "x": 29, "y": 20, "misc": 0}, + {"screen": 2, "sprite": "Heart Block", "x": 5, "y": 13, "misc": 0}, + {"screen": 2, "sprite": "Koopa Troopa", "x": 13, "y": 20, "misc": 0}, + {"screen": 2, "sprite": "Goomba", "x": 21, "y": 20, "misc": 0}, + {"screen": 2, "sprite": "Koopa Troopa", "x": 31, "y": 20, "misc": 0}, + {"screen": 3, "sprite": "Heart Block", "x": 17, "y": 17, "misc": 0}, + {"screen": 3, "sprite": "Heart Block", "x": 25, "y": 13, "misc": 0}, + {"screen": 4, "sprite": "Koopa Troopa", "x": 1, "y": 20, "misc": 0}, + {"screen": 4, "sprite": "Falling Platform", "x": 18, "y": 24, "misc": 0}, + {"screen": 5, "sprite": "Falling Platform", "x": 2, "y": 22, "misc": 0}, + {"screen": 5, "sprite": "Koopa Troopa", "x": 10, "y": 22, "misc": 0}, + {"screen": 5, "sprite": "Mushroom Block", "x": 11, "y": 15, "misc": 0}, + {"screen": 5, "sprite": "Falling Platform", "x": 18, "y": 24, "misc": 0}, + {"screen": 5, "sprite": "Falling Platform", "x": 26, "y": 24, "misc": 0}, + {"screen": 6, "sprite": "Falling Platform", "x": 2, "y": 24, "misc": 0}, + {"screen": 6, "sprite": "Falling Platform", "x": 10, "y": 24, "misc": 0}, + {"screen": 6, "sprite": "Falling Platform", "x": 18, "y": 24, "misc": 0}, + {"screen": 7, "sprite": "Goomba", "x": 13, "y": 18, "misc": 0}, + {"screen": 7, "sprite": "Heart Block", "x": 15, "y": 17, "misc": 0}, + {"screen": 7, "sprite": "Goomba", "x": 29, "y": 18, "misc": 0}, + {"screen": 8, "sprite": "Heart Block", "x": 9, "y": 17, "misc": 0}, + {"screen": 8, "sprite": "Goomba", "x": 11, "y": 18, "misc": 0}, + {"screen": 8, "sprite": "Heart Block", "x": 23, "y": 17, "misc": 0}, + {"screen": 8, "sprite": "Goomba", "x": 25, "y": 18, "misc": 0}, + {"screen": 9, "sprite": "Falling Platform", "x": 12, "y": 20, "misc": 0}, + {"screen": 9, "sprite": "Heart", "x": 23, "y": 16, "misc": 0}, + {"screen": 10, "sprite": "Falling Platform", "x": 0, "y": 22, "misc": 0}, + {"screen": 10, "sprite": "Heart", "x": 17, "y": 14, "misc": 0}]} From 354accdf94a7ecdbf97cf9677a84aced672ab02d Mon Sep 17 00:00:00 2001 From: Alchav Date: Tue, 16 Apr 2024 11:30:41 -0400 Subject: [PATCH 054/113] Randomize platforms --- worlds/marioland2/__init__.py | 26 ++++++++++++++------------ worlds/marioland2/rom.py | 2 +- worlds/marioland2/sprite_randomizer.py | 19 ++++++++++++++++++- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 81ad71a0a1c0..d643e4a7f0df 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -13,7 +13,7 @@ auto_scroll_max) from .items import items from .sprites import level_sprites -from .sprite_randomizer import randomize_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 @@ -95,7 +95,8 @@ def __init__(self, world, player: int): def generate_early(self): self.sprite_data = deepcopy(level_sprites) - randomize_sprites(self.sprite_data, self.random) + randomize_enemies(self.sprite_data, self.random) + randomize_platforms(self.sprite_data, self.random) self.sprite_data["Mario's Castle"][35]["sprite"] = "Midway Bell" if self.options.auto_scroll_chances == -1: @@ -425,16 +426,17 @@ def create_items(self): else: self.multiworld.push_precollected(self.create_item("Pipe Traversal")) - 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 + 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: diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 06863cef8370..d6e64a9183e3 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -40,7 +40,7 @@ def randomize_sprite(data, random, arr, i): from .locations import level_id_to_name from .sprites import sprite_id_to_name -def randomize_enemies(data, random): +def z(data, random): i = 0xe077 level = 0 d = {} diff --git a/worlds/marioland2/sprite_randomizer.py b/worlds/marioland2/sprite_randomizer.py index 9bf054969081..abdb0a5194ad 100644 --- a/worlds/marioland2/sprite_randomizer.py +++ b/worlds/marioland2/sprite_randomizer.py @@ -1,6 +1,6 @@ -def randomize_sprites(sprite_data, random): +def randomize_enemies(sprite_data, random): for level, level_sprite_data in sprite_data.items(): shuffle = () if level in ("Mushroom Zone", "Macro Zone 4"): @@ -99,3 +99,20 @@ def randomize_sprites(sprite_data, random): print(f"Level: {level} - {old_sprite} changed to {sprite['sprite']}") elif level == "Mario's Castle" and sprite["sprite"] == "Karamenbo" and not random.randint(0, 9): sprite["y"] += 1 + + +def randomize_platforms(sprite_data, random): + shuffle = ("Moving Platform (Small, Vertical)", "Moving Platform (Large, Vertical)", + "Moving Platform (Small, Horizontal)", "Moving Platform (Large, Horizontal)", + "Moving Platform (Large, Diagonal)", "Falling Platform") + for sprite in sprite_data["Tree Zone 3"]: + if sprite["sprite"] in shuffle: + sprite["sprite"] = random.choice(shuffle) + shuffle = ("Cloud Platform (Horizontal)", "Owl Platform (Horizontal)/Cheep Cheep (Horizontal)") + for sprite in sprite_data["Tree Zone 5"]: + if sprite["sprite"] in shuffle: + sprite["sprite"] = random.choice(shuffle) + shuffle = ("Falling Bone Platform", "Rising Bone Platform", "Skull Platform") + for sprite in sprite_data["Mario's Castle"]: + if sprite["sprite"] in shuffle: + sprite["sprite"] = random.choice(shuffle) From d21f710df7c87f596439c3d376593fdc32ba8d07 Mon Sep 17 00:00:00 2001 From: Alchav Date: Fri, 3 May 2024 00:06:17 -0400 Subject: [PATCH 055/113] Mario's Castle Midway Bell --- worlds/marioland2/__init__.py | 27 ++- worlds/marioland2/basepatch.bsdiff4 | Bin 1155 -> 1241 bytes worlds/marioland2/items.py | 1 + worlds/marioland2/locations.py | 3 +- worlds/marioland2/options.py | 6 + worlds/marioland2/rom.py | 296 +++++++------------------ worlds/marioland2/rom_addresses.py | 15 +- worlds/marioland2/sprite_randomizer.py | 5 +- 8 files changed, 114 insertions(+), 239 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index d643e4a7f0df..834cebca081e 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -7,7 +7,7 @@ from BaseClasses import Region, Location, Item, ItemClassification, Tutorial from . import client -from .rom import generate_output, SuperMarioLand2DeltaPatch +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) @@ -23,7 +23,7 @@ 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 = [SuperMarioLand2DeltaPatch.hash] + md5s = [SuperMarioLand2ProcedurePatch.hash] rom_file: SML2RomFile = SML2RomFile(SML2RomFile.copy_to) @@ -97,7 +97,9 @@ def generate_early(self): self.sprite_data = deepcopy(level_sprites) randomize_enemies(self.sprite_data, self.random) randomize_platforms(self.sprite_data, self.random) - self.sprite_data["Mario's Castle"][35]["sprite"] = "Midway Bell" + + if self.options.marios_castle_midway_bell: + self.sprite_data["Mario's Castle"][35]["sprite"] = "Midway Bell" if self.options.auto_scroll_chances == -1: self.auto_scroll_levels = [int(i in [19, 25, 30]) for i in range(32)] @@ -156,22 +158,26 @@ def create_regions(self): if "Midway Bell" in location_name and not self.options.shuffle_midway_bells: continue + 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 = Region("Mario's Castle", self.player, self.multiworld) - menu_region.connect(castle) + # castle = Region("Mario's Castle", self.player, self.multiworld) + # menu_region.connect(castle) + 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] - self.max_coin_locations = {region: len(coins_coords[region]) for region in created_regions} + 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 == "locations" 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): @@ -316,7 +322,9 @@ def set_rules(self): self.player), "Turtle Zone 3 - Boss": lambda state: has_pipe_right(state, self.player), "Mario's Castle - Wario": lambda state: has_pipe_right( - state, self.player) and has_pipe_left(state, self.player) + state, self.player) and has_pipe_left(state, self.player), + "Mario's Castle - Midway Bell": lambda state: (has_pipe_right(state, self.player) and has_pipe_left( + state, self.player)) or state.has("Mario's Castle Midway Bell", self.player) } for entrance, rule in entrance_rules.items(): @@ -409,7 +417,8 @@ def create_items(self): if self.options.shuffle_midway_bells: for item in [item for item in items if "Midway Bell" in item]: - item_counts[item] = 1 + 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 diff --git a/worlds/marioland2/basepatch.bsdiff4 b/worlds/marioland2/basepatch.bsdiff4 index fd667da11a169cf7b5505037d90ca65f4cee57cc..a8818419a6b8896c9621fe26d74f82f9cc173853 100644 GIT binary patch delta 1234 zcmV;@1TFi63E2q|LQ_OZMn*I+fB*mh00000@{ti4e|9=_H2?ri-+TYPfItwC00;sA z0sue|7yyI_2m$~=0ssL3pa4`wJg2Fqh9G2Vp|l2+*q|9bMu0K`A_X-_ndNQ|xy<#0 zGXMw=V&WbO0F;~vXAOI&Z4iip`XKa#LRcZH=$@MVraG@aVbm(eAT4*^j zL0KkKe_1CIzm5O^EPwz1|9yJnarajJq6O*32xvn5c=a>k6~VE6R7^zg-hKYS0#^i? zO`-)9$)+Pf0AvP@00E#h4FRCg(9=Kw4LwFc^)z~nnlOOSH8g@6o|;V?NB|8DG|&J5 z00w{n000dEpcw!F+C&;;VKixk7=|X80x~qje=#ruWWWJ34Hy6dGGQ`c34~ygMKMhp zjWqIysgpw^Mw$QyLr+iuWB>pGpfUhy>HyHt2a{489^462K3zL7$cYn;3viPNj5Hh* zGlao+DLR}b7A~b!K>kNHyOz8tjy2#DGv4sstCiDgSU_(x8j(d7lCZ%yQK1UipjkU; ze*&{$L4oOFNm?`rz+w=PYZ^%amldReTeJcUYI%D=0MFt1R)dG)_#E42S^+gkiIU>b0$Fs?2=^5Ln39 z#T(}EAcat8Plw~#|D;I7CS)Srw{i-1p8;7pc*#A4RdgD?Id@Pe0N<=ioatGZe+}=f zBoZg2C=eQl4jb#1fzt*KTZpSf9tj9Ago{V=6({LL7s!Vf>r|b94__{07P!69D?8kt zBym`;m`LayC?GD_EvJ49wflfo?j4DJVu|^ag~k*l75Oe_k%+|O5^>Ls)kEE_9~SL} zdd3Ww0)PTwU;%_&G;k~!$z4TJe-AIh31>O}%7D`ky#;ph-vfkj@pmLsg$V+2EBN3-T4*^jL0KkKS#GcV!TOt@2fCHB-zcW_&+{Oe2~pF3qD?UvhJ@ z04vvn@PH>Z$RR;)$jdU}J%PxtDGX3U!oXr4Ab~y#5k5_j_=q9$za4!cEM!&}fXMM? zZ(nbHD5yXqcJ(@HEOTy?pXs#&NY@$6_Vk42ez{qOWD?!#5g(nII6povL}e3#>xn&( wg))*$AgB7!$#qHUwL}5{hSRY;D&hc`2nehCkcs7jF(2aYNT&)C4b}gcKsP=9SO5S3 delta 1147 zcmV->1cdw934;j{LQ_OZMn*I+Q~&?~00000?2!=}f8DU$?EnBISa|(7f)Ef8KmY&$ zga7~n0RW%?A(YS=0iaU}rhwBDGlWF*RiiSHK<-c%bR&(S$PJfux0Y4%d|k;D;X*@p z!*jGkT4*^jL0KkKS^t%Or~m*e|NsC0eR|_@_g4L=1?k2JXhQsW^)uiV!LfZ*OhoVA zeg41#e>Vh~L^U*!)Wp*uX`@3x4F-VGp`ZW&&;vt2Xaj0#pwMKmsr>W>o z8&GHs0BPy~0001J0000000STZ4^V?9089yl#L&@+iGWN3WDN}(VlWd!07C){m;eA1 z18GE_kq{3^$kRY*0MG}h(?DnpKmZ1T>H(pqe}h088UXa4s(!WUxkmkjSOk$A!#2<% zql^VzlVb#e>{?|oR&-qptf{;V9yw$}s%s&oJ6`92^0eTsnJ7U##CKC7s{)3Q+*pKV zn30cn1d5F%29oo%0c6rbticEpx?l+)<1i94Y)J`WTIDRz1;uy*ouB{$s{tx)0$*kT ze*h6h&ekzlx%djniQvJtNi9P~S>+ThF`bl!JZUJji<#!^c3~+buiCGO!9ZE%4ba(hM@PxXo)kmC z^Or#HE_R%~dj~_5$~h{TdO&~x+sfL~e`!rp7*SJM<3S|`AzwZd^|M^gvtF$91gOT? zL|3iWfQ%H^Q;Fo-e`x})^sG1d9w@OR2msXaYu!JGU0 zppkWuK!M<&;fvdrVJJ|uVyYShXC@;B(9^T+l%fB%7;czyNo@0E13uJ5bje4ye=5V% z7|Qa7f<5qbCikC8(5CAd3z!+rilg*fI?bmIWa+5CR16u6n-)Q0EYkn`JjNr zbG6_Fe$?W{!O(%z_CN#xR6x)G3~T^UKoCS3umIVu5h5A^pwXZ-XblFMe*hX}008|> z2nM2`q?noj0B8UJ0004?005ClP!Cf~Kxv_%003wJ15G_38zh`UR^TKhG!zmNuPq1F zIKsaSH>%53V947TemG4oq{^CXO}LlviUe|7`MXDVhsW)VFp8x|bq*N4XA`{^gU%=D=ivP?+%ePeeY+lj-T z;Tw~1tj5p*UHk+O8@PHNh3wd_ywUa|(`_bH%kjuAbxJ?gNC7B-?PXA92ugd}!Y>H_ N7ji{7P>{M5elncl^9}$2 diff --git a/worlds/marioland2/items.py b/worlds/marioland2/items.py index 74b0b8eb6bc4..df598375d3fe 100644 --- a/worlds/marioland2/items.py +++ b/worlds/marioland2/items.py @@ -68,6 +68,7 @@ "Turtle Zone 1 Midway Bell": ItemClassification.filler, "Turtle Zone 2 Midway Bell": ItemClassification.progression_skip_balancing, "Turtle Zone 3 Midway Bell": ItemClassification.filler, + "Mario's Castle Midway Bell": ItemClassification.progression_skip_balancing, "1 Coin": ItemClassification.filler, **{f"{i} Coins": ItemClassification.filler for i in range(2, 169)} } diff --git a/worlds/marioland2/locations.py b/worlds/marioland2/locations.py index 0e53abb69d12..a2a9cb639812 100644 --- a/worlds/marioland2/locations.py +++ b/worlds/marioland2/locations.py @@ -59,7 +59,8 @@ 'Turtle Zone 2 - Midway Bell': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone 2 Midway Bell", 1), 'type': 'bell'}, 'Turtle Zone 3 - Boss': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Coin", 1), 'type': 'level'}, 'Turtle Zone 3 - Midway Bell': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Zone 3 Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone Secret Course - Normal Exit': {'id': 0x1A, 'ram_index': 37, 'type': 'level'} + 'Turtle Zone Secret Course - Normal Exit': {'id': 0x1A, 'ram_index': 37, 'type': 'level'}, + "Mario's Castle - Midway Bell": {'id': 24, 'ram_index': 24, 'clear_condition': ("Mario's Castle Midway Bell", 1), 'type': 'bell'}, } diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 0a8019ab965b..bfddc91652f8 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -48,6 +48,11 @@ class ShuffleMidwayBells(Toggle): display_name = "Shuffle Midway Bells" +class MariosCastleMidwayBell(Toggle): + """Adds a Midway Bell to the final stage, just before the Wario fight.""" + display_name = "Mario's Castle Midway Bell" + + class Coinsanity(Toggle): """Shuffles the singular coins found freestanding and in question mark blocks into the item pool, and adds location checks made by obtaining a sufficient number of coins in particular levels within a single playthrough.""" @@ -148,6 +153,7 @@ class SML2Options(PerGameCommonOptions): coinsanity: Coinsanity coinsanity_checks: CoinsanityChecks shuffle_midway_bells: ShuffleMidwayBells + marios_castle_midway_bell: MariosCastleMidwayBell shuffle_pipe_traversal: ShufflePipeTraversal auto_scroll_mode: AutoScrollMode auto_scroll_chances: AutoScrollChances diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index d6e64a9183e3..c8ea285ec087 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -5,283 +5,143 @@ import Utils -from worlds.Files import APDeltaPatch +from worlds.Files import APProcedurePatch, APTokenMixin, APTokenTypes from settings import get_settings from .rom_addresses import rom_addresses from .sprites import sprite_name_to_id -# Enemy and Platform randomizer ported directly from SML2R -# https://github.com/slashinfty/sml2r-node/blob/862128c73d336d6cbfbf6290c09f3eff103688e8/src/index.ts#L284 - -def sprite_extract(a, b): - x = ((0b00010000 & a) << 2) - y = ((0b11100000 & a) >> 2) - z = ((0b11100000 & b) >> 5) - return x | y | z - - -def sprite_insert(a, b, s): - x = ((s & 0b01000000) >> 2) - y = ((s & 0b00111000) << 2) - z = ((s & 0b00000111) << 5) - return [(a & 0b00001111) | x | y, (b & 0b00011111) | z] - - -def copy_sprite(data, arr, pos): - for i in range(2): - data[pos + i] = arr[i] - - -def randomize_sprite(data, random, arr, i): - selected_sprite = sprite_insert(data[i], data[i + 1], random.choice(arr)) - copy_sprite(data, selected_sprite, i) - -from .locations import level_id_to_name -from .sprites import sprite_id_to_name -def z(data, random): - i = 0xe077 - level = 0 - d = {} - d[level_id_to_name[level]] = [] - while True: - if data[i] == 0xFF: - level += 1 - i += 1 - if level > len(level_id_to_name) - 1: - break - d[level_id_to_name[level]] = [] - sprite = data[i:i+3] - screen = sprite[0] & 0b00001111 - x = sprite[1] & 0b00011111 - y = sprite[2] & 0b00011111 - sprite_id = sprite_id_to_name[((sprite[0] & 0b11100000) >> 2) | ((sprite[0] & 0b00010000) << 2) | ((sprite[1] & 0b11100000) >> 5)] - ezmode = sprite[2] & 0b11100000 - d[level_id_to_name[level]].append({"screen": screen, "sprite_id": sprite_id, "x": x, "y": y, "misc": ezmode}) - i += 3 - breakpoint() - level_list = [ - {"enemies": [0x01, 0x08, 0x09, 0x3A], "start": 0xE077, "end": 0xE0BC}, # lv00 - {"enemies": [0x01, 0x08, 0x09, 0x3A], "start": 0xE955, "end": 0xE99D}, # lv17 - {"enemies": [0x08, 0x09, 0x3A], "start": 0xEA2F, "end": 0xEA7D}, # lv19 - {"enemies": [0x08, 0x09, 0x3A], "start": 0xEAA3, "end": 0xEACD}, # lv1B - {"enemies": [0x1F, 0x20, 0x21, 0x22], "start": 0xE0BD, "end": 0xE123}, # lv01 - {"enemies": [0x44, 0x58], "start": 0xE124, "end": 0xE181}, # lv02 - {"enemies": [0x35, 0x3E, 0x40, 0x41, 0x42], "start": 0xE182, "end": 0xE1EE}, # lv03 - {"enemies": [0x33, 0x34, 0x5D], "start": 0xE1EF, "end": 0xE249}, # lv04 - {"enemies": [0x08, 0x39, 0x3A], "start": 0xE24A, "end": 0xE2A1}, # lv05 - {"enemies": [0x4D, 0x54, 0x55, 0x56, 0x5E, 0x5F], "start": 0xE30C, "end": 0xE384}, # lv07 - {"enemies": [0x4D, 0x57], "start": 0xE385, "end": 0xE3D3}, # lv08 - {"enemies": [0x01, 0x40, 0x4B], "start": 0xE432, "end": 0xE49B}, # lv0A - {"enemies": [0x08, 0x09, 0x3A, 0x44, 0x4D], "start": 0xE49C, "end": 0xE4F9}, # lv0B - {"enemies": [0x05, 0x06, 0x07, 0x08, 0x09, 0x0B, 0x3A, 0x3D], "start": 0xE5C2, "end": 0xE62B}, # lv0E - {"enemies": [0x05, 0x39, 0x57, 0x5B], "start": 0xE706, "end": 0xE77B}, # lv11 - {"enemies": [0x5C, 0x5E, 0x5F], "start": 0xE7C8, "end": 0xE822}, # lv13 - {"enemies": [0x22, 0x23, 0x25, 0x27], "start": 0xE823, "end": 0xE88F}, # lv14 - {"enemies": [0x07, 0x33, 0x34, 0x3D, 0x5D], "start": 0xE890, "end": 0xE8F6}, # lv15 - {"enemies": [0x01, 0x08, 0x09, 0x34, 0x3A, 0x55], "start": 0xE8F7, "end": 0xE954}, # lv16 - {"enemies": [0x68, 0x69], "start": 0xE99E, "end": 0xEA2E}, # lv18a - {"enemies": [0x6E, 0x6F], "start": 0xE99E, "end": 0xEA2E}, # lv18b - {"enemies": [0x01, 0x09], "start": 0xEB55, "end": 0xEBB5} # lv1F - ] - for level in level_list: - i = level["start"] - while i < level["end"]: - sprite = sprite_extract(data[i], data[i+1]) - if data[i] == 0xFF: - i -= 2 - elif sprite in level["enemies"]: - randomize_sprite(data, random, level["enemies"], i) - i += 3 - for i in range(0xE2A2, 0xE30B, 3): # lvl06 - sprite = sprite_extract(data[i], data[i+1]) - if sprite == 0x4E: - randomize_sprite(data, random, [0x4D, 0x4E, 0x51, 0x53], i) - elif sprite == 0x4F: - randomize_sprite(data, random, [0x4D, 0x4F, 0x51, 0x53], i) - elif sprite in (0x4D, 0x51, 0x53): - randomize_sprite(data, random, [0x4D, 0x51, 0x53], i) - for i in range(0xE3D4, 0xE431, 3): # lvl09 - sprite = sprite_extract(data[i], data[i + 1]) - if sprite == 0x4F: - randomize_sprite(data, random, [0x4D, 0x4F, 0x53, 0x5A, 0x5C], i) - elif sprite in (0x4D, 0x53, 0x5a, 0x5C): - randomize_sprite(data, random, [0x4D, 0x53, 0x5A, 0x5C], i) - for i in range(0xE4FA, 0xE560, 3): # lvl0c - sprite = sprite_extract(data[i], data[i + 1]) - if sprite == 0x49: - randomize_sprite(data, random, [0x01, 0x47, 0x48, 0x49, 0x53], i) - elif sprite in (0x01, 0x47, 0x48): - randomize_sprite(data, random, [0x01, 0x47, 0x48, 0x53], i) - for i in range(0xE561, 0xE5C1, 3): # lvl0D - sprite = sprite_extract(data[i], data[i + 1]) - if sprite == 0x43: - randomize_sprite(data, random, [0x09, 0x43, 0x4D, 0x53], i) - elif sprite == 0x4C: - randomize_sprite(data, random, [0x09, 0x4C, 0x4D, 0x53], i) - elif sprite in (0x09, 0x4D): - randomize_sprite(data, random, [0x09, 0x4D, 0x53], i) - for i in range(0xE6C0, 0xE705, 3): # lvl10 - sprite = sprite_extract(data[i], data[i + 1]) - if sprite == 0x21: - randomize_sprite(data, random, [0x01, 0x08, 0x20, 0x21, 0x3A, 0x55], i) - elif sprite in (0x01, 0x08, 0x20, 0x3A, 0x55): - randomize_sprite(data, random, [0x01, 0x08, 0x20, 0x3A, 0x55], i) - for i in range(0xE77C, 0xE7C7, 3): # lvl12 - sprite = sprite_extract(data[i], data[i + 1]) - if sprite == 0x4D: - randomize_sprite(data, random, [0x4D, 0x58], i) - elif sprite in (0x58, 0x5A): - randomize_sprite(data, random, [0x4D, 0x58, 0x5A], i) - # Thwomps in Wario's Castle - for i in [0xE9D6, 0xE9D9, 0xE9DF, 0xE9E2, 0xE9E5]: - data[i] = 0x34 if random.randint(0, 9) else 0x35 - # Piranha plants - i = 0xE077 - while i < 0xEBB5: - if i < 0xE30C or 0xE384 < i < 0xE3D4 or 0xE431 < i < 0xE8F7 or i > 0xE954: - sprite = sprite_extract(data[i], data[i + 1]) - if data[i] == 0xFF: - i -= 2 - elif sprite in (0x0C, 0x0D): - copy_sprite(data, sprite_insert(data[i], data[i+1], random.choice([0x0C, 0x0D])), i) - i += 3 - - -def randomize_platforms(data, random): - level_list = [ - {"platforms": [0x28, 0x29, 0x2A, 0x2B, 0x2D, 0x2E], "start": 0xE1EF, "end": 0xE249}, - {"platforms": [0x38, 0x3D], "start": 0xE24A, "end": 0xE2A1}, - {"platforms": [0x60, 0x61, 0x67], "start": 0xE99E, "end": 0xEA2E} - ] - for level in level_list: - i = level["start"] - while i < level["end"]: - sprite = sprite_extract(data[i], data[i + 1]) - if sprite == 0xFF: - i -= 2 - elif sprite in level["platforms"]: - randomize_sprite(data, random, level["platforms"], i) - i += 3 - for i in range(0xE9A3, 0xE9CE, 3): - data[i] = (0x57 if data[i] == 0x5E else 0x38) + random.randint(0, 7) - - -def randomize_music(data, random): +def randomize_music(patch, random): # overworld overworld_music_tracks = [0x05, 0x06, 0x0D, 0x0E, 0x10, 0x12, 0x1B, 0x1C, 0x1E] random.shuffle(overworld_music_tracks) for i, track in zip([0x3004F, 0x3EA9B, 0x3D186, 0x3D52B, 0x3D401, 0x3D297, 0x3D840, 0x3D694, 0x3D758], overworld_music_tracks): - data[i] = track + patch.write_bytes(i, track) # levels for i in range(0x5619, 0x5899, 0x14): - data[i] = random.choice([0x01, 0x0B, 0x11, 0x13, 0x14, 0x17, 0x1D, 0x1F, 0x28]) + patch.write_bytes(i, random.choice([0x01, 0x0B, 0x11, 0x13, 0x14, 0x17, 0x1D, 0x1F, 0x28])) def generate_output(self, output_directory: str): - data = get_base_rom_bytes() - base_patch = pkgutil.get_data(__name__, f'basepatch.bsdiff4') - - data = bytearray(bsdiff4.patch(data, base_patch)) - + # data = get_base_rom_bytes() + # base_patch = pkgutil.get_data(__name__, f'basepatch.bsdiff4') + + # data = bytearray(bsdiff4.patch(data, base_patch)) + + # outfilepname = f'_P{self.player}' + # outfilepname += f"_{self.multiworld.get_file_safe_player_name(self.player).replace(' ', '_')}" \ + # if self.multiworld.player_name[self.player] != 'Player%d' % self.player else '' + # rompath = os.path.join(output_directory, f'AP_{self.multiworld.seed_name}{outfilepname}.gb') + # with open(rompath, 'wb') as outfile: + # outfile.write(data) + # patch = SuperMarioLand2ProcedurePatch(os.path.splitext(rompath)[0] + # + SuperMarioLand2ProcedurePatch.patch_file_ending, + # player=self.player, player_name=self.multiworld.player_name[self.player], + # patched_path=rompath) + patch = SuperMarioLand2ProcedurePatch(player=self.player, player_name=self.multiworld.player_name[self.player]) + + patch.write_file("basepatch.bsdiff4", pkgutil.get_data(__name__, "basepatch.bsdiff4")) random = self.random - # if self.options.randomize_enemies: - # randomize_enemies(data, random) - # if self.options.randomize_platforms: - # randomize_platforms(data, random) - - data[0x4F012] = 0x5D # Midway bell Mario's Castle + if self.options.marios_castle_midway_bell: + # Remove Question Mark Block + patch.write_bytes(0x4F012, 0x5D) + # Fix level pointer to read midway bell flag + patch.write_bytes(0x3E569, 0x18) + patch.write_bytes(0x3E56A, 0x18) + # Position and screen coordinates + patch.write_bytes(0x383B, [0xD4, 0x01, 0x4D, 0x0A, 0xC0, 0x01, 0x50, 0x0A]) if self.options.coinsanity: # Add platform to return to start of Pumpkin Zone Secret Course 1 - data[0x258B6] = 0x3B - data[0x258F8] = 0x7a - data[0x2594D] = 0x67 - data[0x259A8] = 0x68 - data[0x259A9] = 0x60 + patch.write_bytes(0x258B6, 0x3B) + patch.write_bytes(0x258F8, 0x7a) + patch.write_bytes(0x2594D, 0x67) + patch.write_bytes(0x259A8, 0x68) + patch.write_bytes(0x259A9, 0x60) i = 0xe077 for level, sprites in self.sprite_data.items(): for sprite_data in sprites: sprite_id = sprite_name_to_id[sprite_data["sprite"]] - data[i] = ((sprite_id & 0b01000000) >> 2) | ((sprite_id & 0b00111000) << 2) | sprite_data["screen"] - data[i + 1] = ((sprite_id & 0b00000111) << 5) | sprite_data["x"] - data[i + 2] = sprite_data["misc"] | sprite_data["y"] + data = [((sprite_id & 0b01000000) >> 2) | ((sprite_id & 0b00111000) << 2) | sprite_data["screen"], + ((sprite_id & 0b00000111) << 5) | sprite_data["x"], + sprite_data["misc"] | sprite_data["y"]] + patch.write_bytes(i, data) i += 3 - data[i] = 255 + patch.write_bytes(i, 255) i += 1 if self.options.randomize_music: - randomize_music(data, random) + randomize_music(patch, random) # if self.options.auto_scroll_trap: - # data[rom_addresses["Auto_Scroll_Disable"]] = 0xAF + # patch.write_bytes(rom_addresses["Auto_Scroll_Disable"]] = 0xAF if self.options.shuffle_golden_coins: - data[rom_addresses["Coin_Shuffle"]] = 0x40 + patch.write_bytes(rom_addresses["Coin_Shuffle"], 0x40) if self.options.shuffle_midway_bells: - data[rom_addresses["Disable_Midway_Bell"]] = 0xC9 + patch.write_bytes(rom_addresses["Disable_Midway_Bell"], 0xC9) if self.options.coinsanity: for section in ("A", "B"): for i in range(0, 30): - data[rom_addresses[f"Coinsanity_{section}"] + i] = 0x00 + patch.write_bytes(rom_addresses[f"Coinsanity_{section}"] + i, 0x00) star_count = max(len([loc for loc in self.multiworld.get_filled_locations() if loc.item.player == self.player and loc.item.name == "Super Star Duration Increase"]), 1) - data[rom_addresses["Star_Count"]] = star_count // 256 - data[rom_addresses["Star_Count"] + 1] = star_count - (star_count // 256) + patch.write_bytes(rom_addresses["Star_Count"], star_count // 256) + patch.write_bytes(rom_addresses["Star_Count"] + 1, star_count - (star_count // 256)) if self.options.shuffle_golden_coins == "mario_coin_fragment_hunt": - data[rom_addresses["Coins_Required"]] = self.coin_fragments_required // 256 - data[rom_addresses["Coins_Required"] + 1] = self.coin_fragments_required % 256 - data[rom_addresses["Required_Golden_Coins"]] = 6 + patch.write_bytes(rom_addresses["Coins_Required"], self.coin_fragments_required // 256) + patch.write_bytes(rom_addresses["Coins_Required"] + 1, self.coin_fragments_required % 256) + patch.write_bytes(rom_addresses["Required_Golden_Coins"], 6) else: - data[rom_addresses["Coins_Required"] + 1] = self.options.required_golden_coins.value - data[rom_addresses["Required_Golden_Coins"]] = self.options.required_golden_coins.value - data[rom_addresses["Midway_Bells"]] = self.options.shuffle_midway_bells.value - data[rom_addresses["Energy_Link"]] = self.options.energy_link.value - data[rom_addresses["Difficulty_Mode"]] = self.options.difficulty_mode.value - data[rom_addresses["Coin_Mode"]] = self.options.shuffle_golden_coins.value + patch.write_bytes(rom_addresses["Coins_Required"] + 1, self.options.required_golden_coins.value) + patch.write_bytes(rom_addresses["Required_Golden_Coins"], self.options.required_golden_coins.value) + patch.write_bytes(rom_addresses["Midway_Bells"], self.options.shuffle_midway_bells.value) + patch.write_bytes(rom_addresses["Energy_Link"], self.options.energy_link.value) + patch.write_bytes(rom_addresses["Difficulty_Mode"], self.options.difficulty_mode.value) + patch.write_bytes(rom_addresses["Coin_Mode"], self.options.shuffle_golden_coins.value) for level, i in enumerate(self.auto_scroll_levels): # We set 0 if no auto scroll or auto scroll trap, so it defaults to no auto scroll. 1 if always or cancel items. - data[rom_addresses["Auto_Scroll_Levels"] + level] = max(0, i - 1) - data[rom_addresses["Auto_Scroll_Levels_B"] + level] = i + patch.write_bytes(rom_addresses["Auto_Scroll_Levels"] + level, max(0, i - 1)) + patch.write_bytes(rom_addresses["Auto_Scroll_Levels_B"] + level, i) if self.options.energy_link: # start with 1 life if Energy Link is on so that you don't deposit lives at the start of the game. - data[rom_addresses["Starting_Lives"]] = 1 + patch.write_bytes(rom_addresses["Starting_Lives"], 1) 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))) - write_bytes(data, rom_name, 0x77777) - - outfilepname = f'_P{self.player}' - outfilepname += f"_{self.multiworld.get_file_safe_player_name(self.player).replace(' ', '_')}" \ - if self.multiworld.player_name[self.player] != 'Player%d' % self.player else '' - rompath = os.path.join(output_directory, f'AP_{self.multiworld.seed_name}{outfilepname}.gb') - with open(rompath, 'wb') as outfile: - outfile.write(data) - patch = SuperMarioLand2DeltaPatch(os.path.splitext(rompath)[0] + SuperMarioLand2DeltaPatch.patch_file_ending, - player=self.player, player_name=self.multiworld.player_name[self.player], - patched_path=rompath) - patch.write() - os.unlink(rompath) + patch.write_bytes(0x77777, rom_name) + patch.write_file("tokens.bin", patch.get_token_binary()) + patch.write(os.path.join(output_directory, + f"{self.multiworld.get_out_file_name_base(self.player)}{patch.patch_file_ending}")) -class SuperMarioLand2DeltaPatch(APDeltaPatch): +class SuperMarioLand2ProcedurePatch(APProcedurePatch, APTokenMixin): hash = "a8413347d5df8c9d14f97f0330d67bce" patch_file_ending = ".apsml2" game = "Super Mario Land 2" result_file_ending = ".gb" + procedure = [ + ("apply_bsdiff4", ["basepatch.bsdiff4"]), + ("apply_tokens", ["tokens.bin"]), + ] @classmethod def get_source_data(cls) -> bytes: return get_base_rom_bytes() + def write_bytes(self, offset, value): + if isinstance(value, int): + value = [value] + self.write_token(APTokenTypes.WRITE, offset, bytes(value)) + def get_base_rom_bytes(): file_name = get_base_rom_path() @@ -290,7 +150,7 @@ def get_base_rom_bytes(): basemd5 = hashlib.md5() basemd5.update(base_rom_bytes) - if SuperMarioLand2DeltaPatch.hash != basemd5.hexdigest(): + if SuperMarioLand2ProcedurePatch.hash != basemd5.hexdigest(): raise Exception("Supplied Base Rom does not match known MD5 for Super Mario Land 1.0. " "Get the correct game and version, then dump it") return base_rom_bytes @@ -303,7 +163,7 @@ def get_base_rom_path(): return file_name -def write_bytes(data, byte_array, address): - for byte in byte_array: - data[address] = byte - address += 1 +# def write_bytes(patch, byte_array, address): +# for byte in byte_array: +# patch.write_bytes(address] = byte +# address += 1 diff --git a/worlds/marioland2/rom_addresses.py b/worlds/marioland2/rom_addresses.py index 1bb6cb5feea6..e4b4f69cd71e 100644 --- a/worlds/marioland2/rom_addresses.py +++ b/worlds/marioland2/rom_addresses.py @@ -1,5 +1,4 @@ rom_addresses = { - "Auto_Scroll_Disable": 0x4c0, "Space_Physics": 0x4e7, "Pipe_Traversal_A": 0x11a4, "Pipe_Traversal_SFX_A": 0x11a9, @@ -12,7 +11,6 @@ "Enable_Swim": 0x1d17, "Coinsanity_B": 0x1d86, "Auto_Scroll_Levels": 0x1f71, - "Auto_Scroll_Levels_B": 0x80134, "Starting_Lives": 0x2920, "Get_Hurt_To_Big_Mario": 0x31c7, "Get_Mushroom_A": 0x345c, @@ -31,10 +29,11 @@ "Get_Mushroom_B": 0x60ddb, "Get_Carrot_B": 0x60de7, "Get_Fire_Flower_B": 0x60df3, - "Coins_Required": 0x8012c, - "Difficulty_Mode": 0x8012e, - "Star_Count": 0x8012f, - "Midway_Bells": 0x80131, - "Energy_Link": 0x80132, - "Coin_Mode": 0x80133, + "Coins_Required": 0x80139, + "Difficulty_Mode": 0x8013b, + "Star_Count": 0x8013c, + "Midway_Bells": 0x8013e, + "Energy_Link": 0x8013f, + "Coin_Mode": 0x80140, + "Auto_Scroll_Levels_B": 0x80141, } diff --git a/worlds/marioland2/sprite_randomizer.py b/worlds/marioland2/sprite_randomizer.py index abdb0a5194ad..300b18e5e476 100644 --- a/worlds/marioland2/sprite_randomizer.py +++ b/worlds/marioland2/sprite_randomizer.py @@ -1,4 +1,5 @@ - +# Based on SML2R enemy and platform randomizer +# # https://github.com/slashinfty/sml2r-node/blob/862128c73d336d6cbfbf6290c09f3eff103688e8/src/index.ts#L284 def randomize_enemies(sprite_data, random): for level, level_sprite_data in sprite_data.items(): @@ -94,9 +95,7 @@ def randomize_enemies(sprite_data, random): if sprite["sprite"] in ("Piranha Plant", "Fire Piranha Plant"): shuffle = ("Piranha Plant", "Fire Piranha Plant") if sprite["sprite"] in shuffle: - old_sprite = sprite["sprite"] sprite["sprite"] = random.choice(shuffle) - print(f"Level: {level} - {old_sprite} changed to {sprite['sprite']}") elif level == "Mario's Castle" and sprite["sprite"] == "Karamenbo" and not random.randint(0, 9): sprite["y"] += 1 From 8c6e31cfe97e7d0f5f76fdba810510b0cce87efa Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 6 May 2024 16:47:26 -0400 Subject: [PATCH 056/113] Chaos auto scroll option --- worlds/marioland2/__init__.py | 13 +++++++------ worlds/marioland2/options.py | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 834cebca081e..87b15ed412e0 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -113,11 +113,12 @@ def generate_early(self): 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 == "level_cancel_or_trap_items": - if ((self.options.accessibility == "locations" - and level_id_to_name[level] in unbeatable_scroll_levels) - or self.random.randint(0, 1)): - self.auto_scroll_levels[level] = 2 + elif self.options.auto_scroll_mode == "chaos": + self.auto_scroll_levels[level] = self.random.randint(1, 3) + if (self.options.accessibility == "locations" + and level_id_to_name[level] in unbeatable_scroll_levels + and self.auto_scroll_levels[level] != 2): + self.auto_scroll_levels[level] = self.random.choice([1, 3]) elif (self.options.accessibility == "locations" and level_id_to_name[level] in unbeatable_scroll_levels): self.auto_scroll_levels[level] = 0 @@ -480,7 +481,7 @@ def create_items(self): item_counts[double_progression_item + " x2"] = 1 continue if self.options.auto_scroll_mode in ("level_trap_items", "level_cancel_items", - "level_cancel_or_trap_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 diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index bfddc91652f8..f51242fbf860 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -117,7 +117,7 @@ class AutoScrollMode(Choice): Level Trap Items: As with Trap Item, but there is a separate trap item for each auto scroll level. Global Cancel Item: Auto Scroll levels will stop auto-scrolling after obtaining the Auto Scroll Cancel item. Level Cancel Items: As with Cancel Item, but there is a separate cancel item for each auto scroll level. - Level Cancel Or Trap Items: Levels will randomly have Auto Scroll Trap or Auto Scroll Cancel items. + Chaos: Each level will randomly always auto scroll, have an Auto Scroll Trap, or have an Auto Scroll Cancel item. The effects of Trap and Cancel items are permanent! If Accessibility is not set to Locations, Traps may cause locations to become permanently unreachable. With individual level items, the number of auto scroll levels may be limited by the available space in the item @@ -128,7 +128,7 @@ class AutoScrollMode(Choice): option_level_trap_items = 2 option_global_cancel_item = 3 option_level_cancel_items = 4 - option_level_cancel_or_trap_items = 5 + option_chaos = 5 default = 0 From 457c44d9b4ea04774812eb0e882e8feedc577742 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 6 May 2024 23:24:42 -0400 Subject: [PATCH 057/113] Code cleanup --- worlds/marioland2/__init__.py | 3 +-- worlds/marioland2/rom.py | 26 ++------------------------ 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 87b15ed412e0..2ada93b832a9 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -167,8 +167,7 @@ def create_regions(self): 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 = Region("Mario's Castle", self.player, self.multiworld) - # menu_region.connect(castle) + castle = self.multiworld.get_region("Mario's Castle", self.player) wario = MarioLand2Location(self.player, "Mario's Castle - Wario", parent=castle) castle.locations.append(wario) diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index c8ea285ec087..1b6be20af78b 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -25,21 +25,7 @@ def randomize_music(patch, random): def generate_output(self, output_directory: str): - # data = get_base_rom_bytes() - # base_patch = pkgutil.get_data(__name__, f'basepatch.bsdiff4') - - # data = bytearray(bsdiff4.patch(data, base_patch)) - - # outfilepname = f'_P{self.player}' - # outfilepname += f"_{self.multiworld.get_file_safe_player_name(self.player).replace(' ', '_')}" \ - # if self.multiworld.player_name[self.player] != 'Player%d' % self.player else '' - # rompath = os.path.join(output_directory, f'AP_{self.multiworld.seed_name}{outfilepname}.gb') - # with open(rompath, 'wb') as outfile: - # outfile.write(data) - # patch = SuperMarioLand2ProcedurePatch(os.path.splitext(rompath)[0] - # + SuperMarioLand2ProcedurePatch.patch_file_ending, - # player=self.player, player_name=self.multiworld.player_name[self.player], - # patched_path=rompath) + patch = SuperMarioLand2ProcedurePatch(player=self.player, player_name=self.multiworld.player_name[self.player]) patch.write_file("basepatch.bsdiff4", pkgutil.get_data(__name__, "basepatch.bsdiff4")) @@ -77,8 +63,6 @@ def generate_output(self, output_directory: str): if self.options.randomize_music: randomize_music(patch, random) - # if self.options.auto_scroll_trap: - # patch.write_bytes(rom_addresses["Auto_Scroll_Disable"]] = 0xAF if self.options.shuffle_golden_coins: patch.write_bytes(rom_addresses["Coin_Shuffle"], 0x40) if self.options.shuffle_midway_bells: @@ -160,10 +144,4 @@ def get_base_rom_path(): file_name = get_settings()["sml2_options"]["rom_file"] if not os.path.exists(file_name): file_name = Utils.user_path(file_name) - return file_name - - -# def write_bytes(patch, byte_array, address): -# for byte in byte_array: -# patch.write_bytes(address] = byte -# address += 1 + return file_name \ No newline at end of file From b8188463b4478565ff39e618c6e3c81d92810262 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 20 May 2024 23:26:35 -0400 Subject: [PATCH 058/113] Sprite Randomizer fixes --- worlds/marioland2/sprite_randomizer.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/worlds/marioland2/sprite_randomizer.py b/worlds/marioland2/sprite_randomizer.py index 300b18e5e476..8440da2b37f6 100644 --- a/worlds/marioland2/sprite_randomizer.py +++ b/worlds/marioland2/sprite_randomizer.py @@ -12,9 +12,6 @@ def randomize_enemies(sprite_data, random): shuffle = ("Money Bag/Bopping Toady", "Ragumo/Aqua Kuribo", "Pencil/Spikey", "Kyotonbo") elif level == "Tree Zone 2": shuffle = ("Noko Bombette/Bear", "No 48/Mogyo") - elif level == "Tree Zone 4": - shuffle = ("Runaway Heart Block/Bibi", "Neiji/Buichi", "Piranha Plant (Downward)/Grubby", - "Spinning Platform (Horizontal)/Skeleton Bee", "Spinning Spike (Horizontal)/Unera") elif level == "Tree Zone 3": shuffle = ("Battle Beetle", "Be", "Ant") elif level == "Tree Zone 5": @@ -66,8 +63,10 @@ def randomize_enemies(sprite_data, random): if sprite["sprite"] == "Claw Grabber": shuffle = ("Koopa Troopa", "Diagonal Ball on Chain", "Kiddokatto", "Claw Grabber", "Masked Ghoul/Bullet Bill") - else: + elif sprite["sprite"] in ("Koopa Troopa", "Diagonal Ball on Chain", "Kiddokatto"): shuffle = ("Koopa Troopa", "Diagonal Ball on Chain", "Kiddokatto", "Masked Ghoul/Bullet Bill") + else: + shuffle = () elif level == "Mario Zone 4": if sprite["sprite"] == "Spinning Spike/Tamara": shuffle = ("Goomba", "Spinning Spike/Tamara", "Boo/Bomubomu", "Masked Ghoul/Bullet Bill") @@ -92,8 +91,23 @@ def randomize_enemies(sprite_data, random): shuffle = ("Fire Pakkun Zo (Large)", "Fire Pakkun Zo (Left)") else: shuffle = ("Spike Ball (Large)", "Spike Ball (Small)") + elif level == "Tree Zone 4": + # Deviation from SML2R: No Buichis placed into non-Buichi locations, as they can place under the + # underground question mark blocks. Potentially could make a list of which ones are allowed to become + # Buichis? + if sprite["sprite"] in ("Runaway Heart Block/Bibi", "Piranha Plant (Downward)/Grubby", + "Spinning Platform (Horizontal)/Skeleton Bee", + "Spinning Spike (Horizontal)/Unera"): + shuffle = ("Runaway Heart Block/Bibi", "Piranha Plant (Downward)/Grubby", + "Spinning Platform (Horizontal)/Skeleton Bee", "Spinning Spike (Horizontal)/Unera") + elif sprite["sprite"] == "Neiji/Buichi": + shuffle = ("Runaway Heart Block/Bibi", "Neiji/Buichi", "Piranha Plant (Downward)/Grubby", + "Spinning Platform (Horizontal)/Skeleton Bee", "Spinning Spike (Horizontal)/Unera") + else: + shuffle = () if sprite["sprite"] in ("Piranha Plant", "Fire Piranha Plant"): - shuffle = ("Piranha Plant", "Fire Piranha Plant") + if level not in ("Pumpkin Zone 2", "Pumpkin Zone 4", "Macro Zone 3"): + shuffle = ("Piranha Plant", "Fire Piranha Plant") if sprite["sprite"] in shuffle: sprite["sprite"] = random.choice(shuffle) elif level == "Mario's Castle" and sprite["sprite"] == "Karamenbo" and not random.randint(0, 9): From 7260d22049587e8117bf96863db801b14ef9585b Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 20 May 2024 23:27:20 -0400 Subject: [PATCH 059/113] Space Zone Secret Course coin logic function name fix --- worlds/marioland2/logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 1d3a26bb4096..9481bc201656 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -337,7 +337,7 @@ def space_zone_2_coins(state, player, coins): return coins <= reachable_coins -def space_zone_secret_coins(state, player, coins): +def space_zone_secret_course_coins(state, player, coins): return coins <= 96 or not is_auto_scroll(state, player, "Space Zone Secret Course") From cdee96d8e98e63c4ca1f712b60b5942f59e7bd1d Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 20 May 2024 23:28:14 -0400 Subject: [PATCH 060/113] 1 Coin locations logic fix --- worlds/marioland2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 2ada93b832a9..f92896f29df7 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -333,7 +333,7 @@ def set_rules(self): for location in self.multiworld.get_locations(self.player): if location.name in location_rules: location.access_rule = location_rules[location.name] - elif location.name.endswith("Coins"): + elif location.name.endswith(("Coins", "Coin")): rule = getattr(logic, location.parent_region.name.lower().replace(" ", "_") + "_coins", None) if rule: coins = int(location.name.split(" ")[-2]) From a40ecca7554421771b3f2f6e2dadbc5c3117e951 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 20 May 2024 23:28:54 -0400 Subject: [PATCH 061/113] Swim renamed to Water Physics --- worlds/marioland2/__init__.py | 20 +++++++++---------- worlds/marioland2/client.py | 2 +- .../marioland2/docs/en_Super Mario Land 2.md | 2 +- worlds/marioland2/items.py | 2 +- worlds/marioland2/logic.py | 20 +++++++++---------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index f92896f29df7..848c321ed23d 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -239,7 +239,7 @@ def set_rules(self): ].count(True) >= self.options.required_golden_coins) location_rules = { - "Hippo Zone - Normal or Secret Exit": lambda state: (state.has_any(["Hippo Bubble", "Swim"], self.player) + "Hippo Zone - Normal or Secret Exit": lambda state: (state.has_any(["Hippo Bubble", "Water Physics"], self.player) or (state.has("Carrot", self.player) and not is_auto_scroll(state, self.player, "Hippo Zone"))), # It is possible, however tricky, to beat the Moon Stage without Carrot or Space Physics. # However, it requires somewhat precisely jumping off enemies. Enemy shuffle may make this impossible. @@ -276,10 +276,10 @@ def set_rules(self): and state.has("Fire Flower", self.player) and has_pipe_up(state, self.player), "Macro Zone 2 - Normal Exit": lambda state: (has_pipe_down(state, self.player) or state.has( "Macro Zone 2 Midway Bell", self.player)) - and state.has("Swim", self.player) and has_pipe_up(state, + and state.has("Water Physics", self.player) and has_pipe_up(state, self.player) and not is_auto_scroll(state, self.player, "Macro Zone 2"), "Macro Zone 2 - Midway Bell": lambda state: (has_pipe_down( - state, self.player) and state.has("Swim", self.player)) or state.has( + state, self.player) and state.has("Water Physics", self.player)) or state.has( "Macro Zone 2 Midway Bell", self.player), "Macro Zone 3 - Normal Exit": lambda state: (has_pipe_down(state, self.player) and has_pipe_down(state, self.player)) or state.has("Macro Zone 3 Midway Bell", self.player), @@ -293,11 +293,11 @@ def set_rules(self): and not is_auto_scroll(state, self.player, "Pumpkin Zone 1")) or state.has( "Pumpkin Zone 1 Midway Bell", self.player), "Pumpkin Zone 2 - Normal Exit": lambda state: has_pipe_down(state, self.player) and has_pipe_up( - state, self.player) and has_pipe_right(state, self.player) and state.has("Swim", + state, self.player) and has_pipe_right(state, self.player) and state.has("Water Physics", self.player) and not is_auto_scroll(state, self.player, "Pumpkin Zone 2"), "Pumpkin Zone 2 - Secret Exit": lambda state: has_pipe_down( state, self.player) and has_pipe_up(state, self.player) and has_pipe_right( - state, self.player) and state.has("Swim", self.player) and state.has_any( + state, self.player) and state.has("Water Physics", self.player) and state.has_any( ["Mushroom", "Fire Flower"], self.player) and not is_auto_scroll(state, self.player, "Pumpkin Zone 2"), "Pumpkin Zone 3 - Secret Exit": lambda state: state.has("Carrot", self.player), "Pumpkin Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), @@ -311,13 +311,13 @@ def set_rules(self): "Mario Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), "Turtle Zone 2 - Normal Exit": lambda state: has_pipe_up(state, self.player) and has_pipe_down( state, self.player) and has_pipe_right(state, self.player) and has_pipe_left(state, self.player) - and state.has("Swim", self.player) and not is_auto_scroll(state, self.player, "Turtle Zone 2"), + and state.has("Water Physics", self.player) and not is_auto_scroll(state, self.player, "Turtle Zone 2"), "Turtle Zone 2 - Midway Bell": lambda state: state.has_any( - ["Swim", "Turtle Zone 2 Midway Bell"], self.player) and not is_auto_scroll(state, + ["Water Physics", "Turtle Zone 2 Midway Bell"], self.player) and not is_auto_scroll(state, self.player, "Turtle Zone 2"), "Turtle Zone 2 - Secret Exit": lambda state: has_pipe_up( - state, self.player) and state.has("Swim", self.player) and not is_auto_scroll(state, - self.player, "Turtle Zone 2"), #state.has_any(["Swim", "Turtle Zone 2 Midway Bell"], self.player), # hard logic option? + state, self.player) and state.has("Water Physics", self.player) and not is_auto_scroll(state, + self.player, "Turtle Zone 2"), #state.has_any(["Water Physics", "Turtle Zone 2 Midway Bell"], self.player), # hard logic option? "Turtle Zone Secret Course - Normal Exit": lambda state: state.has_any(["Fire Flower", "Carrot"], self.player), "Turtle Zone 3 - Boss": lambda state: has_pipe_right(state, self.player), @@ -361,7 +361,7 @@ def create_items(self): "Carrot": 1, "Space Physics": 1, "Hippo Bubble": 1, - "Swim": 1, + "Water Physics": 1, "Super Star Duration Increase": 2, "Mario Coin Fragment": 0, } diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index dd49aed230a0..2b8f855ccee7 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -170,7 +170,7 @@ async def game_watcher(self, ctx: BizHawkClientContext): (rom_addresses["Invincibility_Star_A"], [(invincibility_length >> 8) + 1], "ROM"), (rom_addresses["Invincibility_Star_B"], [invincibility_length & 0xFF], "ROM"), (rom_addresses["Enable_Bubble"], [0xcb, 0xd7] if "Hippo Bubble" in items_received else [0, 0], "ROM"), - (rom_addresses["Enable_Swim"], [0xcb, 0xcf] if "Swim" in items_received else [0, 0], "ROM"), + (rom_addresses["Enable_Swim"], [0xcb, 0xcf] if "Water Physics" in items_received else [0, 0], "ROM"), (rom_addresses["Pipe_Traversal_A"], [16] if "Pipe Traversal - Down" in items_received else [0], "ROM"), (rom_addresses["Pipe_Traversal_B"], [32] if "Pipe Traversal - Up" in items_received else [10], "ROM"), (rom_addresses["Pipe_Traversal_C"], [48] if "Pipe Traversal - Right" in items_received else [0], "ROM"), diff --git a/worlds/marioland2/docs/en_Super Mario Land 2.md b/worlds/marioland2/docs/en_Super Mario Land 2.md index 797b2e1e2c2b..16ab02827177 100644 --- a/worlds/marioland2/docs/en_Super Mario Land 2.md +++ b/worlds/marioland2/docs/en_Super Mario Land 2.md @@ -25,8 +25,8 @@ Besides the zone progression unlocks, the following items are always shuffled: Mushroom, you will drop straight down to Small Mario. - Fire Flower: required to become Fire Mario. - Carrot: required to become Bunny Mario. -- Swim: Mario will fall through water as though it is air until this is obtained. - Hippo Bubble: required to use the bubbles in Hippo Zone to fly. +- Water Physics: Mario will fall through water as though it is air until this is obtained. - Space Physics: the Space Zone levels will have normal gravity until this is obtained. - Super Star Duration Increase: you begin with a drastically lowered invincibility star duration, and these items will increase it. diff --git a/worlds/marioland2/items.py b/worlds/marioland2/items.py index df598375d3fe..9101862c0bd5 100644 --- a/worlds/marioland2/items.py +++ b/worlds/marioland2/items.py @@ -32,7 +32,7 @@ "Carrot": ItemClassification.progression, "Space Physics": ItemClassification.progression_skip_balancing, "Hippo Bubble": ItemClassification.progression_skip_balancing, - "Swim": ItemClassification.progression, + "Water Physics": ItemClassification.progression, "Pipe Traversal": ItemClassification.progression, "Pipe Traversal - Down": ItemClassification.progression, "Pipe Traversal - Up": ItemClassification.progression, diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 9481bc201656..6dccf2464841 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -130,14 +130,14 @@ def hippo_zone_coins(state, player, coins): reachable_coins = 160 elif state.has("Carrot", player): reachable_coins = 90 - elif state.has("Swim", player): + elif state.has("Water Physics", player): reachable_coins = 28 else: - if state.has_any(["Swim", "Hippo Bubble", "Carrot"], player): + if state.has_any(["Water Physics", "Hippo Bubble", "Carrot"], player): reachable_coins += 108 if state.has_any(["Mushroom", "Fire Flower", "Hippo Bubble"], player): reachable_coins += 6 - if state.has_all(["Fire Flower", "Swim"], player): + if state.has_all(["Fire Flower", "Water Physics"], player): reachable_coins += 1 if state.has("Hippo Bubble", player): reachable_coins += 52 @@ -162,7 +162,7 @@ def pumpkin_zone_2_coins(state, player, coins): if has_pipe_down(state, player): if not auto_scroll: reachable_coins += 7 - if (has_pipe_up(state, player) or auto_scroll) and state.has("Swim", player): + if (has_pipe_up(state, player) or auto_scroll) and state.has("Water Physics", player): reachable_coins += 6 if has_pipe_right(state, player) and not auto_scroll: reachable_coins += 1 @@ -249,7 +249,7 @@ def turtle_zone_1_coins(state, player, coins): reachable_coins = 37 if auto_scroll: reachable_coins -= 1 - if state.has("Swim", player): + if state.has("Water Physics", player): reachable_coins += 16 if state.has("Carrot", player): reachable_coins += 24 @@ -262,20 +262,20 @@ def turtle_zone_2_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Turtle Zone 2") reachable_coins = 1 if auto_scroll: - if state.has("Swim", player): + if state.has("Water Physics", player): reachable_coins += 7 else: reachable_coins += 3 - if state.has("Swim", player): + if state.has("Water Physics", player): reachable_coins += 20 elif state.has("Turtle Zone 2 Midway Bell", player): reachable_coins += 4 if (has_pipe_right(state, player) and has_pipe_down(state, player) - and state.has_any(["Swim", "Turtle Zone 2 Midway Bell"], player)): + and state.has_any(["Water Physics", "Turtle Zone 2 Midway Bell"], player)): reachable_coins += 1 if has_pipe_left(state, player) and has_pipe_up(state, player): reachable_coins += 1 - if state.has("Swim", player): + if state.has("Water Physics", player): reachable_coins += 1 return coins <= reachable_coins @@ -371,7 +371,7 @@ def macro_zone_2_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Macro Zone 2") if coins <= 27: return True - if has_pipe_up(state, player) and state.has("Swim", player) and not auto_scroll: + if has_pipe_up(state, player) and state.has("Water Physics", player) and not auto_scroll: if has_pipe_down(state, player): return True if state.has("Macro Zone 2 Midway Bell", player): From 7bf1c3dc92d28db4dcb20516e0012f698d21ce7e Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Mon, 20 May 2024 23:51:32 -0400 Subject: [PATCH 062/113] Update worlds/marioland2/docs/setup_en.md Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/docs/setup_en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/docs/setup_en.md b/worlds/marioland2/docs/setup_en.md index 293f7487068c..031a43cfae49 100644 --- a/worlds/marioland2/docs/setup_en.md +++ b/worlds/marioland2/docs/setup_en.md @@ -23,7 +23,7 @@ Once BizHawk has been installed, open EmuHawk and change the following settings: EmuHawk is running in the background. It is strongly recommended to associate GB rom extensions (\*.gb) to the EmuHawk we've just installed. -To do so, we simply have to search any Gameboy rom we happened to own, right click and select "Open with...", unfold +To do so, we simply have to search any Game Boy ROM we happened to own, right click and select "Open with...", unfold the list that appears and select the bottom option "Look for another application", then browse to the BizHawk folder and select EmuHawk.exe. From df92408bbcccac9bbdde1d4562803a34eadf43ee Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Mon, 20 May 2024 23:51:39 -0400 Subject: [PATCH 063/113] Update worlds/marioland2/docs/setup_en.md Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/docs/setup_en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/docs/setup_en.md b/worlds/marioland2/docs/setup_en.md index 031a43cfae49..581d36e7864f 100644 --- a/worlds/marioland2/docs/setup_en.md +++ b/worlds/marioland2/docs/setup_en.md @@ -22,7 +22,7 @@ Once BizHawk has been installed, open EmuHawk and change the following settings: - Under Config > Customize, check the "Run in background" box. This will prevent disconnecting from the client while EmuHawk is running in the background. -It is strongly recommended to associate GB rom extensions (\*.gb) to the EmuHawk we've just installed. +It is strongly recommended to associate Game Boy ROM extensions (\*.gb) to the EmuHawk we've just installed. To do so, we simply have to search any Game Boy ROM we happened to own, right click and select "Open with...", unfold the list that appears and select the bottom option "Look for another application", then browse to the BizHawk folder and select EmuHawk.exe. From 3b4bc52b7c901ce4ff55ce1057f9fce5898b18cc Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Mon, 20 May 2024 23:51:58 -0400 Subject: [PATCH 064/113] Update worlds/marioland2/options.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/options.py | 1 + 1 file changed, 1 insertion(+) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index f51242fbf860..e12b3ee7b5b4 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -144,6 +144,7 @@ class EnergyLink(Toggle): default = 1 + @dataclass class SML2Options(PerGameCommonOptions): shuffle_golden_coins: ShuffleGoldenCoins From 3e31f9374c88629aa0c9b6c0ea89fde312bd7373 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 20 May 2024 23:58:18 -0400 Subject: [PATCH 065/113] Remove hard logic comment --- worlds/marioland2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 848c321ed23d..f6cc1901fe2a 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -317,7 +317,7 @@ def set_rules(self): self.player, "Turtle Zone 2"), "Turtle Zone 2 - Secret Exit": lambda state: has_pipe_up( state, self.player) and state.has("Water Physics", self.player) and not is_auto_scroll(state, - self.player, "Turtle Zone 2"), #state.has_any(["Water Physics", "Turtle Zone 2 Midway Bell"], self.player), # hard logic option? + self.player, "Turtle Zone 2"), "Turtle Zone Secret Course - Normal Exit": lambda state: state.has_any(["Fire Flower", "Carrot"], self.player), "Turtle Zone 3 - Boss": lambda state: has_pipe_right(state, self.player), From e2950114473a089c8172370d9a3251112fff525d Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 25 May 2024 11:32:01 -0400 Subject: [PATCH 066/113] Check randomize enemies/platform options --- worlds/marioland2/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 848c321ed23d..8cd732036ca7 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -95,8 +95,10 @@ def __init__(self, world, player: int): def generate_early(self): self.sprite_data = deepcopy(level_sprites) - randomize_enemies(self.sprite_data, self.random) - randomize_platforms(self.sprite_data, self.random) + 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" @@ -508,7 +510,7 @@ 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 "Super Star Duration Increase" + 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', From cde24f6849147c223d942d8ffd36483a2284aee9 Mon Sep 17 00:00:00 2001 From: Alchav Date: Sat, 25 May 2024 11:32:16 -0400 Subject: [PATCH 067/113] Logic updates --- worlds/marioland2/__init__.py | 1 + worlds/marioland2/logic.py | 37 +++++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 8cd732036ca7..bf0c1370e51d 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -311,6 +311,7 @@ def set_rules(self): ["Mushroom", "Fire Flower", "Carrot"], self.player) and has_pipe_right(state, self.player)) or state.has("Mario Zone 1 Midway Bell", self.player), "Mario Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), + "Turtle Zone 1 - Normal Exit": lambda state: logic.not_blocked_by_sharks(state, self.player), "Turtle Zone 2 - Normal Exit": lambda state: has_pipe_up(state, self.player) and has_pipe_down( state, self.player) and has_pipe_right(state, self.player) and has_pipe_left(state, self.player) and state.has("Water Physics", self.player) and not is_auto_scroll(state, self.player, "Turtle Zone 2"), diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 6dccf2464841..8ce88528bfc6 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -230,13 +230,20 @@ def mario_zone_1_coins(state, player, coins): def mario_zone_3_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Mario Zone 3") - reachable_coins = 24 + reachable_coins = 10 + if state.has("Carrot", player): + reachable_spike_coins = 15 + else: + sprites = state.multiworld.worlds[player].sprite_data["Mario Zone 3"] + reachable_spike_coins = min(3, len({sprites[i]["sprite"] == "Claw Grabber" for i in (17, 18, 25)}) + + state.has("Mushroom", player) + state.has("Fire Flower", player)) * 5 + reachable_coins += reachable_spike_coins if not auto_scroll: reachable_coins += 10 if state.has("Fire Flower", player): - reachable_coins += 23 + reachable_coins += 22 if auto_scroll: - reachable_coins -= 18 + reachable_coins -= 3 + reachable_spike_coins return coins <= reachable_coins @@ -244,13 +251,27 @@ def mario_zone_4_coins(state, player, coins): return coins <= 63 or not is_auto_scroll(state, player, "Mario Zone 4") +def not_blocked_by_sharks(state, player): + sharks = [state.multiworld.worlds[player].sprite_data["Turtle Zone 1"][i]["sprite"] + for i in (27, 28)].count("Shark") + if state.has("Carrot", player) or not sharks: + return True + if sharks == 2: + return state.has_all(["Mushroom", "Fire Flower"], player) + if sharks == 1: + return state.has_any(["Mushroom", "Fire Flower"], player) + return False + + def turtle_zone_1_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Turtle Zone 1") - reachable_coins = 37 - if auto_scroll: - reachable_coins -= 1 - if state.has("Water Physics", player): - reachable_coins += 16 + reachable_coins = 30 + if not_blocked_by_sharks(state, player): + reachable_coins += 13 + if auto_scroll: + reachable_coins -= 1 + if state.has("Water Physics", player) or state.has("Carrot", player): + reachable_coins += 10 if state.has("Carrot", player): reachable_coins += 24 if auto_scroll: From 3f5f25abe2af8f2020dffd1dcabd8bb476684640 Mon Sep 17 00:00:00 2001 From: Alchav Date: Tue, 28 May 2024 01:53:03 -0400 Subject: [PATCH 068/113] Turtle Zone 2 coin logic adjustment --- worlds/marioland2/logic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 8ce88528bfc6..cd2766817d16 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -281,10 +281,10 @@ def turtle_zone_1_coins(state, player, coins): def turtle_zone_2_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Turtle Zone 2") - reachable_coins = 1 + reachable_coins = 2 if auto_scroll: if state.has("Water Physics", player): - reachable_coins += 7 + reachable_coins += 6 else: reachable_coins += 3 if state.has("Water Physics", player): From 0824b53730265fc1428d56e5f0ad75106bbb9c26 Mon Sep 17 00:00:00 2001 From: Alchav Date: Sun, 2 Jun 2024 15:02:31 -0400 Subject: [PATCH 069/113] Remove BizHawkClientContext import --- worlds/marioland2/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/worlds/marioland2/client.py b/worlds/marioland2/client.py index 2b8f855ccee7..c63727c3e269 100644 --- a/worlds/marioland2/client.py +++ b/worlds/marioland2/client.py @@ -2,7 +2,7 @@ import logging from NetUtils import ClientStatus -from worlds._bizhawk.client import BizHawkClient, BizHawkClientContext +from worlds._bizhawk.client import BizHawkClient from worlds._bizhawk import read, write, guarded_write from .rom_addresses import rom_addresses @@ -36,7 +36,7 @@ async def set_auth(self, ctx): auth_name = base64.b64encode(auth_name[0]).decode() ctx.auth = auth_name - async def game_watcher(self, ctx: BizHawkClientContext): + async def game_watcher(self, ctx): from . import START_IDS from .items import items from .locations import locations, level_id_to_name, coins_coords, location_name_to_id @@ -233,7 +233,7 @@ async def game_watcher(self, ctx: BizHawkClientContext): await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) ctx.finished_game = True - def on_package(self, ctx: BizHawkClientContext, cmd: str, args: dict): + def on_package(self, ctx, cmd: str, args: dict): super().on_package(ctx, cmd, args) if cmd == 'Connected': if ctx.slot_data["energy_link"]: From 7d7a5772b89b05d043833d8a593e208726e48c57 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 17 Jun 2024 19:06:17 -0400 Subject: [PATCH 070/113] Can't reach some coins without water physics if you are small --- worlds/marioland2/logic.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 6dccf2464841..b6d93788bb62 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -289,6 +289,10 @@ def turtle_zone_secret_course_coins(state, player, coins): return coins <= reachable_coins +def turtle_zone_3_coins(state, player, coins): + return state.has_any(["Water Physics", "Mushroom", "Fire Flower", "Carrot"], player) or coins <= 51 + + def space_zone_1_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Space Zone 1") if auto_scroll: From 1e4ad7fcd35500cb09b35783a96ad01606cba687 Mon Sep 17 00:00:00 2001 From: Alchav Date: Mon, 17 Jun 2024 19:51:50 -0400 Subject: [PATCH 071/113] Fix item name mistakes --- worlds/marioland2/__init__.py | 5 +++-- worlds/marioland2/items.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index f6cc1901fe2a..149513b147a0 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -57,8 +57,9 @@ class MarioLand2World(World): web = MarioLand2WebWorld() item_name_groups = { - "Level Progression": {item_name for item_name in items if item_name.endswith("Progression") - or item_name.endswith("Secret")}, + "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)}}, diff --git a/worlds/marioland2/items.py b/worlds/marioland2/items.py index 9101862c0bd5..5f9ede60cdb3 100644 --- a/worlds/marioland2/items.py +++ b/worlds/marioland2/items.py @@ -73,5 +73,6 @@ **{f"{i} Coins": ItemClassification.filler for i in range(2, 169)} } -for level in {"Turtle Zone Secret", "Macro Zone Secret", "Turtle Zone 3", "Scenic Course", "Mario Zone 2"}: +for level in {"Turtle Zone Secret Course", "Macro Zone Secret Course", "Turtle Zone 3", "Scenic Course", + "Mario Zone 2"}: items[f"Cancel Auto Scroll - {level}"] = ItemClassification.useful From ff896adba0e0d986cc16669c43db7e45ab9bbcd5 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:18:31 -0400 Subject: [PATCH 072/113] Update worlds/marioland2/options.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/options.py | 1 + 1 file changed, 1 insertion(+) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index e12b3ee7b5b4..43be9019ce68 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -145,6 +145,7 @@ class EnergyLink(Toggle): + @dataclass class SML2Options(PerGameCommonOptions): shuffle_golden_coins: ShuffleGoldenCoins From 3f8395504eef1e2b656bdc2d07077600e74e4e5a Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:19:47 -0400 Subject: [PATCH 073/113] Update README.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 4f0fef1a3362..6b70cb5bbc52 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,6 @@ Currently, the following games are supported: * Aquaria * Yu-Gi-Oh! Ultimate Masters: World Championship Tournament 2006 * A Hat in Time - * Super Mario Land 2: 6 Golden Coins For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/). From a668a23ff7d2868421ac08dd0ca0a0513852c9c3 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:20:27 -0400 Subject: [PATCH 074/113] Update worlds/marioland2/docs/en_Super Mario Land 2.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> --- worlds/marioland2/docs/en_Super Mario Land 2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/docs/en_Super Mario Land 2.md b/worlds/marioland2/docs/en_Super Mario Land 2.md index 16ab02827177..fe163e249e7a 100644 --- a/worlds/marioland2/docs/en_Super Mario Land 2.md +++ b/worlds/marioland2/docs/en_Super Mario Land 2.md @@ -31,7 +31,7 @@ Mushroom, you will drop straight down to Small Mario. - Super Star Duration Increase: you begin with a drastically lowered invincibility star duration, and these items will increase it. -Additionally, the following items can be shuffled depending on your YAML settings: +Additionally, the following items can be shuffled depending on your YAML options: - The 6 Golden Coins: note that the game will still show you the coin being sent to the castle when defeating a boss regardless of whether the coin is actually obtained in that location. - Mario Coin Fragments: As an alternative to shuffling the 6 Golden Coins, you can shuffle Mario Coin Fragments, From 380418c0b7dc5ec8506e841e0e2fcbfafdee589d Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:21:08 -0400 Subject: [PATCH 075/113] Update worlds/marioland2/options.py Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> --- worlds/marioland2/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 43be9019ce68..b2819de9b5d0 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -25,7 +25,7 @@ class GoldenCoinsRequired(Range): class MarioCoinFragmentPercentage(Range): """Percentage of filler items to be replaced with Mario Coin Fragments. Note that the Coinsanity and Coinsanity - Checks settings will greatly impact the number of replaceable filler items. There may be as few as 6 available + Checks options will greatly impact the number of replaceable filler items. There may be as few as 6 available slots for Mario Coin Fragments if Coinsanity is off.""" display_name = "Mario Coin Fragment Percentage" range_start = 1 From fbf5f19be83d42597ec4bf75f9bc6ed59147a0f0 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Wed, 26 Jun 2024 11:11:38 -0400 Subject: [PATCH 076/113] Update worlds/marioland2/options.py Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> --- worlds/marioland2/options.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index b2819de9b5d0..839cb5d56922 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -3,11 +3,12 @@ class ShuffleGoldenCoins(Choice): - """Vanilla: Golden Coins are not in the item pool, you receive them when defeating bosses as in vanilla. - Shuffle: Shuffle the Golden Coins into the item pool and open location checks on bosses. - Mario Coin Fragment Hunt: You start with all Golden Coins except the Mario Coin, which has been fragmented into - many pieces. - You will see a Golden Coin being received when defeating bosses regardless of whether you are actually getting a coin.""" + """ + Vanilla: Golden Coins are received when defeating bosses. + Shuffle: Shuffle the Golden Coins into the item pool and make bosses location checks. + Mario Coin Fragment Hunt: You start with all Golden Coins except the Mario Coin, which has been fragmented into many pieces. + You will see a Golden Coin being received when defeating bosses regardless of whether you are actually getting a coin. + """ display_name = "Shuffle Golden Coins" default = 0 option_vanilla = 0 From d0d51bb1fa2b681d821f0cbd6cb9b30d52a849cb Mon Sep 17 00:00:00 2001 From: Alchav Date: Wed, 26 Jun 2024 11:11:15 -0400 Subject: [PATCH 077/113] Reformat docstrings --- worlds/marioland2/options.py | 76 +++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 839cb5d56922..b4ab673d7780 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -17,7 +17,9 @@ class ShuffleGoldenCoins(Choice): class GoldenCoinsRequired(Range): - """Number of Golden Coins required to enter Mario's Castle. Ignored on Mario Coin Fragment Hunt.""" + """ + Number of Golden Coins required to enter Mario's Castle. Ignored on Mario Coin Fragment Hunt. + """ display_name = "Golden Coins Required" range_start = 0 range_end = 6 @@ -25,9 +27,11 @@ class GoldenCoinsRequired(Range): class MarioCoinFragmentPercentage(Range): - """Percentage of filler items to be replaced with Mario Coin Fragments. Note that the Coinsanity and Coinsanity + """ + Percentage of filler items to be replaced with Mario Coin Fragments. Note that the Coinsanity and Coinsanity Checks options will greatly impact the number of replaceable filler items. There may be as few as 6 available - slots for Mario Coin Fragments if Coinsanity is off.""" + slots for Mario Coin Fragments if Coinsanity is off. + """ display_name = "Mario Coin Fragment Percentage" range_start = 1 range_end = 100 @@ -35,7 +39,9 @@ class MarioCoinFragmentPercentage(Range): class MarioCoinFragmentsRequiredPercentage(Range): - """Percentage of the Mario Coins in the item pool that are required to put the Mario Coin together.""" + """ + Percentage of the Mario Coins in the item pool that are required to put the Mario Coin together. + """ display_name = "Mario Coin Fragments Required Percentage" range_start = 1 range_end = 100 @@ -43,28 +49,36 @@ class MarioCoinFragmentsRequiredPercentage(Range): class ShuffleMidwayBells(Toggle): - """Shuffle the Midway Bells into the item pool. Ringing a bell will trigger a location check. + """ + Shuffle the Midway Bells into the item pool. Ringing a bell will trigger a location check. Obtaining a Midway Bell will be permanent, and some levels will require backtracking from the midway point to reach - secret exits.""" + secret exits. + """ display_name = "Shuffle Midway Bells" class MariosCastleMidwayBell(Toggle): - """Adds a Midway Bell to the final stage, just before the Wario fight.""" + """ + Adds a Midway Bell to the final stage, just before the Wario fight. + """ display_name = "Mario's Castle Midway Bell" class Coinsanity(Toggle): - """Shuffles the singular coins found freestanding and in question mark blocks into the item pool, and adds location - checks made by obtaining a sufficient number of coins in particular levels within a single playthrough.""" + """ + Shuffles the singular coins found freestanding and in question mark blocks into the item pool, and adds location + checks made by obtaining a sufficient number of coins in particular levels within a single playthrough. + """ default_name = "Coinsanity" class CoinsanityChecks(Range): - """Number of Coinsanity checks. + """ + Number of Coinsanity checks. A higher number means more checks, and smaller coin amounts per coin item in the item pool. If Accessibility is set to Locations, auto-scroll levels may have a lower maximum count, which may lead to this - value being limited.""" + value being limited. + """ default_name = "Coinsanity Checks" range_start = 31 range_end = 2599 @@ -72,8 +86,10 @@ class CoinsanityChecks(Range): class DifficultyMode(Choice): - """Play in normal or easy mode. You can also start in Normal Mode with an "upgrade" to Easy Mode in the item pool, - or start in Easy Mode with a Normal Mode "trap" in the item pool.""" + """ + Play in normal or easy mode. You can also start in Normal Mode with an "upgrade" to Easy Mode in the item pool, + or start in Easy Mode with a Normal Mode "trap" in the item pool. + """ display_name = "Difficulty Mode" option_normal = 0 option_easy = 1 @@ -83,9 +99,11 @@ class DifficultyMode(Choice): class ShufflePipeTraversal(Choice): - """Single: Shuffle a Pipe Traversal item into the item pool, which is required to enter any pipes. + """ + Single: Shuffle a Pipe Traversal item into the item pool, which is required to enter any pipes. Split: Shuffle 4 Pipe Traversal items, one required for entering pipes from each direction. - Note that being unable to enter pipes is very limiting and affects nearly half of all levels.""" + Note that being unable to enter pipes is very limiting and affects nearly half of all levels. + """ display_name = "Shuffle Pipe Traversal" option_off = 0 option_single = 1 @@ -94,17 +112,23 @@ class ShufflePipeTraversal(Choice): class RandomizeEnemies(Toggle): - """Randomize enemies throughout levels.""" + """ + Randomize enemies throughout levels. + """ display_name = "Randomize Enemies" class RandomizePlatforms(Toggle): - """Randomize platforms throughout levels.""" + """ + Randomize platforms throughout levels. + """ display_name = "Randomize Platforms" class AutoScrollChances(NamedRange): - """Chance per eligible level to be made into an auto scroll level. Can also set to Vanilla to leave them unchanged.""" + """ + Chance per eligible level to be made into an auto scroll level. Can also set to Vanilla to leave them unchanged. + """ display_name = "Auto Scroll Chance" range_start = 0 range_end = 100 @@ -113,7 +137,8 @@ class AutoScrollChances(NamedRange): class AutoScrollMode(Choice): - """Always: Any auto scroll levels will always auto-scroll. + """ + Always: Any auto scroll levels will always auto-scroll. Global Trap Item: Auto scroll levels will only auto-scroll after obtaining the Auto Scroll trap item. Level Trap Items: As with Trap Item, but there is a separate trap item for each auto scroll level. Global Cancel Item: Auto Scroll levels will stop auto-scrolling after obtaining the Auto Scroll Cancel item. @@ -122,7 +147,8 @@ class AutoScrollMode(Choice): The effects of Trap and Cancel items are permanent! If Accessibility is not set to Locations, Traps may cause locations to become permanently unreachable. With individual level items, the number of auto scroll levels may be limited by the available space in the item - pool.""" + pool. + """ display_name = "Auto Scroll Mode" option_always = 0 option_global_trap_item = 1 @@ -134,13 +160,17 @@ class AutoScrollMode(Choice): class RandomizeMusic(Toggle): - """Randomize the music that plays in levels and overworld areas.""" + """ + Randomize the music that plays in levels and overworld areas. + """ display_name = "Randomize Music" class EnergyLink(Toggle): - """All extra lives beyond 1 are transferred into the server's shared EnergyLink storage. If you drop to 0, - 1 will be replenished if there is sufficient energy stored.""" + """ + All extra lives beyond 1 are transferred into the server's shared EnergyLink storage. If you drop to 0, + 1 will be replenished if there is sufficient energy stored. + """ display_name = "Energy Link" default = 1 From 0620ca59ca1a1b0927f7dbd449793bc2c5a8777e Mon Sep 17 00:00:00 2001 From: Alchav Date: Wed, 26 Jun 2024 11:19:20 -0400 Subject: [PATCH 078/113] Reformat Location Rules --- worlds/marioland2/__init__.py | 208 +++++++++++++++++++++------------- 1 file changed, 127 insertions(+), 81 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index a93a6a6777f8..75801f2e5246 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -242,93 +242,139 @@ def set_rules(self): ].count(True) >= self.options.required_golden_coins) location_rules = { - "Hippo Zone - Normal or Secret Exit": lambda state: (state.has_any(["Hippo Bubble", "Water Physics"], self.player) - or (state.has("Carrot", self.player) and not is_auto_scroll(state, self.player, "Hippo Zone"))), + "Hippo Zone - Normal or Secret Exit": + lambda state: (state.has_any(["Hippo Bubble", "Water Physics"], self.player) + or (state.has("Carrot", self.player) + and not is_auto_scroll(state, self.player, "Hippo Zone"))), # It is possible, however tricky, to beat the Moon Stage without Carrot or Space Physics. # However, it requires somewhat precisely jumping off enemies. Enemy shuffle may make this impossible. # I have not done any testing there. Instead, I will just always make one or the other required, since # it is difficult without them anyway. - "Space Zone 1 - Normal Exit": lambda state: state.has_any(["Space Physics", "Carrot"], self.player), + "Space Zone 1 - Normal Exit": + lambda state: state.has_any(["Space Physics", "Carrot"], self.player), # One or the other is actually necessary for the secret exit. - "Space Zone 1 - Secret Exit": lambda state: state.has_any( - ["Space Physics", "Carrot"], self.player) and not is_auto_scroll(state, self.player, "Space Zone 1"), - "Space Zone 2 - Boss": lambda state: logic.space_zone_2_boss(state, self.player), - "Space Zone 2 - Midway Bell": lambda state: state.has_any( - ["Space Physics", "Space Zone 2 Midway Bell", "Mushroom", "Fire Flower", "Carrot"], - self.player), - "Tree Zone 2 - Normal Exit": lambda state: has_pipe_right(state, self.player) or state.has( - "Tree Zone 2 Midway Bell", self.player), - "Tree Zone 2 - Midway Bell": lambda state: has_pipe_right(state, self.player) or state.has( - "Tree Zone 2 Midway Bell", self.player), - "Tree Zone 2 - Secret Exit": lambda state: has_pipe_right(state, self.player) - and state.has("Carrot", self.player), - "Tree Zone 3 - Normal Exit": lambda state: not is_auto_scroll(state, self.player, "Tree Zone 3"), - "Tree Zone 4 - Normal Exit": lambda state: has_pipe_down(state, self.player) - and ((has_pipe_right(state, self.player) and has_pipe_up(state, self.player)) - or state.has("Tree Zone 4 Midway Bell", self.player)), - "Tree Zone 4 - Midway Bell": lambda state: ((has_pipe_right(state, self.player) - and has_pipe_up(state, self.player)) or state.has("Tree Zone 4 Midway Bell", self.player)), - "Tree Zone 5 - Boss": lambda state: has_pipe_right(state, self.player) and ( - has_pipe_up(state, self.player) or state.has("Carrot", self.player)), - "Macro Zone 1 - Normal Exit": lambda state: has_pipe_down(state, self.player) - or state.has("Macro Zone 1 Midway Bell", self.player), - "Macro Zone 1 - Midway Bell": lambda state: has_pipe_down(state, self.player) - or state.has("Macro Zone 1 Midway Bell", self.player), - "Macro Zone 1 - Secret Exit": lambda state: (has_pipe_down(state, self.player) - or state.has("Macro Zone 1 Midway Bell", self.player)) - and state.has("Fire Flower", self.player) and has_pipe_up(state, self.player), - "Macro Zone 2 - Normal Exit": lambda state: (has_pipe_down(state, self.player) or state.has( - "Macro Zone 2 Midway Bell", self.player)) - and state.has("Water Physics", self.player) and has_pipe_up(state, - self.player) and not is_auto_scroll(state, self.player, "Macro Zone 2"), - "Macro Zone 2 - Midway Bell": lambda state: (has_pipe_down( - state, self.player) and state.has("Water Physics", self.player)) or state.has( - "Macro Zone 2 Midway Bell", self.player), - "Macro Zone 3 - Normal Exit": lambda state: (has_pipe_down(state, self.player) - and has_pipe_down(state, self.player)) or state.has("Macro Zone 3 Midway Bell", self.player), - "Macro Zone 3 - Midway Bell": lambda state: (has_pipe_down(state, self.player) - and has_pipe_down(state, self.player)) or state.has("Macro Zone 3 Midway Bell", self.player), - "Macro Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), - "Pumpkin Zone 1 - Normal Exit": lambda state: (has_pipe_down(state, self.player) - and not is_auto_scroll(state, self.player, "Pumpkin Zone 1")) or state.has( - "Pumpkin Zone 1 Midway Bell", self.player), - "Pumpkin Zone 1 - Midway Bell": lambda state: (has_pipe_down(state, self.player) - and not is_auto_scroll(state, self.player, "Pumpkin Zone 1")) or state.has( - "Pumpkin Zone 1 Midway Bell", self.player), - "Pumpkin Zone 2 - Normal Exit": lambda state: has_pipe_down(state, self.player) and has_pipe_up( - state, self.player) and has_pipe_right(state, self.player) and state.has("Water Physics", - self.player) and not is_auto_scroll(state, self.player, "Pumpkin Zone 2"), - "Pumpkin Zone 2 - Secret Exit": lambda state: has_pipe_down( - state, self.player) and has_pipe_up(state, self.player) and has_pipe_right( - state, self.player) and state.has("Water Physics", self.player) and state.has_any( - ["Mushroom", "Fire Flower"], self.player) and not is_auto_scroll(state, self.player, "Pumpkin Zone 2"), - "Pumpkin Zone 3 - Secret Exit": lambda state: state.has("Carrot", self.player), - "Pumpkin Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), - "Mario Zone 1 - Normal Exit": lambda state: has_pipe_right(state, self.player) and ( - state.has_any(["Mushroom", "Fire Flower", "Carrot", "Mario Zone 1 Midway Bell"], self.player) - or not is_auto_scroll(state, self.player, "Mario Zone 1")), + "Space Zone 1 - Secret Exit": + lambda state: state.has_any(["Space Physics", "Carrot"], self.player) + and not is_auto_scroll(state, self.player, "Space Zone 1"), + "Space Zone 2 - Boss": + lambda state: logic.space_zone_2_boss(state, self.player), + "Space Zone 2 - Midway Bell": + lambda state: state.has_any(["Space Physics", "Space Zone 2 Midway Bell", "Mushroom", "Fire Flower", + "Carrot"], self.player), + "Tree Zone 2 - Normal Exit": + lambda state: has_pipe_right(state, self.player) or state.has("Tree Zone 2 Midway Bell", self.player), + "Tree Zone 2 - Midway Bell": + lambda state: has_pipe_right(state, self.player) or state.has("Tree Zone 2 Midway Bell", self.player), + "Tree Zone 2 - Secret Exit": + lambda state: has_pipe_right(state, self.player) and state.has("Carrot", self.player), + "Tree Zone 3 - Normal Exit": + lambda state: not is_auto_scroll(state, self.player, "Tree Zone 3"), + "Tree Zone 4 - Normal Exit": + lambda state: has_pipe_down(state, self.player) + and ((has_pipe_right(state, self.player) + and has_pipe_up(state, self.player)) + or state.has("Tree Zone 4 Midway Bell", self.player)), + "Tree Zone 4 - Midway Bell": + lambda state: (has_pipe_right(state, self.player) + and has_pipe_up(state, self.player)) + or state.has("Tree Zone 4 Midway Bell", self.player), + "Tree Zone 5 - Boss": + lambda state: has_pipe_right(state, self.player) + and (has_pipe_up(state, self.player) + or state.has("Carrot", self.player)), + "Macro Zone 1 - Normal Exit": + lambda state: has_pipe_down(state, self.player) + or state.has("Macro Zone 1 Midway Bell", self.player), + "Macro Zone 1 - Midway Bell": + lambda state: has_pipe_down(state, self.player) + or state.has("Macro Zone 1 Midway Bell", self.player), + "Macro Zone 1 - Secret Exit": + lambda state: (has_pipe_down(state, self.player) + or state.has("Macro Zone 1 Midway Bell", self.player)) + and state.has("Fire Flower", self.player) and has_pipe_up(state, self.player), + "Macro Zone 2 - Normal Exit": + lambda state: (has_pipe_down(state, self.player) + or state.has("Macro Zone 2 Midway Bell", self.player)) + and state.has("Water Physics", self.player) and has_pipe_up(state, self.player) + and not is_auto_scroll(state, self.player, "Macro Zone 2"), + "Macro Zone 2 - Midway Bell": + lambda state: (has_pipe_down(state, self.player) + and state.has("Water Physics", self.player)) or state.has("Macro Zone 2 Midway Bell", + self.player), + "Macro Zone 3 - Normal Exit": + lambda state: (has_pipe_down(state, self.player) + and has_pipe_down(state, self.player)) + or state.has("Macro Zone 3 Midway Bell", self.player), + "Macro Zone 3 - Midway Bell": + lambda state: (has_pipe_down(state, self.player) + and has_pipe_down(state, self.player)) + or state.has("Macro Zone 3 Midway Bell", self.player), + "Macro Zone 4 - Boss": + lambda state:has_pipe_right(state, self.player), + "Pumpkin Zone 1 - Normal Exit": + lambda state: (has_pipe_down(state, self.player) + and not is_auto_scroll(state, self.player, "Pumpkin Zone 1")) + or state.has("Pumpkin Zone 1 Midway Bell", self.player), + "Pumpkin Zone 1 - Midway Bell": + lambda state: (has_pipe_down(state, self.player) + and not is_auto_scroll(state, self.player, "Pumpkin Zone 1")) + or state.has("Pumpkin Zone 1 Midway Bell", self.player), + "Pumpkin Zone 2 - Normal Exit": + lambda state: has_pipe_down(state, self.player) + and has_pipe_up(state, self.player) + and has_pipe_right(state, self.player) + and state.has("Water Physics", self.player) + and not is_auto_scroll(state, self.player, "Pumpkin Zone 2"), + "Pumpkin Zone 2 - Secret Exit": + lambda state: has_pipe_down(state, self.player) + and has_pipe_up(state, self.player) + and has_pipe_right(state, self.player) + and state.has("Water Physics", self.player) + and state.has_any(["Mushroom", "Fire Flower"], self.player) + and not is_auto_scroll(state, self.player, "Pumpkin Zone 2"), + "Pumpkin Zone 3 - Secret Exit": + lambda state: state.has("Carrot", self.player), + "Pumpkin Zone 4 - Boss": + lambda state: has_pipe_right(state, self.player), + "Mario Zone 1 - Normal Exit": + lambda state: has_pipe_right(state, self.player) + and (state.has_any(["Mushroom", "Fire Flower", "Carrot", "Mario Zone 1 Midway Bell"], + self.player) + or not is_auto_scroll(state, self.player, "Mario Zone 1")), # It is possible to get as small mario, but it is a very precise jump and you will die afterward. - "Mario Zone 1 - Midway Bell": lambda state: (state.has_any( - ["Mushroom", "Fire Flower", "Carrot"], self.player) and has_pipe_right(state, self.player)) - or state.has("Mario Zone 1 Midway Bell", self.player), - "Mario Zone 4 - Boss": lambda state: has_pipe_right(state, self.player), - "Turtle Zone 1 - Normal Exit": lambda state: logic.not_blocked_by_sharks(state, self.player), - "Turtle Zone 2 - Normal Exit": lambda state: has_pipe_up(state, self.player) and has_pipe_down( - state, self.player) and has_pipe_right(state, self.player) and has_pipe_left(state, self.player) - and state.has("Water Physics", self.player) and not is_auto_scroll(state, self.player, "Turtle Zone 2"), - "Turtle Zone 2 - Midway Bell": lambda state: state.has_any( - ["Water Physics", "Turtle Zone 2 Midway Bell"], self.player) and not is_auto_scroll(state, - self.player, "Turtle Zone 2"), - "Turtle Zone 2 - Secret Exit": lambda state: has_pipe_up( - state, self.player) and state.has("Water Physics", self.player) and not is_auto_scroll(state, - self.player, "Turtle Zone 2"), - "Turtle Zone Secret Course - Normal Exit": lambda state: state.has_any(["Fire Flower", "Carrot"], - self.player), - "Turtle Zone 3 - Boss": lambda state: has_pipe_right(state, self.player), - "Mario's Castle - Wario": lambda state: has_pipe_right( - state, self.player) and has_pipe_left(state, self.player), - "Mario's Castle - Midway Bell": lambda state: (has_pipe_right(state, self.player) and has_pipe_left( - state, self.player)) or state.has("Mario's Castle Midway Bell", self.player) + "Mario Zone 1 - Midway Bell": + lambda state: (state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player) + and has_pipe_right(state, self.player)) + or state.has("Mario Zone 1 Midway Bell", self.player), + "Mario Zone 4 - Boss": + lambda state: has_pipe_right(state, self.player), + "Turtle Zone 1 - Normal Exit": + lambda state: logic.not_blocked_by_sharks(state, self.player), + "Turtle Zone 2 - Normal Exit": + lambda state: has_pipe_up(state, self.player) + and has_pipe_down(state, self.player) + and has_pipe_right(state, self.player) + and has_pipe_left(state, self.player) + and state.has("Water Physics", self.player) + and not is_auto_scroll(state, self.player, "Turtle Zone 2"), + "Turtle Zone 2 - Midway Bell": + lambda state: state.has_any(["Water Physics", "Turtle Zone 2 Midway Bell"], self.player) + and not is_auto_scroll(state, self.player, "Turtle Zone 2"), + "Turtle Zone 2 - Secret Exit": + lambda state: has_pipe_up(state, self.player) + and state.has("Water Physics", self.player) + and not is_auto_scroll(state, self.player, "Turtle Zone 2"), + "Turtle Zone Secret Course - Normal Exit": + lambda state: state.has_any(["Fire Flower", "Carrot"], self.player), + "Turtle Zone 3 - Boss": + lambda state: has_pipe_right(state, self.player), + "Mario's Castle - Wario": + lambda state: has_pipe_right(state, self.player) + and has_pipe_left(state, self.player), + "Mario's Castle - Midway Bell": + lambda state: (has_pipe_right(state, self.player) + and has_pipe_left(state, self.player)) + or state.has("Mario's Castle Midway Bell", self.player) } for entrance, rule in entrance_rules.items(): From 8ef9388e811941f5bdacbfc8f3aaaaec8cc3eabc Mon Sep 17 00:00:00 2001 From: Alchav Date: Sun, 14 Jul 2024 19:58:25 -0400 Subject: [PATCH 079/113] Further fix Turtle Zone 2 logic --- worlds/marioland2/logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index eb098ef31b43..2188488c5f4c 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -286,7 +286,7 @@ def turtle_zone_2_coins(state, player, coins): if state.has("Water Physics", player): reachable_coins += 6 else: - reachable_coins += 3 + reachable_coins += 2 if state.has("Water Physics", player): reachable_coins += 20 elif state.has("Turtle Zone 2 Midway Bell", player): From e4261dd1d9c64193f3d14b520bc6326d2188a7e7 Mon Sep 17 00:00:00 2001 From: alchav Date: Mon, 5 Aug 2024 12:51:24 -0400 Subject: [PATCH 080/113] Macro Zone 3 Midway Bell coin logic --- worlds/marioland2/logic.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 2188488c5f4c..8f7cee9faed0 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -421,6 +421,8 @@ def macro_zone_3_coins(state, player, coins): reachable_coins += 36 elif has_pipe_down(state, player): reachable_coins += 18 + if state.has("Macro Zone 3 - Midway Bell", player): + reachable_coins = max(reachable_coins, 30) return coins <= reachable_coins From b363b69a0ad0baf0e347fc11fdd421276943bd7a Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Wed, 7 Aug 2024 16:27:25 -0400 Subject: [PATCH 081/113] Update worlds/marioland2/docs/en_Super Mario Land 2.md Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/docs/en_Super Mario Land 2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/docs/en_Super Mario Land 2.md b/worlds/marioland2/docs/en_Super Mario Land 2.md index fe163e249e7a..59aa073c6d9e 100644 --- a/worlds/marioland2/docs/en_Super Mario Land 2.md +++ b/worlds/marioland2/docs/en_Super Mario Land 2.md @@ -16,7 +16,7 @@ Unlocking paths to new levels requires finding or receiving Zone Progression ite "Turtle Zone Progression" will unlock the path from Turtle Zone 1 to Turtle Zone 2. Paths to secret levels are separate items, so Turtle Zone Secret will open the path from Turtle Zone 2 to the Turtle Zone Secret Course. -Depending on settings, there may be some "Zone Progression x2" item that opens two paths at once. +Depending on settings, there may be some "Zone Progression x2" items that open two paths at once. The path from Tree Zone 2 to the branch to Tree Zone 3 and 4 is one unlock, so both levels will open at this point. From 973f614026d0a218000cc1cc9070be5ecde770d7 Mon Sep 17 00:00:00 2001 From: alchav Date: Mon, 2 Sep 2024 20:16:54 -0400 Subject: [PATCH 082/113] Fix Tree Zone 4 coin logic --- worlds/marioland2/logic.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 8f7cee9faed0..db90ce4b7246 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -87,17 +87,17 @@ def tree_zone_4_coins(state, player, coins): if has_pipe_right(state, player): reachable_coins += 4 if has_pipe_down(state, player): - reachable_coins += 46 + reachable_coins += 10 if not auto_scroll: - reachable_coins += 10 + reachable_coins += 46 elif state.has("Tree Zone 4 Midway Bell", player): if not auto_scroll: if has_pipe_left(state, player): reachable_coins += 18 if has_pipe_down(state, player): - reachable_coins += 46 + reachable_coins += 10 if has_pipe_up(state, player): - reachable_coins += 10 + reachable_coins += 46 elif has_pipe_down(state, player): reachable_coins += 10 return coins <= reachable_coins From 6cdfd800a6cde58e1200315ec9808eb1aba7a5b7 Mon Sep 17 00:00:00 2001 From: alchav Date: Mon, 2 Sep 2024 20:17:11 -0400 Subject: [PATCH 083/113] Move all location access rule logic to logic.py --- worlds/marioland2/__init__.py | 151 ++---------------------- worlds/marioland2/logic.py | 213 ++++++++++++++++++++++++++++++---- 2 files changed, 202 insertions(+), 162 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 75801f2e5246..8507e69337e2 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -57,9 +57,10 @@ class MarioLand2World(World): 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}, + "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)}}, @@ -241,154 +242,22 @@ def set_rules(self): state.has("Mario Coin", self.player), state.has("Turtle Coin", self.player) ].count(True) >= self.options.required_golden_coins) - location_rules = { - "Hippo Zone - Normal or Secret Exit": - lambda state: (state.has_any(["Hippo Bubble", "Water Physics"], self.player) - or (state.has("Carrot", self.player) - and not is_auto_scroll(state, self.player, "Hippo Zone"))), - # It is possible, however tricky, to beat the Moon Stage without Carrot or Space Physics. - # However, it requires somewhat precisely jumping off enemies. Enemy shuffle may make this impossible. - # I have not done any testing there. Instead, I will just always make one or the other required, since - # it is difficult without them anyway. - "Space Zone 1 - Normal Exit": - lambda state: state.has_any(["Space Physics", "Carrot"], self.player), - # One or the other is actually necessary for the secret exit. - "Space Zone 1 - Secret Exit": - lambda state: state.has_any(["Space Physics", "Carrot"], self.player) - and not is_auto_scroll(state, self.player, "Space Zone 1"), - "Space Zone 2 - Boss": - lambda state: logic.space_zone_2_boss(state, self.player), - "Space Zone 2 - Midway Bell": - lambda state: state.has_any(["Space Physics", "Space Zone 2 Midway Bell", "Mushroom", "Fire Flower", - "Carrot"], self.player), - "Tree Zone 2 - Normal Exit": - lambda state: has_pipe_right(state, self.player) or state.has("Tree Zone 2 Midway Bell", self.player), - "Tree Zone 2 - Midway Bell": - lambda state: has_pipe_right(state, self.player) or state.has("Tree Zone 2 Midway Bell", self.player), - "Tree Zone 2 - Secret Exit": - lambda state: has_pipe_right(state, self.player) and state.has("Carrot", self.player), - "Tree Zone 3 - Normal Exit": - lambda state: not is_auto_scroll(state, self.player, "Tree Zone 3"), - "Tree Zone 4 - Normal Exit": - lambda state: has_pipe_down(state, self.player) - and ((has_pipe_right(state, self.player) - and has_pipe_up(state, self.player)) - or state.has("Tree Zone 4 Midway Bell", self.player)), - "Tree Zone 4 - Midway Bell": - lambda state: (has_pipe_right(state, self.player) - and has_pipe_up(state, self.player)) - or state.has("Tree Zone 4 Midway Bell", self.player), - "Tree Zone 5 - Boss": - lambda state: has_pipe_right(state, self.player) - and (has_pipe_up(state, self.player) - or state.has("Carrot", self.player)), - "Macro Zone 1 - Normal Exit": - lambda state: has_pipe_down(state, self.player) - or state.has("Macro Zone 1 Midway Bell", self.player), - "Macro Zone 1 - Midway Bell": - lambda state: has_pipe_down(state, self.player) - or state.has("Macro Zone 1 Midway Bell", self.player), - "Macro Zone 1 - Secret Exit": - lambda state: (has_pipe_down(state, self.player) - or state.has("Macro Zone 1 Midway Bell", self.player)) - and state.has("Fire Flower", self.player) and has_pipe_up(state, self.player), - "Macro Zone 2 - Normal Exit": - lambda state: (has_pipe_down(state, self.player) - or state.has("Macro Zone 2 Midway Bell", self.player)) - and state.has("Water Physics", self.player) and has_pipe_up(state, self.player) - and not is_auto_scroll(state, self.player, "Macro Zone 2"), - "Macro Zone 2 - Midway Bell": - lambda state: (has_pipe_down(state, self.player) - and state.has("Water Physics", self.player)) or state.has("Macro Zone 2 Midway Bell", - self.player), - "Macro Zone 3 - Normal Exit": - lambda state: (has_pipe_down(state, self.player) - and has_pipe_down(state, self.player)) - or state.has("Macro Zone 3 Midway Bell", self.player), - "Macro Zone 3 - Midway Bell": - lambda state: (has_pipe_down(state, self.player) - and has_pipe_down(state, self.player)) - or state.has("Macro Zone 3 Midway Bell", self.player), - "Macro Zone 4 - Boss": - lambda state:has_pipe_right(state, self.player), - "Pumpkin Zone 1 - Normal Exit": - lambda state: (has_pipe_down(state, self.player) - and not is_auto_scroll(state, self.player, "Pumpkin Zone 1")) - or state.has("Pumpkin Zone 1 Midway Bell", self.player), - "Pumpkin Zone 1 - Midway Bell": - lambda state: (has_pipe_down(state, self.player) - and not is_auto_scroll(state, self.player, "Pumpkin Zone 1")) - or state.has("Pumpkin Zone 1 Midway Bell", self.player), - "Pumpkin Zone 2 - Normal Exit": - lambda state: has_pipe_down(state, self.player) - and has_pipe_up(state, self.player) - and has_pipe_right(state, self.player) - and state.has("Water Physics", self.player) - and not is_auto_scroll(state, self.player, "Pumpkin Zone 2"), - "Pumpkin Zone 2 - Secret Exit": - lambda state: has_pipe_down(state, self.player) - and has_pipe_up(state, self.player) - and has_pipe_right(state, self.player) - and state.has("Water Physics", self.player) - and state.has_any(["Mushroom", "Fire Flower"], self.player) - and not is_auto_scroll(state, self.player, "Pumpkin Zone 2"), - "Pumpkin Zone 3 - Secret Exit": - lambda state: state.has("Carrot", self.player), - "Pumpkin Zone 4 - Boss": - lambda state: has_pipe_right(state, self.player), - "Mario Zone 1 - Normal Exit": - lambda state: has_pipe_right(state, self.player) - and (state.has_any(["Mushroom", "Fire Flower", "Carrot", "Mario Zone 1 Midway Bell"], - self.player) - or not is_auto_scroll(state, self.player, "Mario Zone 1")), - # It is possible to get as small mario, but it is a very precise jump and you will die afterward. - "Mario Zone 1 - Midway Bell": - lambda state: (state.has_any(["Mushroom", "Fire Flower", "Carrot"], self.player) - and has_pipe_right(state, self.player)) - or state.has("Mario Zone 1 Midway Bell", self.player), - "Mario Zone 4 - Boss": - lambda state: has_pipe_right(state, self.player), - "Turtle Zone 1 - Normal Exit": - lambda state: logic.not_blocked_by_sharks(state, self.player), - "Turtle Zone 2 - Normal Exit": - lambda state: has_pipe_up(state, self.player) - and has_pipe_down(state, self.player) - and has_pipe_right(state, self.player) - and has_pipe_left(state, self.player) - and state.has("Water Physics", self.player) - and not is_auto_scroll(state, self.player, "Turtle Zone 2"), - "Turtle Zone 2 - Midway Bell": - lambda state: state.has_any(["Water Physics", "Turtle Zone 2 Midway Bell"], self.player) - and not is_auto_scroll(state, self.player, "Turtle Zone 2"), - "Turtle Zone 2 - Secret Exit": - lambda state: has_pipe_up(state, self.player) - and state.has("Water Physics", self.player) - and not is_auto_scroll(state, self.player, "Turtle Zone 2"), - "Turtle Zone Secret Course - Normal Exit": - lambda state: state.has_any(["Fire Flower", "Carrot"], self.player), - "Turtle Zone 3 - Boss": - lambda state: has_pipe_right(state, self.player), - "Mario's Castle - Wario": - lambda state: has_pipe_right(state, self.player) - and has_pipe_left(state, self.player), - "Mario's Castle - Midway Bell": - lambda state: (has_pipe_right(state, self.player) - and has_pipe_left(state, self.player)) - or state.has("Mario's Castle Midway Bell", self.player) - } 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 in location_rules: - location.access_rule = location_rules[location.name] - elif location.name.endswith(("Coins", "Coin")): + 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.parent_region.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): diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index db90ce4b7246..c9a81db3b1d4 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -49,10 +49,22 @@ def mushroom_zone_coins(state, player, coins): return coins <= reachable_coins +def tree_zone_1_normal_exit(state, player): + return has_pipe_right(state, player) or state.has("Tree Zone 2 Midway Bell", player), + + def tree_zone_1_coins(state, player, coins): return coins <= 87 or not is_auto_scroll(state, player, "Tree Zone 1") +def tree_zone_2_secret_exit(state, player): + return has_pipe_right(state, player) and state.has("Carrot", player) + + +def tree_zone_2_midway_bell(state, player): + return has_pipe_right(state, player) or state.has("Tree Zone 2 Midway Bell", player) + + def tree_zone_2_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Tree Zone 2") reachable_coins = 18 @@ -69,6 +81,10 @@ def tree_zone_2_coins(state, player, coins): return coins <= reachable_coins +def tree_zone_3_normal_exit(state, player): + return not is_auto_scroll(state, player, "Tree Zone 3") + + def tree_zone_3_coins(state, player, coins): if is_auto_scroll(state, player, "Tree Zone 3"): return coins <= 4 @@ -79,6 +95,17 @@ def tree_zone_3_coins(state, player, coins): return state.has("Carrot", player) +def tree_zone_4_normal_exit(state, player): + if has_pipe_down(state, player): + return tree_zone_4_midway_bell(state, player) + return False + + +def tree_zone_4_midway_bell(state, player): + return ((has_pipe_right(state, player) and has_pipe_up(state, player)) + or state.has("Tree Zone 4 Midway Bell", player)) + + def tree_zone_4_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Tree Zone 4") reachable_coins = 0 @@ -103,6 +130,10 @@ def tree_zone_4_coins(state, player, coins): return coins <= reachable_coins +def tree_zone_5_boss(state, player): + return has_pipe_right(state, player) and (has_pipe_up(state, player) or state.has("Carrot", player)) + + def tree_zone_5_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Tree Zone 5") reachable_coins = 0 @@ -121,29 +152,15 @@ def tree_zone_5_coins(state, player, coins): return coins <= reachable_coins -def hippo_zone_coins(state, player, coins): - auto_scroll = is_auto_scroll(state, player, "Hippo Zone") - # This is all somewhat forgiving. - reachable_coins = 4 - if auto_scroll: - if state.has("Hippo Bubble", player): - reachable_coins = 160 - elif state.has("Carrot", player): - reachable_coins = 90 - elif state.has("Water Physics", player): - reachable_coins = 28 - else: - if state.has_any(["Water Physics", "Hippo Bubble", "Carrot"], player): - reachable_coins += 108 - if state.has_any(["Mushroom", "Fire Flower", "Hippo Bubble"], player): - reachable_coins += 6 - if state.has_all(["Fire Flower", "Water Physics"], player): - reachable_coins += 1 - if state.has("Hippo Bubble", player): - reachable_coins += 52 - return coins <= reachable_coins +def pumpkin_zone_1_normal_exit(state, player): + return pumpkin_zone_1_midway_bell(state, player) +def pumpkin_zone_1_midway_bell(state, player): + return ((has_pipe_down(state, player) and not is_auto_scroll(state, player, "Pumpkin Zone 1")) + or state.has("Pumpkin Zone 1 Midway Bell", player)) + + def pumpkin_zone_1_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Pumpkin Zone 1") if auto_scroll: @@ -156,6 +173,15 @@ def pumpkin_zone_1_coins(state, player, coins): return coins <= reachable_coins +def pumpkin_zone_2_normal_exit(state, player): + return has_pipe_down(state, player) and has_pipe_up(state, player) and has_pipe_right(state, player) and state.has( + "Water Physics", player) and not is_auto_scroll(state, player, "Pumpkin Zone 2") + + +def pumpkin_zone_2_secret_exit(state, player): + return pumpkin_zone_1_normal_exit(state, player) and state.has_any(["Mushroom", "Fire Flower"], player) + + def pumpkin_zone_2_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Pumpkin Zone 2") reachable_coins = 17 @@ -182,6 +208,10 @@ def pumpkin_zone_secret_course_1_coins(state, player, coins): return True +def pumpkin_zone_3_normal_exit(state, player): + return state.has("Carrot", player) + + def pumpkin_zone_3_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Pumpkin Zone 3") reachable_coins = 38 @@ -192,6 +222,10 @@ def pumpkin_zone_3_coins(state, player, coins): return coins <= reachable_coins +def pumpkin_zone_4_boss(state, player): + return has_pipe_right(state, player) + + def pumpkin_zone_4_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Pumpkin Zone 4") reachable_coins = 29 @@ -209,6 +243,21 @@ def pumpkin_zone_4_coins(state, player, coins): return coins <= reachable_coins +def mario_zone_1_normal_exit(state, player): + if has_pipe_right(state, player): + if state.has_any(["Mushroom", "Fire Flower", "Carrot", "Mario Zone 1 Midway Bell"], player): + return True + if is_auto_scroll(state, player, "Mario Zone 1"): + return True + return False + + +def mario_zone_1_midway_bell(state, player): + # It is possible to get as small mario, but it is a very precise jump and you will die afterward. + return ((state.has_any(["Mushroom", "Fire Flower", "Carrot"], player) and has_pipe_right(state, player)) + or state.has("Mario Zone 1 Midway Bell", player)) + + def mario_zone_1_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Mario Zone 1") reachable_coins = 0 @@ -247,6 +296,10 @@ def mario_zone_3_coins(state, player, coins): return coins <= reachable_coins +def mario_zone_4_boss(state, player): + return has_pipe_right(state, player) + + def mario_zone_4_coins(state, player, coins): return coins <= 63 or not is_auto_scroll(state, player, "Mario Zone 4") @@ -263,6 +316,10 @@ def not_blocked_by_sharks(state, player): return False +def turtle_zone_1_normal_exit(state, player): + return not_blocked_by_sharks(state, player) + + def turtle_zone_1_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Turtle Zone 1") reachable_coins = 30 @@ -279,6 +336,21 @@ def turtle_zone_1_coins(state, player, coins): return coins <= reachable_coins +def turtle_zone_2_normal_exit(state, player): + return (has_pipe_up(state, player) and has_pipe_down(state, player) and has_pipe_right(state, player) and + has_pipe_left(state, player) and state.has("Water Physics", player) + and not is_auto_scroll(state, player, "Turtle Zone 2")) + + +def turtle_zone_2_secret_exit(state, player): + return (has_pipe_up(state, player) and state.has("Water Physics", player) + and not is_auto_scroll(state, player, "Turtle Zone 2")) + + +def turtle_zone_2_midway_bell(state, player): + return turtle_zone_2_secret_exit(state, player) or state.has("Turtle Zone 2 Midway Bell", player) + + def turtle_zone_2_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Turtle Zone 2") reachable_coins = 2 @@ -301,6 +373,10 @@ def turtle_zone_2_coins(state, player, coins): return coins <= reachable_coins +def turtle_zone_secret_course_normal_exit(state, player): + return state.has_any(["Fire Flower", "Carrot"], player) + + def turtle_zone_secret_course_coins(state, player, coins): reachable_coins = 53 if state.has("Carrot", player): @@ -310,10 +386,55 @@ def turtle_zone_secret_course_coins(state, player, coins): return coins <= reachable_coins +def turtle_zone_3_boss(state, player): + return has_pipe_right(state, player) + + def turtle_zone_3_coins(state, player, coins): return state.has_any(["Water Physics", "Mushroom", "Fire Flower", "Carrot"], player) or coins <= 51 +def hippo_zone_normal_or_secret_exit(state, player): + return (state.has_any(["Hippo Bubble", "Water Physics"], player) + or (state.has("Carrot", player) + and not is_auto_scroll(state, player, "Hippo Zone"))) + + +def hippo_zone_coins(state, player, coins): + auto_scroll = is_auto_scroll(state, player, "Hippo Zone") + # This is all somewhat forgiving. + reachable_coins = 4 + if auto_scroll: + if state.has("Hippo Bubble", player): + reachable_coins = 160 + elif state.has("Carrot", player): + reachable_coins = 90 + elif state.has("Water Physics", player): + reachable_coins = 28 + else: + if state.has_any(["Water Physics", "Hippo Bubble", "Carrot"], player): + reachable_coins += 108 + if state.has_any(["Mushroom", "Fire Flower", "Hippo Bubble"], player): + reachable_coins += 6 + if state.has_all(["Fire Flower", "Water Physics"], player): + reachable_coins += 1 + if state.has("Hippo Bubble", player): + reachable_coins += 52 + return coins <= reachable_coins + + +def space_zone_1_normal_exit(state, player): + # It is possible, however tricky, to beat the Moon Stage without Carrot or Space Physics. + # However, it requires somewhat precisely jumping off enemies. Enemy shuffle may make this impossible. + # Instead, I will just always make one or the other required, since it is difficult without them anyway. + return state.has_any(["Space Physics", "Carrot"], player) + + +def space_zone_1_secret_exit(state, player): + # One or the other is actually necessary for the secret exit. + return state.has_any(["Space Physics", "Carrot"], player) and not is_auto_scroll(state, player, "Space Zone 1") + + def space_zone_1_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Space Zone 1") if auto_scroll: @@ -328,6 +449,11 @@ def space_zone_1_coins(state, player, coins): or state.has_any(["Carrot", "Space Physics"], player)) +def space_zone_2_midway_bell(state, player): + return state.has_any(["Space Physics", "Space Zone 2 Midway Bell", "Mushroom", "Fire Flower", + "Carrot"], player), + + def space_zone_2_boss(state, player): if has_pipe_right(state, player): if state.has("Space Physics", player): @@ -366,6 +492,18 @@ def space_zone_secret_course_coins(state, player, coins): return coins <= 96 or not is_auto_scroll(state, player, "Space Zone Secret Course") +def macro_zone_1_normal_exit(state, player): + return has_pipe_down(state, player) or state.has("Macro Zone 1 Midway Bell", player) + + +def macro_zone_1_secret_exit(state, player): + return state.has("Fire Flower", player) and has_pipe_up(state, player) and macro_zone_1_midway_bell(state, player) + + +def macro_zone_1_midway_bell(state, player): + return has_pipe_down(state, player) or state.has("Macro Zone 1 Midway Bell", player) + + def macro_zone_1_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Macro Zone 1") reachable_coins = 0 @@ -392,6 +530,16 @@ def macro_zone_secret_course_coins(state, player, coins): return state.has_any(["Mushroom", "Fire Flower"], player) +def macro_zone_2_normal_exit(state, player): + return (has_pipe_down(state, player) or state.has("Macro Zone 2 Midway Bell", player)) and state.has( + "Water Physics", player) and has_pipe_up(state, player) and not is_auto_scroll(state, player, "Macro Zone 2") + + +def macro_zone_2_midway_bell(state, player): + return ((has_pipe_down(state, player) and state.has("Water Physics", player)) + or state.has("Macro Zone 2 Midway Bell", player)) + + def macro_zone_2_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Macro Zone 2") if coins <= 27: @@ -404,6 +552,16 @@ def macro_zone_2_coins(state, player, coins): return coins <= 42 +def macro_zone_3_normal_exit(state, player): + return ((has_pipe_down(state, player) and has_pipe_down(state, player)) + or state.has("Macro Zone 3 Midway Bell", player),) + + +def macro_zone_3_midway_bell(state, player): + return ((has_pipe_down(state, player) and has_pipe_down(state, player)) + or state.has("Macro Zone 3 Midway Bell", player)) + + def macro_zone_3_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Macro Zone 3") reachable_coins = 7 @@ -426,6 +584,10 @@ def macro_zone_3_coins(state, player, coins): return coins <= reachable_coins +def macro_zone_4_boss(state, player): + return has_pipe_right(state, player) + + def macro_zone_4_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Macro Zone 4") reachable_coins = 61 @@ -434,3 +596,12 @@ def macro_zone_4_coins(state, player, coins): if state.has("Carrot", player): reachable_coins += 6 return coins <= reachable_coins + + +def marios_castle_wario(state, player): + return has_pipe_right(state, player) and has_pipe_left(state, player) + + +def marios_castle_midway_bell(state, player): + return ((has_pipe_right(state, player) and has_pipe_left(state, player)) + or state.has("Mario's Castle Midway Bell", player)) From 877b0b9063ac59800decb49284e4110120721ed6 Mon Sep 17 00:00:00 2001 From: alchav Date: Mon, 2 Sep 2024 20:19:45 -0400 Subject: [PATCH 084/113] Double Quotes --- worlds/marioland2/locations.py | 134 ++++++++++++++++----------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/worlds/marioland2/locations.py b/worlds/marioland2/locations.py index a2a9cb639812..ee1f976a0399 100644 --- a/worlds/marioland2/locations.py +++ b/worlds/marioland2/locations.py @@ -1,66 +1,66 @@ START_IDS = 7770000 locations = { - 'Mushroom Zone - Normal Exit': {'id': 0x00, 'ram_index': 0, 'type': 'level'}, - 'Mushroom Zone - Midway Bell': {'id': 0x00, 'ram_index': 0, 'clear_condition': ("Mushroom Zone Midway Bell", 1), 'type': 'bell'}, - 'Scenic Course - Normal Exit': {'id': 0x19, 'ram_index': 40, 'type': 'level'}, - 'Tree Zone 1 - Normal Exit': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone Progression", 1), 'type': 'level'}, - 'Tree Zone 1 - Midway Bell': {'id': 0x01, 'ram_index': 1, 'clear_condition': ("Tree Zone 1 Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone 2 - Normal Exit': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Progression", 2), 'type': 'level'}, - 'Tree Zone 2 - Secret Exit': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone Secret", 1), 'type': 'secret'}, - 'Tree Zone 2 - Midway Bell': {'id': 0x02, 'ram_index': 2, 'clear_condition': ("Tree Zone 2 Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone 3 - Normal Exit': {'id': 0x04, 'ram_index': 4, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, - 'Tree Zone 4 - Normal Exit': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone Progression", 3), 'type': 'level'}, - 'Tree Zone 4 - Midway Bell': {'id': 0x03, 'ram_index': 3, 'clear_condition': ("Tree Zone 4 Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone 5 - Boss': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Coin", 1), 'type': 'level'}, - 'Tree Zone 5 - Midway Bell': {'id': 0x05, 'ram_index': 5, 'clear_condition': ("Tree Zone 5 Midway Bell", 1), 'type': 'bell'}, - 'Tree Zone Secret Course - Normal Exit': {'id': 0x1D, 'ram_index': 36, 'type': 'level'}, - 'Hippo Zone - Normal or Secret Exit': {'id': 0x11, 'ram_index': 31, 'type': 'level'}, - 'Space Zone 1 - Normal Exit': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Progression", 1), 'type': 'level'}, - 'Space Zone 1 - Secret Exit': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone Secret", 1), 'type': 'secret'}, - 'Space Zone 1 - Midway Bell': {'id': 0x12, 'ram_index': 16, 'clear_condition': ("Space Zone 1 Midway Bell", 1), 'type': 'bell'}, - 'Space Zone Secret Course - Normal Exit': {'id': 0x1C, 'ram_index': 41, 'type': 'level'}, - 'Space Zone 2 - Boss': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Coin", 1), 'type': 'level'}, - 'Space Zone 2 - Midway Bell': {'id': 0x13, 'ram_index': 17, 'clear_condition': ("Space Zone 2 Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 1 - Normal Exit': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Progression", 1), 'type': 'level'}, - 'Macro Zone 1 - Secret Exit': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone Secret 1", 1), 'type': 'secret'}, - 'Macro Zone 1 - Midway Bell': {'id': 0x14, 'ram_index': 11, 'clear_condition': ("Macro Zone 1 Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 2 - Normal Exit': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone Progression", 2), 'type': 'level'}, - 'Macro Zone 2 - Midway Bell': {'id': 0x15, 'ram_index': 12, 'clear_condition': ("Macro Zone 2 Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 3 - Normal Exit': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone Progression", 3), 'type': 'level'}, - 'Macro Zone 3 - Midway Bell': {'id': 0x16, 'ram_index': 13, 'clear_condition': ("Macro Zone 3 Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone 4 - Boss': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Coin", 1), 'type': 'level'}, - 'Macro Zone 4 - Midway Bell': {'id': 0x17, 'ram_index': 14, 'clear_condition': ("Macro Zone 4 Midway Bell", 1), 'type': 'bell'}, - 'Macro Zone Secret Course - Normal Exit': {'id': 0x1E, 'ram_index': 35, 'clear_condition': ("Macro Zone Secret 2", 1), 'type': 'level'}, - 'Pumpkin Zone 1 - Normal Exit': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone Progression", 1), 'type': 'level'}, - 'Pumpkin Zone 1 - Midway Bell': {'id': 0x06, 'ram_index': 6, 'clear_condition': ("Pumpkin Zone 1 Midway Bell", 1), 'type': 'bell'}, - 'Pumpkin Zone 2 - Normal Exit': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Progression", 2), 'type': 'level'}, - 'Pumpkin Zone 2 - Secret Exit': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone Secret 1", 1), 'type': 'secret'}, - 'Pumpkin Zone 2 - Midway Bell': {'id': 0x07, 'ram_index': 7, 'clear_condition': ("Pumpkin Zone 2 Midway Bell", 2), 'type': 'bell'}, - 'Pumpkin Zone 3 - Normal Exit': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Progression", 3), 'type': 'level'}, - 'Pumpkin Zone 3 - Secret Exit': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone Secret 2", 1), 'type': 'secret'}, - 'Pumpkin Zone 3 - Midway Bell': {'id': 0x08, 'ram_index': 8, 'clear_condition': ("Pumpkin Zone 3 Midway Bell", 3), 'type': 'bell'}, - "Pumpkin Zone 4 - Boss": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Coin", 1), 'type': 'level'}, - "Pumpkin Zone 4 - Midway Bell": {'id': 0x09, 'ram_index': 9, 'clear_condition': ("Pumpkin Zone 4 Midway Bell", 1), 'type': 'bell'}, - 'Pumpkin Zone Secret Course 1 - Normal Exit': {'id': 0x1B, 'ram_index': 38, 'type': 'level'}, - 'Pumpkin Zone Secret Course 2 - Normal Exit': {'id': 0x1F, 'ram_index': 39, 'type': 'level'}, - 'Mario Zone 1 - Normal Exit': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone Progression", 1), 'type': 'level'}, - 'Mario Zone 1 - Midway Bell': {'id': 0x0A, 'ram_index': 26, 'clear_condition': ("Mario Zone 1 Midway Bell", 1), 'type': 'bell'}, - 'Mario Zone 2 - Normal Exit': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone Progression", 2), 'type': 'level'}, - 'Mario Zone 2 - Midway Bell': {'id': 0x0B, 'ram_index': 27, 'clear_condition': ("Mario Zone 2 Midway Bell", 1), 'type': 'bell'}, - 'Mario Zone 3 - Normal Exit': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone Progression", 3), 'type': 'level'}, - 'Mario Zone 3 - Midway Bell': {'id': 0x0C, 'ram_index': 28, 'clear_condition': ("Mario Zone 3 Midway Bell", 1), 'type': 'bell'}, - 'Mario Zone 4 - Boss': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Coin", 1), 'type': 'level'}, - 'Mario Zone 4 - Midway Bell': {'id': 0x0D, 'ram_index': 29, 'clear_condition': ("Mario Zone 4 Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone 1 - Normal Exit': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone Progression", 1), 'type': 'level'}, - 'Turtle Zone 1 - Midway Bell': {'id': 0x0E, 'ram_index': 21, 'clear_condition': ("Turtle Zone 1 Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone 2 - Normal Exit': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Progression", 2), 'type': 'level'}, - 'Turtle Zone 2 - Secret Exit': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone Secret", 1), 'type': 'secret'}, - 'Turtle Zone 2 - Midway Bell': {'id': 0x0F, 'ram_index': 22, 'clear_condition': ("Turtle Zone 2 Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone 3 - Boss': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Coin", 1), 'type': 'level'}, - 'Turtle Zone 3 - Midway Bell': {'id': 0x10, 'ram_index': 23, 'clear_condition': ("Turtle Zone 3 Midway Bell", 1), 'type': 'bell'}, - 'Turtle Zone Secret Course - Normal Exit': {'id': 0x1A, 'ram_index': 37, 'type': 'level'}, - "Mario's Castle - Midway Bell": {'id': 24, 'ram_index': 24, 'clear_condition': ("Mario's Castle Midway Bell", 1), 'type': 'bell'}, + "Mushroom Zone - Normal Exit": {"id": 0x00, "ram_index": 0, "type": "level"}, + "Mushroom Zone - Midway Bell": {"id": 0x00, "ram_index": 0, "clear_condition": ("Mushroom Zone Midway Bell", 1), "type": "bell"}, + "Scenic Course - Normal Exit": {"id": 0x19, "ram_index": 40, "type": "level"}, + "Tree Zone 1 - Normal Exit": {"id": 0x01, "ram_index": 1, "clear_condition": ("Tree Zone Progression", 1), "type": "level"}, + "Tree Zone 1 - Midway Bell": {"id": 0x01, "ram_index": 1, "clear_condition": ("Tree Zone 1 Midway Bell", 1), "type": "bell"}, + "Tree Zone 2 - Normal Exit": {"id": 0x02, "ram_index": 2, "clear_condition": ("Tree Zone Progression", 2), "type": "level"}, + "Tree Zone 2 - Secret Exit": {"id": 0x02, "ram_index": 2, "clear_condition": ("Tree Zone Secret", 1), "type": "secret"}, + "Tree Zone 2 - Midway Bell": {"id": 0x02, "ram_index": 2, "clear_condition": ("Tree Zone 2 Midway Bell", 1), "type": "bell"}, + "Tree Zone 3 - Normal Exit": {"id": 0x04, "ram_index": 4, "clear_condition": ("Tree Zone Progression", 3), "type": "level"}, + "Tree Zone 4 - Normal Exit": {"id": 0x03, "ram_index": 3, "clear_condition": ("Tree Zone Progression", 3), "type": "level"}, + "Tree Zone 4 - Midway Bell": {"id": 0x03, "ram_index": 3, "clear_condition": ("Tree Zone 4 Midway Bell", 1), "type": "bell"}, + "Tree Zone 5 - Boss": {"id": 0x05, "ram_index": 5, "clear_condition": ("Tree Coin", 1), "type": "level"}, + "Tree Zone 5 - Midway Bell": {"id": 0x05, "ram_index": 5, "clear_condition": ("Tree Zone 5 Midway Bell", 1), "type": "bell"}, + "Tree Zone Secret Course - Normal Exit": {"id": 0x1D, "ram_index": 36, "type": "level"}, + "Hippo Zone - Normal or Secret Exit": {"id": 0x11, "ram_index": 31, "type": "level"}, + "Space Zone 1 - Normal Exit": {"id": 0x12, "ram_index": 16, "clear_condition": ("Space Zone Progression", 1), "type": "level"}, + "Space Zone 1 - Secret Exit": {"id": 0x12, "ram_index": 16, "clear_condition": ("Space Zone Secret", 1), "type": "secret"}, + "Space Zone 1 - Midway Bell": {"id": 0x12, "ram_index": 16, "clear_condition": ("Space Zone 1 Midway Bell", 1), "type": "bell"}, + "Space Zone Secret Course - Normal Exit": {"id": 0x1C, "ram_index": 41, "type": "level"}, + "Space Zone 2 - Boss": {"id": 0x13, "ram_index": 17, "clear_condition": ("Space Coin", 1), "type": "level"}, + "Space Zone 2 - Midway Bell": {"id": 0x13, "ram_index": 17, "clear_condition": ("Space Zone 2 Midway Bell", 1), "type": "bell"}, + "Macro Zone 1 - Normal Exit": {"id": 0x14, "ram_index": 11, "clear_condition": ("Macro Zone Progression", 1), "type": "level"}, + "Macro Zone 1 - Secret Exit": {"id": 0x14, "ram_index": 11, "clear_condition": ("Macro Zone Secret 1", 1), "type": "secret"}, + "Macro Zone 1 - Midway Bell": {"id": 0x14, "ram_index": 11, "clear_condition": ("Macro Zone 1 Midway Bell", 1), "type": "bell"}, + "Macro Zone 2 - Normal Exit": {"id": 0x15, "ram_index": 12, "clear_condition": ("Macro Zone Progression", 2), "type": "level"}, + "Macro Zone 2 - Midway Bell": {"id": 0x15, "ram_index": 12, "clear_condition": ("Macro Zone 2 Midway Bell", 1), "type": "bell"}, + "Macro Zone 3 - Normal Exit": {"id": 0x16, "ram_index": 13, "clear_condition": ("Macro Zone Progression", 3), "type": "level"}, + "Macro Zone 3 - Midway Bell": {"id": 0x16, "ram_index": 13, "clear_condition": ("Macro Zone 3 Midway Bell", 1), "type": "bell"}, + "Macro Zone 4 - Boss": {"id": 0x17, "ram_index": 14, "clear_condition": ("Macro Coin", 1), "type": "level"}, + "Macro Zone 4 - Midway Bell": {"id": 0x17, "ram_index": 14, "clear_condition": ("Macro Zone 4 Midway Bell", 1), "type": "bell"}, + "Macro Zone Secret Course - Normal Exit": {"id": 0x1E, "ram_index": 35, "clear_condition": ("Macro Zone Secret 2", 1), "type": "level"}, + "Pumpkin Zone 1 - Normal Exit": {"id": 0x06, "ram_index": 6, "clear_condition": ("Pumpkin Zone Progression", 1), "type": "level"}, + "Pumpkin Zone 1 - Midway Bell": {"id": 0x06, "ram_index": 6, "clear_condition": ("Pumpkin Zone 1 Midway Bell", 1), "type": "bell"}, + "Pumpkin Zone 2 - Normal Exit": {"id": 0x07, "ram_index": 7, "clear_condition": ("Pumpkin Zone Progression", 2), "type": "level"}, + "Pumpkin Zone 2 - Secret Exit": {"id": 0x07, "ram_index": 7, "clear_condition": ("Pumpkin Zone Secret 1", 1), "type": "secret"}, + "Pumpkin Zone 2 - Midway Bell": {"id": 0x07, "ram_index": 7, "clear_condition": ("Pumpkin Zone 2 Midway Bell", 2), "type": "bell"}, + "Pumpkin Zone 3 - Normal Exit": {"id": 0x08, "ram_index": 8, "clear_condition": ("Pumpkin Zone Progression", 3), "type": "level"}, + "Pumpkin Zone 3 - Secret Exit": {"id": 0x08, "ram_index": 8, "clear_condition": ("Pumpkin Zone Secret 2", 1), "type": "secret"}, + "Pumpkin Zone 3 - Midway Bell": {"id": 0x08, "ram_index": 8, "clear_condition": ("Pumpkin Zone 3 Midway Bell", 3), "type": "bell"}, + "Pumpkin Zone 4 - Boss": {"id": 0x09, "ram_index": 9, "clear_condition": ("Pumpkin Coin", 1), "type": "level"}, + "Pumpkin Zone 4 - Midway Bell": {"id": 0x09, "ram_index": 9, "clear_condition": ("Pumpkin Zone 4 Midway Bell", 1), "type": "bell"}, + "Pumpkin Zone Secret Course 1 - Normal Exit": {"id": 0x1B, "ram_index": 38, "type": "level"}, + "Pumpkin Zone Secret Course 2 - Normal Exit": {"id": 0x1F, "ram_index": 39, "type": "level"}, + "Mario Zone 1 - Normal Exit": {"id": 0x0A, "ram_index": 26, "clear_condition": ("Mario Zone Progression", 1), "type": "level"}, + "Mario Zone 1 - Midway Bell": {"id": 0x0A, "ram_index": 26, "clear_condition": ("Mario Zone 1 Midway Bell", 1), "type": "bell"}, + "Mario Zone 2 - Normal Exit": {"id": 0x0B, "ram_index": 27, "clear_condition": ("Mario Zone Progression", 2), "type": "level"}, + "Mario Zone 2 - Midway Bell": {"id": 0x0B, "ram_index": 27, "clear_condition": ("Mario Zone 2 Midway Bell", 1), "type": "bell"}, + "Mario Zone 3 - Normal Exit": {"id": 0x0C, "ram_index": 28, "clear_condition": ("Mario Zone Progression", 3), "type": "level"}, + "Mario Zone 3 - Midway Bell": {"id": 0x0C, "ram_index": 28, "clear_condition": ("Mario Zone 3 Midway Bell", 1), "type": "bell"}, + "Mario Zone 4 - Boss": {"id": 0x0D, "ram_index": 29, "clear_condition": ("Mario Coin", 1), "type": "level"}, + "Mario Zone 4 - Midway Bell": {"id": 0x0D, "ram_index": 29, "clear_condition": ("Mario Zone 4 Midway Bell", 1), "type": "bell"}, + "Turtle Zone 1 - Normal Exit": {"id": 0x0E, "ram_index": 21, "clear_condition": ("Turtle Zone Progression", 1), "type": "level"}, + "Turtle Zone 1 - Midway Bell": {"id": 0x0E, "ram_index": 21, "clear_condition": ("Turtle Zone 1 Midway Bell", 1), "type": "bell"}, + "Turtle Zone 2 - Normal Exit": {"id": 0x0F, "ram_index": 22, "clear_condition": ("Turtle Zone Progression", 2), "type": "level"}, + "Turtle Zone 2 - Secret Exit": {"id": 0x0F, "ram_index": 22, "clear_condition": ("Turtle Zone Secret", 1), "type": "secret"}, + "Turtle Zone 2 - Midway Bell": {"id": 0x0F, "ram_index": 22, "clear_condition": ("Turtle Zone 2 Midway Bell", 1), "type": "bell"}, + "Turtle Zone 3 - Boss": {"id": 0x10, "ram_index": 23, "clear_condition": ("Turtle Coin", 1), "type": "level"}, + "Turtle Zone 3 - Midway Bell": {"id": 0x10, "ram_index": 23, "clear_condition": ("Turtle Zone 3 Midway Bell", 1), "type": "bell"}, + "Turtle Zone Secret Course - Normal Exit": {"id": 0x1A, "ram_index": 37, "type": "level"}, + "Mario's Castle - Midway Bell": {"id": 24, "ram_index": 24, "clear_condition": ("Mario's Castle Midway Bell", 1), "type": "bell"}, } @@ -459,13 +459,13 @@ # eligible_levels = [0, 1, 2, 3, 5, 8, 9, 11, 13, 14, 16, 19, 20, 22, 23, 25, 30, 31] level_id_to_name = { - 0: 'Mushroom Zone', 25: 'Scenic Course', 1: 'Tree Zone 1', 2: 'Tree Zone 2', 4: 'Tree Zone 3', 3: 'Tree Zone 4', - 5: 'Tree Zone 5', 29: 'Tree Zone Secret Course', 17: 'Hippo Zone', 18: 'Space Zone 1', - 28: 'Space Zone Secret Course', 19: 'Space Zone 2', 20: 'Macro Zone 1', 21: 'Macro Zone 2', 22: 'Macro Zone 3', - 23: 'Macro Zone 4', 30: 'Macro Zone Secret Course', 6: 'Pumpkin Zone 1', 7: 'Pumpkin Zone 2', - 8: 'Pumpkin Zone 3', 9: 'Pumpkin Zone 4', 27: 'Pumpkin Zone Secret Course 1', 31: 'Pumpkin Zone Secret Course 2', - 10: 'Mario Zone 1', 11: 'Mario Zone 2', 12: 'Mario Zone 3', 13: 'Mario Zone 4', 14: 'Turtle Zone 1', - 15: 'Turtle Zone 2', 16: 'Turtle Zone 3', 26: 'Turtle Zone Secret Course', 24: "Mario's Castle" + 0: "Mushroom Zone", 25: "Scenic Course", 1: "Tree Zone 1", 2: "Tree Zone 2", 4: "Tree Zone 3", 3: "Tree Zone 4", + 5: "Tree Zone 5", 29: "Tree Zone Secret Course", 17: "Hippo Zone", 18: "Space Zone 1", + 28: "Space Zone Secret Course", 19: "Space Zone 2", 20: "Macro Zone 1", 21: "Macro Zone 2", 22: "Macro Zone 3", + 23: "Macro Zone 4", 30: "Macro Zone Secret Course", 6: "Pumpkin Zone 1", 7: "Pumpkin Zone 2", + 8: "Pumpkin Zone 3", 9: "Pumpkin Zone 4", 27: "Pumpkin Zone Secret Course 1", 31: "Pumpkin Zone Secret Course 2", + 10: "Mario Zone 1", 11: "Mario Zone 2", 12: "Mario Zone 3", 13: "Mario Zone 4", 14: "Turtle Zone 1", + 15: "Turtle Zone 2", 16: "Turtle Zone 3", 26: "Turtle Zone Secret Course", 24: "Mario's Castle" } level_name_to_id = {name: level_id for level_id, name in level_id_to_name.items()} From e7d4e5a70c7578e20fb0700d2c2667602145036b Mon Sep 17 00:00:00 2001 From: alchav Date: Mon, 2 Sep 2024 20:21:55 -0400 Subject: [PATCH 085/113] Locations -> Full --- worlds/marioland2/__init__.py | 6 +++--- worlds/marioland2/options.py | 2 +- worlds/marioland2/rom.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 8507e69337e2..6e5301eb91a0 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -119,11 +119,11 @@ def generate_early(self): self.auto_scroll_levels[level] = 2 elif self.options.auto_scroll_mode == "chaos": self.auto_scroll_levels[level] = self.random.randint(1, 3) - if (self.options.accessibility == "locations" + if (self.options.accessibility == "full" and level_id_to_name[level] in unbeatable_scroll_levels and self.auto_scroll_levels[level] != 2): self.auto_scroll_levels[level] = self.random.choice([1, 3]) - elif (self.options.accessibility == "locations" + 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: @@ -182,7 +182,7 @@ def create_regions(self): 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 == "locations" or self.options.auto_scroll_mode == "always": + 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): self.max_coin_locations[level] = min(auto_scroll_max[level], self.max_coin_locations[level]) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index b4ab673d7780..087beee569eb 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -144,7 +144,7 @@ class AutoScrollMode(Choice): Global Cancel Item: Auto Scroll levels will stop auto-scrolling after obtaining the Auto Scroll Cancel item. Level Cancel Items: As with Cancel Item, but there is a separate cancel item for each auto scroll level. Chaos: Each level will randomly always auto scroll, have an Auto Scroll Trap, or have an Auto Scroll Cancel item. - The effects of Trap and Cancel items are permanent! If Accessibility is not set to Locations, + The effects of Trap and Cancel items are permanent! If Accessibility is not set to Full, Traps may cause locations to become permanently unreachable. With individual level items, the number of auto scroll levels may be limited by the available space in the item pool. diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index 1b6be20af78b..c031dfae1338 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -26,7 +26,7 @@ def randomize_music(patch, random): def generate_output(self, output_directory: str): - patch = SuperMarioLand2ProcedurePatch(player=self.player, player_name=self.multiworld.player_name[self.player]) + patch = SuperMarioLand2ProcedurePatch(player=self.player, player_name=self.player_name) patch.write_file("basepatch.bsdiff4", pkgutil.get_data(__name__, "basepatch.bsdiff4")) random = self.random From d3ccecaec3addfa450205d5ddd40a661c9cd199c Mon Sep 17 00:00:00 2001 From: alchav Date: Mon, 2 Sep 2024 20:23:07 -0400 Subject: [PATCH 086/113] More Locations -> Full --- worlds/marioland2/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index 087beee569eb..c736b700a688 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -76,7 +76,7 @@ class CoinsanityChecks(Range): """ Number of Coinsanity checks. A higher number means more checks, and smaller coin amounts per coin item in the item pool. - If Accessibility is set to Locations, auto-scroll levels may have a lower maximum count, which may lead to this + If Accessibility is set to Full, auto-scroll levels may have a lower maximum count, which may lead to this value being limited. """ default_name = "Coinsanity Checks" From b70dbdfa1a94c0a9b210cfc89b581f0d3f83ded6 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Mon, 2 Sep 2024 20:24:48 -0400 Subject: [PATCH 087/113] use has_from_list_unique Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/__init__.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 6e5301eb91a0..8525b9b7574d 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -236,11 +236,9 @@ def set_rules(self): ["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("Tree Coin", self.player), state.has("Space Coin", self.player), - state.has("Macro Coin", self.player), state.has("Pumpkin Coin", self.player), - state.has("Mario Coin", self.player), state.has("Turtle Coin", self.player) - ].count(True) >= self.options.required_golden_coins) + 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(): From bc558d702dd9662f8d53ff1f7310c5cb1c297908 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Mon, 2 Sep 2024 20:25:28 -0400 Subject: [PATCH 088/113] use self.player_name Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 8525b9b7574d..318dc7e9c2d3 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -403,7 +403,7 @@ def create_items(self): 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.multiworld.player_name[self.player]}") + 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: From e8c5f8ada9f33f677b09605bc57fd8a3fe06ce08 Mon Sep 17 00:00:00 2001 From: alchav Date: Mon, 2 Sep 2024 20:27:33 -0400 Subject: [PATCH 089/113] More self.player_name --- worlds/marioland2/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 6e5301eb91a0..d8f1f132f682 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -405,7 +405,7 @@ def create_items(self): 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.multiworld.player_name[self.player]}") + 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: @@ -434,7 +434,7 @@ def modify_multidata(self, multidata: dict): '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.multiworld.player_name[self.player]] + multidata["connect_names"][new_name] = multidata["connect_names"][self.player_name] class MarioLand2Location(Location): From 859f3bc65d5cf97e26c8cc4e4f80bdf73399bf89 Mon Sep 17 00:00:00 2001 From: alchav Date: Tue, 3 Sep 2024 10:14:13 -0400 Subject: [PATCH 090/113] Fix unbeatable scroll levels not being forced cancellable on chaos --- worlds/marioland2/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index ccecd9beec27..539e62c1fa42 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -118,11 +118,11 @@ def generate_early(self): 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": - self.auto_scroll_levels[level] = self.random.randint(1, 3) if (self.options.accessibility == "full" - and level_id_to_name[level] in unbeatable_scroll_levels - and self.auto_scroll_levels[level] != 2): - self.auto_scroll_levels[level] = self.random.choice([1, 3]) + 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 From aae1b28e4acdabe9fa135805d823bc44b61b416f Mon Sep 17 00:00:00 2001 From: alchav Date: Tue, 3 Sep 2024 10:28:54 -0400 Subject: [PATCH 091/113] Update auto_scroll_max --- worlds/marioland2/locations.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/worlds/marioland2/locations.py b/worlds/marioland2/locations.py index ee1f976a0399..1c3ee9897bfa 100644 --- a/worlds/marioland2/locations.py +++ b/worlds/marioland2/locations.py @@ -476,10 +476,11 @@ "Tree Zone 1": 87, "Tree Zone 2": 68, "Tree Zone 3": 4, - "Tree Zone 4": 64, + "Tree Zone 4": 28, "Tree Zone 5": 22, "Space Zone 1": 72, "Space Zone 2": 113, + "Space Zone Secret Course": 96, "Macro Zone 1": 74, "Macro Zone 2": 27, "Macro Zone 3": 63, @@ -490,7 +491,7 @@ "Pumpkin Zone 4": 45, "Pumpkin Zone Secret Course 1": 172, "Mario Zone 1": 68, - "Mario Zone 3": 29, + "Mario Zone 3": 47, "Mario Zone 4": 63, "Turtle Zone 1": 66, "Turtle Zone 2": 8, From f7c3a8c20b16e44fe12626ffe550920ed16705c9 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 3 Sep 2024 10:34:23 -0400 Subject: [PATCH 092/113] Update worlds/marioland2/logic.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/logic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index c9a81db3b1d4..7db927d18fbe 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -439,11 +439,11 @@ def space_zone_1_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Space Zone 1") if auto_scroll: reachable_coins = 12 - if state.has_any(["Carrot", "Space Physics"], 1): + if state.has_any(["Carrot", "Space Physics"], player): reachable_coins += 20 # If you have Space Physics, you can't make it up to the upper section. We have to assume you might have it, # so the coins up there must be out of logic if there is auto scrolling. - if state.has("Space Physics", 1): + if state.has("Space Physics", player): reachable_coins += 40 return (coins <= 21 or (coins <= 50 and state.has_any(["Mushroom", "Fire Flower"], player)) or state.has_any(["Carrot", "Space Physics"], player)) From 7fabe1c38eeac0319cd0848643a9dc374b579e07 Mon Sep 17 00:00:00 2001 From: alchav Date: Tue, 3 Sep 2024 11:12:30 -0400 Subject: [PATCH 093/113] Fix Space Zone 1 coin logic --- worlds/marioland2/logic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 7db927d18fbe..2cdd6432ac37 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -445,6 +445,7 @@ def space_zone_1_coins(state, player, coins): # so the coins up there must be out of logic if there is auto scrolling. if state.has("Space Physics", player): reachable_coins += 40 + return coins <= reachable_coins return (coins <= 21 or (coins <= 50 and state.has_any(["Mushroom", "Fire Flower"], player)) or state.has_any(["Carrot", "Space Physics"], player)) From 52fea1f60047011522b8f1a76e6d4493258888ed Mon Sep 17 00:00:00 2001 From: alchav Date: Fri, 6 Sep 2024 00:27:01 -0400 Subject: [PATCH 094/113] Fix non-coin rule function name generation --- worlds/marioland2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 539e62c1fa42..d82bf36b7668 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -252,7 +252,7 @@ def set_rules(self): location.access_rule = lambda state, coin_rule=rule, num_coins=coins: \ coin_rule(state, self.player, num_coins) else: - rule = getattr(logic, location.parent_region.name.lower().replace( + 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) From da43bdaabfff3d374e1659957852e7fba2a36768 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Sat, 7 Sep 2024 16:27:17 -0400 Subject: [PATCH 095/113] Tree zone 4 exit rewrite Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/logic.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 2cdd6432ac37..cda93422c364 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -96,9 +96,7 @@ def tree_zone_3_coins(state, player, coins): def tree_zone_4_normal_exit(state, player): - if has_pipe_down(state, player): - return tree_zone_4_midway_bell(state, player) - return False + return has_pipe_down(state, player) and tree_zone_4_midway_bell(state, player) def tree_zone_4_midway_bell(state, player): From 4c53035422a43a694754052b3abbc4ae14bb98c5 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Sat, 7 Sep 2024 16:29:57 -0400 Subject: [PATCH 096/113] Update worlds/marioland2/logic.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/logic.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index cda93422c364..8fdac9283978 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -449,8 +449,7 @@ def space_zone_1_coins(state, player, coins): def space_zone_2_midway_bell(state, player): - return state.has_any(["Space Physics", "Space Zone 2 Midway Bell", "Mushroom", "Fire Flower", - "Carrot"], player), + return state.has_any(["Space Physics", "Space Zone 2 Midway Bell", "Mushroom", "Fire Flower", "Carrot"], player) def space_zone_2_boss(state, player): From 2892855013506d99aff900fc4be7aa7e1e2478ab Mon Sep 17 00:00:00 2001 From: alchav Date: Sun, 8 Sep 2024 22:37:35 -0400 Subject: [PATCH 097/113] Logic fixes --- worlds/marioland2/logic.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 2cdd6432ac37..c8ace5c2cc1e 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -49,14 +49,14 @@ def mushroom_zone_coins(state, player, coins): return coins <= reachable_coins -def tree_zone_1_normal_exit(state, player): - return has_pipe_right(state, player) or state.has("Tree Zone 2 Midway Bell", player), - - def tree_zone_1_coins(state, player, coins): return coins <= 87 or not is_auto_scroll(state, player, "Tree Zone 1") +def tree_zone_2_normal_exit(state, player): + return has_pipe_right(state, player) or state.has("Tree Zone 2 Midway Bell", player), + + def tree_zone_2_secret_exit(state, player): return has_pipe_right(state, player) and state.has("Carrot", player) @@ -179,7 +179,7 @@ def pumpkin_zone_2_normal_exit(state, player): def pumpkin_zone_2_secret_exit(state, player): - return pumpkin_zone_1_normal_exit(state, player) and state.has_any(["Mushroom", "Fire Flower"], player) + return pumpkin_zone_2_normal_exit(state, player) and state.has_any(["Mushroom", "Fire Flower"], player) def pumpkin_zone_2_coins(state, player, coins): @@ -208,7 +208,7 @@ def pumpkin_zone_secret_course_1_coins(state, player, coins): return True -def pumpkin_zone_3_normal_exit(state, player): +def pumpkin_zone_3_secret_exit(state, player): return state.has("Carrot", player) @@ -348,7 +348,8 @@ def turtle_zone_2_secret_exit(state, player): def turtle_zone_2_midway_bell(state, player): - return turtle_zone_2_secret_exit(state, player) or state.has("Turtle Zone 2 Midway Bell", player) + return ((state.has("Water Physics", player) and not is_auto_scroll(state, player, "Turtle Zone 2")) + or state.has("Turtle Zone 2 Midway Bell", player)) def turtle_zone_2_coins(state, player, coins): @@ -554,13 +555,12 @@ def macro_zone_2_coins(state, player, coins): def macro_zone_3_normal_exit(state, player): - return ((has_pipe_down(state, player) and has_pipe_down(state, player)) + return ((has_pipe_down(state, player) and has_pipe_up(state, player)) or state.has("Macro Zone 3 Midway Bell", player),) def macro_zone_3_midway_bell(state, player): - return ((has_pipe_down(state, player) and has_pipe_down(state, player)) - or state.has("Macro Zone 3 Midway Bell", player)) + return macro_zone_3_normal_exit(state, player) def macro_zone_3_coins(state, player, coins): From c27f5be8e73aaaa49fcab0c92c9a42e13907864e Mon Sep 17 00:00:00 2001 From: alchav Date: Sun, 8 Sep 2024 22:40:22 -0400 Subject: [PATCH 098/113] Pumpkin Zone Secret Course 1 return false --- worlds/marioland2/logic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index ab51604c9777..6a607d36411f 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -204,6 +204,7 @@ def pumpkin_zone_secret_course_1_coins(state, player, coins): if auto_scroll: return coins <= 172 return True + return False def pumpkin_zone_3_secret_exit(state, player): From c1504c795e3b342f52d27db18cdeb72c20c7a1fa Mon Sep 17 00:00:00 2001 From: alchav Date: Sun, 8 Sep 2024 22:40:53 -0400 Subject: [PATCH 099/113] Macro Zone 2 return false --- worlds/marioland2/logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 6a607d36411f..a7ebc8a6c346 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -550,7 +550,7 @@ def macro_zone_2_coins(state, player, coins): if state.has("Macro Zone 2 Midway Bell", player): # Cannot return to the first section from the bell return coins <= 42 - + return False def macro_zone_3_normal_exit(state, player): return ((has_pipe_down(state, player) and has_pipe_up(state, player)) From 7d5a6829cd909d8d641a50351d0ec82112cd5834 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:19:13 -0400 Subject: [PATCH 100/113] Update worlds/marioland2/__init__.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index d82bf36b7668..887185efed52 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -105,7 +105,7 @@ def generate_early(self): if self.options.marios_castle_midway_bell: self.sprite_data["Mario's Castle"][35]["sprite"] = "Midway Bell" - if self.options.auto_scroll_chances == -1: + 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) From 94e58b25c79c69a5fb81fcdcc68fa52da00d7093 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:21:39 -0400 Subject: [PATCH 101/113] Update worlds/marioland2/__init__.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 887185efed52..d287c57803a5 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -108,7 +108,7 @@ def generate_early(self): 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) + 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 From 010c96aa229cd734bd177e819cd42cdf13457733 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:22:28 -0400 Subject: [PATCH 102/113] Update worlds/marioland2/logic.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index a7ebc8a6c346..d161cf54d0f5 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -54,7 +54,7 @@ def tree_zone_1_coins(state, player, coins): def tree_zone_2_normal_exit(state, player): - return has_pipe_right(state, player) or state.has("Tree Zone 2 Midway Bell", player), + return has_pipe_right(state, player) or state.has("Tree Zone 2 Midway Bell", player) def tree_zone_2_secret_exit(state, player): From da3a7c5b049203029b8a9c85775efc2227546127 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:22:40 -0400 Subject: [PATCH 103/113] Update worlds/marioland2/logic.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index d161cf54d0f5..a931b192ecd8 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -554,7 +554,7 @@ def macro_zone_2_coins(state, player, coins): def macro_zone_3_normal_exit(state, player): return ((has_pipe_down(state, player) and has_pipe_up(state, player)) - or state.has("Macro Zone 3 Midway Bell", player),) + or state.has("Macro Zone 3 Midway Bell", player)) def macro_zone_3_midway_bell(state, player): From 2e6226f3ae60ea159e2faa5da4db2761b705767f Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:23:04 -0400 Subject: [PATCH 104/113] Update worlds/marioland2/logic.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/marioland2/logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index a931b192ecd8..1cd2bcb1f341 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -565,7 +565,7 @@ def macro_zone_3_coins(state, player, coins): auto_scroll = is_auto_scroll(state, player, "Macro Zone 3") reachable_coins = 7 if not auto_scroll: - reachable_coins = 24 + reachable_coins += 17 if has_pipe_up(state, player) and has_pipe_down(state, player): if auto_scroll: reachable_coins += 56 From 6126df28ac9391ffe67b2a05d68f6598a0b4eaba Mon Sep 17 00:00:00 2001 From: alchav Date: Tue, 10 Sep 2024 10:23:53 -0400 Subject: [PATCH 105/113] Cleanup --- worlds/marioland2/logic.py | 1 + worlds/marioland2/rom.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index a7ebc8a6c346..eea1e57221a1 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -552,6 +552,7 @@ def macro_zone_2_coins(state, player, coins): return coins <= 42 return False + def macro_zone_3_normal_exit(state, player): return ((has_pipe_down(state, player) and has_pipe_up(state, player)) or state.has("Macro Zone 3 Midway Bell", player),) diff --git a/worlds/marioland2/rom.py b/worlds/marioland2/rom.py index c031dfae1338..2a19a9cb8cad 100644 --- a/worlds/marioland2/rom.py +++ b/worlds/marioland2/rom.py @@ -1,7 +1,6 @@ import hashlib import os import pkgutil -import bsdiff4 import Utils From 5519685f6bd451bf8d0f69453ef8e98aa17f4a1e Mon Sep 17 00:00:00 2001 From: alchav Date: Tue, 10 Sep 2024 10:25:29 -0400 Subject: [PATCH 106/113] increase number of filler coin items --- worlds/marioland2/items.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/items.py b/worlds/marioland2/items.py index 5f9ede60cdb3..a3487d0655f8 100644 --- a/worlds/marioland2/items.py +++ b/worlds/marioland2/items.py @@ -70,7 +70,7 @@ "Turtle Zone 3 Midway Bell": ItemClassification.filler, "Mario's Castle Midway Bell": ItemClassification.progression_skip_balancing, "1 Coin": ItemClassification.filler, - **{f"{i} Coins": ItemClassification.filler for i in range(2, 169)} + **{f"{i} Coins": ItemClassification.filler for i in range(2, 200)} } for level in {"Turtle Zone Secret Course", "Macro Zone Secret Course", "Turtle Zone 3", "Scenic Course", From 47a3ee3aada9fe0192ee0434817288067e92c164 Mon Sep 17 00:00:00 2001 From: alchav Date: Tue, 10 Sep 2024 11:08:55 -0400 Subject: [PATCH 107/113] Adjust Mario Zone 3 auto scroll max --- worlds/marioland2/locations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/locations.py b/worlds/marioland2/locations.py index 1c3ee9897bfa..4fe3a48e092b 100644 --- a/worlds/marioland2/locations.py +++ b/worlds/marioland2/locations.py @@ -491,7 +491,7 @@ "Pumpkin Zone 4": 45, "Pumpkin Zone Secret Course 1": 172, "Mario Zone 1": 68, - "Mario Zone 3": 47, + "Mario Zone 3": 29, "Mario Zone 4": 63, "Turtle Zone 1": 66, "Turtle Zone 2": 8, From 34676d24581077fcf8878b3e04309b24331ff18d Mon Sep 17 00:00:00 2001 From: alchav Date: Tue, 10 Sep 2024 12:02:50 -0400 Subject: [PATCH 108/113] ItemsAccessibility --- worlds/marioland2/options.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/worlds/marioland2/options.py b/worlds/marioland2/options.py index c736b700a688..cad4d60cf2ce 100644 --- a/worlds/marioland2/options.py +++ b/worlds/marioland2/options.py @@ -1,4 +1,4 @@ -from Options import Toggle, Choice, NamedRange, Range, PerGameCommonOptions +from Options import Toggle, Choice, NamedRange, Range, PerGameCommonOptions, ItemsAccessibility from dataclasses import dataclass @@ -179,6 +179,7 @@ class EnergyLink(Toggle): @dataclass class SML2Options(PerGameCommonOptions): + accessibility: ItemsAccessibility shuffle_golden_coins: ShuffleGoldenCoins required_golden_coins: GoldenCoinsRequired mario_coin_fragment_percentage: MarioCoinFragmentPercentage From bafe770d76d32ad6e4662896e77a7ea5dbcd5fb2 Mon Sep 17 00:00:00 2001 From: alchav Date: Tue, 10 Sep 2024 12:04:37 -0400 Subject: [PATCH 109/113] Corrections to Mario Zone 4 logic and Pumpkin Zone 1 auto scroll max --- worlds/marioland2/__init__.py | 7 ++++++- worlds/marioland2/locations.py | 4 ++-- worlds/marioland2/logic.py | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index d287c57803a5..28fef8cfb67e 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -185,7 +185,12 @@ def create_regions(self): 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): - self.max_coin_locations[level] = min(auto_scroll_max[level], self.max_coin_locations[level]) + 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]) diff --git a/worlds/marioland2/locations.py b/worlds/marioland2/locations.py index 4fe3a48e092b..373e048b91e0 100644 --- a/worlds/marioland2/locations.py +++ b/worlds/marioland2/locations.py @@ -485,14 +485,14 @@ "Macro Zone 2": 27, "Macro Zone 3": 63, "Macro Zone 4": 59, - "Pumpkin Zone 1": 12, + "Pumpkin Zone 1": (0, 12), "Pumpkin Zone 2": 23, "Pumpkin Zone 3": 50, "Pumpkin Zone 4": 45, "Pumpkin Zone Secret Course 1": 172, "Mario Zone 1": 68, "Mario Zone 3": 29, - "Mario Zone 4": 63, + "Mario Zone 4": 60, "Turtle Zone 1": 66, "Turtle Zone 2": 8, } diff --git a/worlds/marioland2/logic.py b/worlds/marioland2/logic.py index 0ebdcf521cf8..4cf3630ca26d 100644 --- a/worlds/marioland2/logic.py +++ b/worlds/marioland2/logic.py @@ -300,7 +300,7 @@ def mario_zone_4_boss(state, player): def mario_zone_4_coins(state, player, coins): - return coins <= 63 or not is_auto_scroll(state, player, "Mario Zone 4") + return coins <= 60 or not is_auto_scroll(state, player, "Mario Zone 4") def not_blocked_by_sharks(state, player): From d1f136b9ead4a9a32cd396a7b59474859bcb8d84 Mon Sep 17 00:00:00 2001 From: alchav Date: Mon, 16 Sep 2024 16:07:16 -0400 Subject: [PATCH 110/113] Pumpkin Zone 1 unbeatable with Midway Bells unshuffled --- worlds/marioland2/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/worlds/marioland2/__init__.py b/worlds/marioland2/__init__.py index 28fef8cfb67e..6b748e7383c2 100644 --- a/worlds/marioland2/__init__.py +++ b/worlds/marioland2/__init__.py @@ -113,6 +113,8 @@ def generate_early(self): 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"): From 74c6e9fce28c7c40de140759c12a14832b8013da Mon Sep 17 00:00:00 2001 From: alchav Date: Thu, 5 Dec 2024 14:27:40 -0500 Subject: [PATCH 111/113] SML2: whole lotta coin items --- worlds/marioland2/items.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/worlds/marioland2/items.py b/worlds/marioland2/items.py index a3487d0655f8..a50a20b7ca86 100644 --- a/worlds/marioland2/items.py +++ b/worlds/marioland2/items.py @@ -1,5 +1,6 @@ from BaseClasses import ItemClassification from .locations import level_name_to_id +from .options import CoinsanityChecks items = { "Space Zone Progression": ItemClassification.progression, @@ -70,7 +71,7 @@ "Turtle Zone 3 Midway Bell": ItemClassification.filler, "Mario's Castle Midway Bell": ItemClassification.progression_skip_balancing, "1 Coin": ItemClassification.filler, - **{f"{i} Coins": ItemClassification.filler for i in range(2, 200)} + **{f"{i} Coins": ItemClassification.filler for i in range(2, CoinsanityChecks.range_end)} } for level in {"Turtle Zone Secret Course", "Macro Zone Secret Course", "Turtle Zone 3", "Scenic Course", From 9ca800e711d96a9d0435d14eda3af4509f666348 Mon Sep 17 00:00:00 2001 From: alchav Date: Thu, 5 Dec 2024 14:28:38 -0500 Subject: [PATCH 112/113] Start at ID 1 --- worlds/marioland2/locations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/marioland2/locations.py b/worlds/marioland2/locations.py index 373e048b91e0..02ae1cca9dc5 100644 --- a/worlds/marioland2/locations.py +++ b/worlds/marioland2/locations.py @@ -1,4 +1,4 @@ -START_IDS = 7770000 +START_IDS = 1 locations = { "Mushroom Zone - Normal Exit": {"id": 0x00, "ram_index": 0, "type": "level"}, From 6c2b4e5bb42488553441d227b605b9cd5e46adc1 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:31:08 -0500 Subject: [PATCH 113/113] fix merge conflict mistake --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 188f21532fec..6da719751a5d 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ Currently, the following games are supported: * Kingdom Hearts 1 * Mega Man 2 * Yacht Dice +* Faxanadu * Super Mario Land 2: 6 Golden Coins For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).