mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 04:01:32 -06:00
Super Mario Land 2: Implement New Game (#2730)
Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> Co-authored-by: alchav <alchav@jalchavware.com>
This commit is contained in:
@@ -81,6 +81,7 @@ Currently, the following games are supported:
|
||||
* Civilization VI
|
||||
* The Legend of Zelda: The Wind Waker
|
||||
* Jak and Daxter: The Precursor Legacy
|
||||
* 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
|
||||
|
@@ -178,6 +178,9 @@
|
||||
# Super Mario 64
|
||||
/worlds/sm64ex/ @N00byKing
|
||||
|
||||
# Super Mario Land 2: 6 Golden Coins
|
||||
/worlds/marioland2/ @Alchav
|
||||
|
||||
# Super Mario World
|
||||
/worlds/smw/ @PoryGone
|
||||
|
||||
|
21
worlds/marioland2/LICENSE
Normal file
21
worlds/marioland2/LICENSE
Normal file
@@ -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.
|
449
worlds/marioland2/__init__.py
Normal file
449
worlds/marioland2/__init__.py
Normal file
@@ -0,0 +1,449 @@
|
||||
import base64
|
||||
import Utils
|
||||
import settings
|
||||
from copy import deepcopy
|
||||
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
from BaseClasses import Region, Location, Item, ItemClassification, Tutorial
|
||||
|
||||
from . import client
|
||||
from .rom import generate_output, SuperMarioLand2ProcedurePatch
|
||||
from .options import SML2Options
|
||||
from .locations import (locations, location_name_to_id, level_name_to_id, level_id_to_name, START_IDS, coins_coords,
|
||||
auto_scroll_max)
|
||||
from .items import items
|
||||
from .sprites import level_sprites
|
||||
from .sprite_randomizer import randomize_enemies, randomize_platforms
|
||||
from .logic import has_pipe_up, has_pipe_down, has_pipe_left, has_pipe_right, has_level_progression, is_auto_scroll
|
||||
from . import logic
|
||||
|
||||
|
||||
class MarioLand2Settings(settings.Group):
|
||||
class SML2RomFile(settings.UserFilePath):
|
||||
"""File name of the Super Mario Land 2 1.0 ROM"""
|
||||
description = "Super Mario Land 2 - 6 Golden Coins (USA, Europe) 1.0 ROM File"
|
||||
copy_to = "Super Mario Land 2 - 6 Golden Coins (USA, Europe).gb"
|
||||
md5s = [SuperMarioLand2ProcedurePatch.hash]
|
||||
|
||||
rom_file: SML2RomFile = SML2RomFile(SML2RomFile.copy_to)
|
||||
|
||||
|
||||
class MarioLand2WebWorld(WebWorld):
|
||||
setup_en = Tutorial(
|
||||
"Multiworld Setup Guide",
|
||||
"A guide to playing Super Mario Land 2 with Archipelago.",
|
||||
"English",
|
||||
"setup_en.md",
|
||||
"setup/en",
|
||||
["Alchav"]
|
||||
)
|
||||
|
||||
tutorials = [setup_en]
|
||||
|
||||
|
||||
class MarioLand2World(World):
|
||||
"""Super Mario Land 2 is a classic platformer that follows Mario on a quest to reclaim his castle from the
|
||||
villainous Wario. This iconic game features 32 levels, unique power-ups, and introduces Wario as Mario's
|
||||
arch-rival.""" # -ChatGPT
|
||||
|
||||
game = "Super Mario Land 2"
|
||||
|
||||
settings_key = "sml2_options"
|
||||
settings: MarioLand2Settings
|
||||
|
||||
location_name_to_id = location_name_to_id
|
||||
item_name_to_id = {item_name: ID for ID, item_name in enumerate(items, START_IDS)}
|
||||
|
||||
web = MarioLand2WebWorld()
|
||||
|
||||
item_name_groups = {
|
||||
"Level Progression": {
|
||||
item_name for item_name in items if item_name.endswith(("Progression", "Secret", "Secret 1", "Secret 2"))
|
||||
and "Auto Scroll" not in item_name
|
||||
},
|
||||
"Bells": {item_name for item_name in items if "Bell" in item_name},
|
||||
"Golden Coins": {"Mario Coin", "Macro Coin", "Space Coin", "Tree Coin", "Turtle Coin", "Pumpkin Coin"},
|
||||
"Coins": {"1 Coin", *{f"{i} Coins" for i in range(2, 169)}},
|
||||
"Powerups": {"Mushroom", "Fire Flower", "Carrot"},
|
||||
"Difficulties": {"Easy Mode", "Normal Mode"},
|
||||
"Auto Scroll Traps": {item_name for item_name in items
|
||||
if "Auto Scroll" in item_name and "Cancel" not in item_name},
|
||||
"Cancel Auto Scrolls": {item_name for item_name in items if "Cancel Auto Scroll" in item_name},
|
||||
}
|
||||
|
||||
location_name_groups = {
|
||||
"Bosses": {
|
||||
"Tree Zone 5 - Boss", "Space Zone 2 - Boss", "Macro Zone 4 - Boss",
|
||||
"Pumpkin Zone 4 - Boss", "Mario Zone 4 - Boss", "Turtle Zone 3 - Boss"
|
||||
},
|
||||
"Normal Exits": {location for location in locations if locations[location]["type"] == "level"},
|
||||
"Secret Exits": {location for location in locations if locations[location]["type"] == "secret"},
|
||||
"Bells": {location for location in locations if locations[location]["type"] == "bell"},
|
||||
"Coins": {location for location in location_name_to_id if "Coin" in location}
|
||||
}
|
||||
|
||||
options_dataclass = SML2Options
|
||||
options: SML2Options
|
||||
|
||||
generate_output = generate_output
|
||||
|
||||
def __init__(self, world, player: int):
|
||||
super().__init__(world, player)
|
||||
self.auto_scroll_levels = []
|
||||
self.num_coin_locations = []
|
||||
self.max_coin_locations = {}
|
||||
self.sprite_data = {}
|
||||
self.coin_fragments_required = 0
|
||||
|
||||
def generate_early(self):
|
||||
self.sprite_data = deepcopy(level_sprites)
|
||||
if self.options.randomize_enemies:
|
||||
randomize_enemies(self.sprite_data, self.random)
|
||||
if self.options.randomize_platforms:
|
||||
randomize_platforms(self.sprite_data, self.random)
|
||||
|
||||
if self.options.marios_castle_midway_bell:
|
||||
self.sprite_data["Mario's Castle"][35]["sprite"] = "Midway Bell"
|
||||
|
||||
if self.options.auto_scroll_chances == "vanilla":
|
||||
self.auto_scroll_levels = [int(i in [19, 25, 30]) for i in range(32)]
|
||||
else:
|
||||
self.auto_scroll_levels = [int(self.random.randint(1, 100) <= self.options.auto_scroll_chances)
|
||||
for _ in range(32)]
|
||||
|
||||
self.auto_scroll_levels[level_name_to_id["Mario's Castle"]] = 0
|
||||
unbeatable_scroll_levels = ["Tree Zone 3", "Macro Zone 2", "Space Zone 1", "Turtle Zone 2", "Pumpkin Zone 2"]
|
||||
if not self.options.shuffle_midway_bells:
|
||||
unbeatable_scroll_levels.append("Pumpkin Zone 1")
|
||||
for level, i in enumerate(self.auto_scroll_levels):
|
||||
if i == 1:
|
||||
if self.options.auto_scroll_mode in ("global_cancel_item", "level_cancel_items"):
|
||||
self.auto_scroll_levels[level] = 2
|
||||
elif self.options.auto_scroll_mode == "chaos":
|
||||
if (self.options.accessibility == "full"
|
||||
and level_id_to_name[level] in unbeatable_scroll_levels):
|
||||
self.auto_scroll_levels[level] = 2
|
||||
else:
|
||||
self.auto_scroll_levels[level] = self.random.randint(1, 3)
|
||||
elif (self.options.accessibility == "full"
|
||||
and level_id_to_name[level] in unbeatable_scroll_levels):
|
||||
self.auto_scroll_levels[level] = 0
|
||||
if self.auto_scroll_levels[level] == 1 and "trap" in self.options.auto_scroll_mode.current_key:
|
||||
self.auto_scroll_levels[level] = 3
|
||||
|
||||
def create_regions(self):
|
||||
menu_region = Region("Menu", self.player, self.multiworld)
|
||||
self.multiworld.regions.append(menu_region)
|
||||
created_regions = []
|
||||
for location_name, data in locations.items():
|
||||
region_name = location_name.split(" -")[0]
|
||||
if region_name in created_regions:
|
||||
region = self.multiworld.get_region(region_name, self.player)
|
||||
else:
|
||||
region = Region(region_name, self.player, self.multiworld)
|
||||
if region_name == "Tree Zone Secret Course":
|
||||
region_to_connect = self.multiworld.get_region("Tree Zone 2", self.player)
|
||||
elif region_name == "Space Zone Secret Course":
|
||||
region_to_connect = self.multiworld.get_region("Space Zone 1", self.player)
|
||||
elif region_name == "Macro Zone Secret Course":
|
||||
region_to_connect = self.multiworld.get_region("Macro Zone 1", self.player)
|
||||
elif region_name == "Pumpkin Zone Secret Course 1":
|
||||
region_to_connect = self.multiworld.get_region("Pumpkin Zone 2", self.player)
|
||||
elif region_name == "Pumpkin Zone Secret Course 2":
|
||||
region_to_connect = self.multiworld.get_region("Pumpkin Zone 3", self.player)
|
||||
elif region_name == "Turtle Zone Secret Course":
|
||||
region_to_connect = self.multiworld.get_region("Turtle Zone 2", self.player)
|
||||
elif region_name.split(" ")[-1].isdigit() and int(region_name.split(" ")[-1]) > 1:
|
||||
region_to_connect = self.multiworld.get_region(" ".join(region_name.split(" ")[:2])
|
||||
+ f" {int(region_name.split(' ')[2]) - 1}",
|
||||
self.player)
|
||||
else:
|
||||
region_to_connect = menu_region
|
||||
region_to_connect.connect(region)
|
||||
self.multiworld.regions.append(region)
|
||||
created_regions.append(region_name)
|
||||
|
||||
if location_name == "Mario's Castle - Midway Bell" and not self.options.marios_castle_midway_bell:
|
||||
continue
|
||||
region.locations.append(MarioLand2Location(self.player, location_name,
|
||||
self.location_name_to_id[location_name], region))
|
||||
self.multiworld.get_region("Macro Zone Secret Course", self.player).connect(
|
||||
self.multiworld.get_region("Macro Zone 4", self.player))
|
||||
self.multiworld.get_region("Macro Zone 4", self.player).connect(
|
||||
self.multiworld.get_region("Macro Zone Secret Course", self.player))
|
||||
|
||||
castle = self.multiworld.get_region("Mario's Castle", self.player)
|
||||
wario = MarioLand2Location(self.player, "Mario's Castle - Wario", parent=castle)
|
||||
castle.locations.append(wario)
|
||||
wario.place_locked_item(MarioLand2Item("Wario Defeated", ItemClassification.progression, None, self.player))
|
||||
|
||||
if self.options.coinsanity:
|
||||
coinsanity_checks = self.options.coinsanity_checks.value
|
||||
self.num_coin_locations = [[region, 1] for region in created_regions if region != "Mario's Castle"]
|
||||
self.max_coin_locations = {region: len(coins_coords[region]) for region in created_regions
|
||||
if region != "Mario's Castle"}
|
||||
if self.options.accessibility == "full" or self.options.auto_scroll_mode == "always":
|
||||
for level in self.max_coin_locations:
|
||||
if level in auto_scroll_max and self.auto_scroll_levels[level_name_to_id[level]] in (1, 3):
|
||||
if isinstance(auto_scroll_max[level], tuple):
|
||||
self.max_coin_locations[level] = min(
|
||||
auto_scroll_max[level][int(self.options.shuffle_midway_bells.value)],
|
||||
self.max_coin_locations[level])
|
||||
else:
|
||||
self.max_coin_locations[level] = min(auto_scroll_max[level], self.max_coin_locations[level])
|
||||
coinsanity_checks = min(sum(self.max_coin_locations.values()), coinsanity_checks)
|
||||
for i in range(coinsanity_checks - 31):
|
||||
self.num_coin_locations.sort(key=lambda region: self.max_coin_locations[region[0]] / region[1])
|
||||
self.num_coin_locations[-1][1] += 1
|
||||
coin_locations = []
|
||||
for level, coins in self.num_coin_locations:
|
||||
if self.max_coin_locations[level]:
|
||||
coin_thresholds = self.random.sample(range(1, self.max_coin_locations[level] + 1), coins)
|
||||
coin_locations += [f"{level} - {i} Coin{'s' if i > 1 else ''}" for i in coin_thresholds]
|
||||
for location_name in coin_locations:
|
||||
region = self.multiworld.get_region(location_name.split(" -")[0], self.player)
|
||||
region.locations.append(MarioLand2Location(self.player, location_name,
|
||||
self.location_name_to_id[location_name], parent=region))
|
||||
|
||||
def set_rules(self):
|
||||
entrance_rules = {
|
||||
"Menu -> Space Zone 1": lambda state: state.has("Hippo Bubble", self.player)
|
||||
or (state.has("Carrot", self.player)
|
||||
and not is_auto_scroll(state, self.player, "Hippo Zone")),
|
||||
"Space Zone 1 -> Space Zone Secret Course": lambda state: state.has("Space Zone Secret", self.player),
|
||||
"Space Zone 1 -> Space Zone 2": lambda state: has_level_progression(state, "Space Zone Progression", self.player),
|
||||
"Tree Zone 1 -> Tree Zone 2": lambda state: has_level_progression(state, "Tree Zone Progression", self.player),
|
||||
"Tree Zone 2 -> Tree Zone Secret Course": lambda state: state.has("Tree Zone Secret", self.player),
|
||||
"Tree Zone 2 -> Tree Zone 3": lambda state: has_level_progression(state, "Tree Zone Progression", self.player, 2),
|
||||
"Tree Zone 4 -> Tree Zone 5": lambda state: has_level_progression(state, "Tree Zone Progression", self.player, 3),
|
||||
"Macro Zone 1 -> Macro Zone Secret Course": lambda state: state.has("Macro Zone Secret 1", self.player),
|
||||
"Macro Zone Secret Course -> Macro Zone 4": lambda state: state.has("Macro Zone Secret 2", self.player),
|
||||
"Macro Zone 1 -> Macro Zone 2": lambda state: has_level_progression(state, "Macro Zone Progression", self.player),
|
||||
"Macro Zone 2 -> Macro Zone 3": lambda state: has_level_progression(state, "Macro Zone Progression", self.player, 2),
|
||||
"Macro Zone 3 -> Macro Zone 4": lambda state: has_level_progression(state, "Macro Zone Progression", self.player, 3),
|
||||
"Macro Zone 4 -> Macro Zone Secret Course": lambda state: state.has("Macro Zone Secret 2", self.player),
|
||||
"Pumpkin Zone 1 -> Pumpkin Zone 2": lambda state: has_level_progression(state, "Pumpkin Zone Progression", self.player),
|
||||
"Pumpkin Zone 2 -> Pumpkin Zone Secret Course 1": lambda state: state.has("Pumpkin Zone Secret 1", self.player),
|
||||
"Pumpkin Zone 2 -> Pumpkin Zone 3": lambda state: has_level_progression(state, "Pumpkin Zone Progression", self.player, 2),
|
||||
"Pumpkin Zone 3 -> Pumpkin Zone Secret Course 2": lambda state: state.has("Pumpkin Zone Secret 2", self.player),
|
||||
"Pumpkin Zone 3 -> Pumpkin Zone 4": lambda state: has_level_progression(state, "Pumpkin Zone Progression", self.player, 3),
|
||||
"Mario Zone 1 -> Mario Zone 2": lambda state: has_level_progression(state, "Mario Zone Progression", self.player),
|
||||
"Mario Zone 2 -> Mario Zone 3": lambda state: has_level_progression(state, "Mario Zone Progression", self.player, 2),
|
||||
"Mario Zone 3 -> Mario Zone 4": lambda state: has_level_progression(state, "Mario Zone Progression", self.player, 3),
|
||||
"Turtle Zone 1 -> Turtle Zone 2": lambda state: has_level_progression(state, "Turtle Zone Progression", self.player),
|
||||
"Turtle Zone 2 -> Turtle Zone Secret Course": lambda state: state.has("Turtle Zone Secret", self.player),
|
||||
"Turtle Zone 2 -> Turtle Zone 3": lambda state: has_level_progression(state, "Turtle Zone Progression", self.player, 2),
|
||||
}
|
||||
|
||||
if self.options.shuffle_golden_coins == "mario_coin_fragment_hunt":
|
||||
# Require the other coins just to ensure they are being added to start inventory properly,
|
||||
# and so they show up in Playthrough as required
|
||||
entrance_rules["Menu -> Mario's Castle"] = lambda state: (state.has_all(
|
||||
["Tree Coin", "Space Coin", "Macro Coin", "Pumpkin Coin", "Turtle Coin"], self.player)
|
||||
and state.has("Mario Coin Fragment", self.player, self.coin_fragments_required))
|
||||
else:
|
||||
entrance_rules["Menu -> Mario's Castle"] = lambda state: state.has_from_list_unique([
|
||||
"Tree Coin", "Space Coin", "Macro Coin", "Pumpkin Coin", "Mario Coin", "Turtle Coin"
|
||||
], self.player, self.options.required_golden_coins)
|
||||
|
||||
|
||||
for entrance, rule in entrance_rules.items():
|
||||
self.multiworld.get_entrance(entrance, self.player).access_rule = rule
|
||||
|
||||
for location in self.multiworld.get_locations(self.player):
|
||||
if location.name.endswith(("Coins", "Coin")):
|
||||
rule = getattr(logic, location.parent_region.name.lower().replace(" ", "_") + "_coins", None)
|
||||
if rule:
|
||||
coins = int(location.name.split(" ")[-2])
|
||||
location.access_rule = lambda state, coin_rule=rule, num_coins=coins: \
|
||||
coin_rule(state, self.player, num_coins)
|
||||
else:
|
||||
rule = getattr(logic, location.name.lower().replace(
|
||||
" - ", "_").replace(" ", "_").replace("'", ""), None)
|
||||
if rule:
|
||||
location.access_rule = lambda state, loc_rule=rule: loc_rule(state, self.player)
|
||||
self.multiworld.completion_condition[self.player] = lambda state: state.has("Wario Defeated", self.player)
|
||||
|
||||
def create_items(self):
|
||||
item_counts = {
|
||||
"Space Zone Progression": 1,
|
||||
"Space Zone Secret": 1,
|
||||
"Tree Zone Progression": 3,
|
||||
"Tree Zone Secret": 1,
|
||||
"Macro Zone Progression": 3,
|
||||
"Macro Zone Secret 1": 1,
|
||||
"Macro Zone Secret 2": 1,
|
||||
"Pumpkin Zone Progression": 3,
|
||||
"Pumpkin Zone Secret 1": 1,
|
||||
"Pumpkin Zone Secret 2": 1,
|
||||
"Mario Zone Progression": 3,
|
||||
"Turtle Zone Progression": 2,
|
||||
"Turtle Zone Secret": 1,
|
||||
"Mushroom": 1,
|
||||
"Fire Flower": 1,
|
||||
"Carrot": 1,
|
||||
"Space Physics": 1,
|
||||
"Hippo Bubble": 1,
|
||||
"Water Physics": 1,
|
||||
"Super Star Duration Increase": 2,
|
||||
"Mario Coin Fragment": 0,
|
||||
}
|
||||
|
||||
if self.options.shuffle_golden_coins == "mario_coin_fragment_hunt":
|
||||
# There are 5 Zone Progression items that can be condensed.
|
||||
item_counts["Mario Coin Fragment"] = 1 + ((5 * self.options.mario_coin_fragment_percentage) // 100)
|
||||
|
||||
if self.options.coinsanity:
|
||||
coin_count = sum([level[1] for level in self.num_coin_locations])
|
||||
max_coins = sum(self.max_coin_locations.values())
|
||||
if self.options.shuffle_golden_coins == "mario_coin_fragment_hunt":
|
||||
removed_coins = (coin_count * self.options.mario_coin_fragment_percentage) // 100
|
||||
coin_count -= removed_coins
|
||||
item_counts["Mario Coin Fragment"] += removed_coins
|
||||
# Randomly remove some coin items for variety
|
||||
coin_count -= (coin_count // self.random.randint(100, max(100, coin_count)))
|
||||
|
||||
if coin_count:
|
||||
coin_bundle_sizes = [max_coins // coin_count] * coin_count
|
||||
remainder = max_coins - sum(coin_bundle_sizes)
|
||||
for i in range(remainder):
|
||||
coin_bundle_sizes[i] += 1
|
||||
for a, b in zip(range(1, len(coin_bundle_sizes), 2), range(2, len(coin_bundle_sizes), 2)):
|
||||
split = self.random.randint(1, coin_bundle_sizes[a] + coin_bundle_sizes[b] - 1)
|
||||
coin_bundle_sizes[a], coin_bundle_sizes[b] = split, coin_bundle_sizes[a] + coin_bundle_sizes[b] - split
|
||||
for coin_bundle_size in coin_bundle_sizes:
|
||||
item_name = f"{coin_bundle_size} Coin{'s' if coin_bundle_size > 1 else ''}"
|
||||
if item_name in item_counts:
|
||||
item_counts[item_name] += 1
|
||||
else:
|
||||
item_counts[item_name] = 1
|
||||
|
||||
if self.options.shuffle_golden_coins == "shuffle":
|
||||
for item in self.item_name_groups["Golden Coins"]:
|
||||
item_counts[item] = 1
|
||||
elif self.options.shuffle_golden_coins == "mario_coin_fragment_hunt":
|
||||
for item in ("Tree Coin", "Space Coin", "Macro Coin", "Pumpkin Coin", "Turtle Coin"):
|
||||
self.multiworld.push_precollected(self.create_item(item))
|
||||
else:
|
||||
for item, location_name in (
|
||||
("Mario Coin", "Mario Zone 4 - Boss"),
|
||||
("Tree Coin", "Tree Zone 5 - Boss"),
|
||||
("Space Coin", "Space Zone 2 - Boss"),
|
||||
("Macro Coin", "Macro Zone 4 - Boss"),
|
||||
("Pumpkin Coin", "Pumpkin Zone 4 - Boss"),
|
||||
("Turtle Coin", "Turtle Zone 3 - Boss")
|
||||
):
|
||||
location = self.multiworld.get_location(location_name, self.player)
|
||||
location.place_locked_item(self.create_item(item))
|
||||
location.address = None
|
||||
location.item.code = None
|
||||
|
||||
if self.options.shuffle_midway_bells:
|
||||
for item in [item for item in items if "Midway Bell" in item]:
|
||||
if item != "Mario's Castle Midway Bell" or self.options.marios_castle_midway_bell:
|
||||
item_counts[item] = 1
|
||||
|
||||
if self.options.difficulty_mode == "easy_to_normal":
|
||||
item_counts["Normal Mode"] = 1
|
||||
elif self.options.difficulty_mode == "normal_to_easy":
|
||||
item_counts["Easy Mode"] = 1
|
||||
|
||||
if self.options.shuffle_pipe_traversal == "single":
|
||||
item_counts["Pipe Traversal"] = 1
|
||||
elif self.options.shuffle_pipe_traversal == "split":
|
||||
item_counts["Pipe Traversal - Right"] = 1
|
||||
item_counts["Pipe Traversal - Left"] = 1
|
||||
item_counts["Pipe Traversal - Up"] = 1
|
||||
item_counts["Pipe Traversal - Down"] = 1
|
||||
else:
|
||||
self.multiworld.push_precollected(self.create_item("Pipe Traversal"))
|
||||
|
||||
if any(self.auto_scroll_levels):
|
||||
if self.options.auto_scroll_mode == "global_trap_item":
|
||||
item_counts["Auto Scroll"] = 1
|
||||
elif self.options.auto_scroll_mode == "global_cancel_item":
|
||||
item_counts["Cancel Auto Scroll"] = 1
|
||||
else:
|
||||
for level, i in enumerate(self.auto_scroll_levels):
|
||||
if i == 3:
|
||||
item_counts[f"Auto Scroll - {level_id_to_name[level]}"] = 1
|
||||
elif i == 2:
|
||||
item_counts[f"Cancel Auto Scroll - {level_id_to_name[level]}"] = 1
|
||||
|
||||
for item in self.multiworld.precollected_items[self.player]:
|
||||
if item.name in item_counts and item_counts[item.name] > 0:
|
||||
item_counts[item.name] -= 1
|
||||
|
||||
location_count = len(self.multiworld.get_unfilled_locations(self.player))
|
||||
items_to_add = location_count - sum(item_counts.values())
|
||||
if items_to_add > 0:
|
||||
mario_coin_frags = 0
|
||||
if self.options.shuffle_golden_coins == "mario_coin_fragment_hunt":
|
||||
mario_coin_frags = (items_to_add * self.options.mario_coin_fragment_percentage) // 100
|
||||
item_counts["Mario Coin Fragment"] += mario_coin_frags
|
||||
item_counts["Super Star Duration Increase"] += items_to_add - mario_coin_frags
|
||||
elif items_to_add < 0:
|
||||
if self.options.coinsanity:
|
||||
for i in range(1, 168):
|
||||
coin_name = f"{i} Coin{'s' if i > 1 else ''}"
|
||||
if coin_name in item_counts:
|
||||
amount_to_remove = min(-items_to_add, item_counts[coin_name])
|
||||
item_counts[coin_name] -= amount_to_remove
|
||||
items_to_add += amount_to_remove
|
||||
if items_to_add >= 0:
|
||||
break
|
||||
|
||||
double_progression_items = ["Tree Zone Progression", "Macro Zone Progression", "Pumpkin Zone Progression",
|
||||
"Mario Zone Progression", "Turtle Zone Progression"]
|
||||
self.random.shuffle(double_progression_items)
|
||||
while sum(item_counts.values()) > location_count:
|
||||
if double_progression_items:
|
||||
double_progression_item = double_progression_items.pop()
|
||||
item_counts[double_progression_item] -= 2
|
||||
item_counts[double_progression_item + " x2"] = 1
|
||||
continue
|
||||
if self.options.auto_scroll_mode in ("level_trap_items", "level_cancel_items",
|
||||
"chaos"):
|
||||
auto_scroll_item = self.random.choice([item for item in item_counts if "Auto Scroll" in item])
|
||||
level = auto_scroll_item.split("- ")[1]
|
||||
self.auto_scroll_levels[level_name_to_id[level]] = 0
|
||||
del item_counts[auto_scroll_item]
|
||||
continue
|
||||
raise Exception(f"Too many items in the item pool for Super Mario Land 2 player {self.player_name}")
|
||||
# item = self.random.choice(list(item_counts))
|
||||
# item_counts[item] -= 1
|
||||
# if item_counts[item] == 0:
|
||||
# del item_counts[item]
|
||||
# self.multiworld.push_precollected(self.create_item(item))
|
||||
|
||||
self.coin_fragments_required = max((item_counts["Mario Coin Fragment"]
|
||||
* self.options.mario_coin_fragments_required_percentage) // 100, 1)
|
||||
|
||||
for item_name, count in item_counts.items():
|
||||
self.multiworld.itempool += [self.create_item(item_name) for _ in range(count)]
|
||||
|
||||
def fill_slot_data(self):
|
||||
return {
|
||||
"energy_link": self.options.energy_link.value
|
||||
}
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return MarioLand2Item(name, items[name], self.item_name_to_id[name], self.player)
|
||||
|
||||
def get_filler_item_name(self):
|
||||
return "1 Coin"
|
||||
|
||||
def modify_multidata(self, multidata: dict):
|
||||
rom_name = bytearray(f'AP{Utils.__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed:11}\0',
|
||||
'utf8')[:21]
|
||||
rom_name.extend([0] * (21 - len(rom_name)))
|
||||
new_name = base64.b64encode(bytes(rom_name)).decode()
|
||||
multidata["connect_names"][new_name] = multidata["connect_names"][self.player_name]
|
||||
|
||||
|
||||
class MarioLand2Location(Location):
|
||||
game = "Super Mario Land 2"
|
||||
|
||||
|
||||
class MarioLand2Item(Item):
|
||||
game = "Super Mario Land 2"
|
BIN
worlds/marioland2/basepatch.bsdiff4
Normal file
BIN
worlds/marioland2/basepatch.bsdiff4
Normal file
Binary file not shown.
250
worlds/marioland2/client.py
Normal file
250
worlds/marioland2/client.py
Normal file
@@ -0,0 +1,250 @@
|
||||
import base64
|
||||
import logging
|
||||
|
||||
from NetUtils import ClientStatus
|
||||
from worlds._bizhawk.client import BizHawkClient
|
||||
from worlds._bizhawk import read, write, guarded_write
|
||||
|
||||
from .rom_addresses import rom_addresses
|
||||
|
||||
logger = logging.getLogger("Client")
|
||||
|
||||
BANK_EXCHANGE_RATE = 20000000000
|
||||
|
||||
overworld_music = (0x05, 0x06, 0x0D, 0x0E, 0x10, 0x12, 0x1B, 0x1C, 0x1E)
|
||||
|
||||
class MarioLand2Client(BizHawkClient):
|
||||
system = ("GB", "SGB")
|
||||
patch_suffix = ".apsml2"
|
||||
game = "Super Mario Land 2"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.locations_array = []
|
||||
self.previous_level = None
|
||||
|
||||
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):
|
||||
from . import START_IDS
|
||||
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_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_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")])
|
||||
|
||||
coins_required = int.from_bytes(options[:2], "big")
|
||||
difficulty_mode = options[2]
|
||||
star_count = int.from_bytes(options[3:5], "big")
|
||||
midway_bells = options[5]
|
||||
energy_link = options[6]
|
||||
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")
|
||||
level_data = list(level_data)
|
||||
lives = bcd_lives.hex()
|
||||
num_items_received = int.from_bytes(num_items_received, "big")
|
||||
if num_items_received == 0xFFFF:
|
||||
num_items_received = 0
|
||||
|
||||
items_received = [list(items.keys())[item.item - START_IDS] for item in ctx.items_received]
|
||||
write_num_items_received = len(items_received).to_bytes(2, "big")
|
||||
|
||||
level_progression = {
|
||||
"Space Zone Progression",
|
||||
"Tree Zone Progression",
|
||||
"Macro Zone Progression",
|
||||
"Pumpkin Zone Progression",
|
||||
"Mario Zone Progression",
|
||||
"Turtle Zone Progression",
|
||||
}
|
||||
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"]
|
||||
|
||||
if coin_mode == 2 and items_received.count("Mario Coin Fragment") >= 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:
|
||||
if items_received.count(data["clear_condition"][0]) >= data["clear_condition"][1]:
|
||||
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"]] & 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)
|
||||
|
||||
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
|
||||
|
||||
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"),
|
||||
(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 "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 "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["Enable_Bubble"], [0xcb, 0xd7] if "Hippo Bubble" 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"),
|
||||
(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"),
|
||||
(0x0262, new_coins, "CartRAM"),
|
||||
]
|
||||
|
||||
if items_received:
|
||||
data_writes.append((0x00F0, write_num_items_received, "CartRAM"))
|
||||
|
||||
if midway_point == 0xFF and (midway_bells or music in overworld_music):
|
||||
# after registering the check for the midway bell, clear the value just for safety.
|
||||
data_writes.append((0x02A0, [0], "CartRAM"))
|
||||
|
||||
for i in range(32):
|
||||
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"),
|
||||
(0x022C, [int.from_bytes(bcd_lives, "big")],
|
||||
"CartRAM"),
|
||||
[0x0262, coins, "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
|
||||
|
||||
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
|
||||
|
||||
def on_package(self, ctx, cmd: str, args: dict):
|
||||
super().on_package(ctx, cmd, args)
|
||||
if cmd == 'Connected':
|
||||
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)}"
|
||||
elif cmd == "Retrieved":
|
||||
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)}"
|
64
worlds/marioland2/docs/en_Super Mario Land 2.md
Normal file
64
worlds/marioland2/docs/en_Super Mario Land 2.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Super Mario Land 2: 6 Golden Coins
|
||||
|
||||
## Where is the options page?
|
||||
|
||||
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?
|
||||
|
||||
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.
|
||||
|
||||
Ringing the Midway Bells in each level that has one will register a location check. If the "Shuffle Midway Bells" option
|
||||
is turned on, then ringing the bell will not grant the checkpoint, and instead you must obtain the Midway Bell item from
|
||||
the item pool to gain the checkpoint for that level. Holding SELECT while loading into a level where you have unlocked
|
||||
the Midway Bell checkpoint will start you at the beginning of the level.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
- 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.
|
||||
|
||||
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,
|
||||
a chosen percentage of which are needed to assemble the Mario Coin. You will start with the other 5 coins.
|
||||
- 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. 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 or web tracker to be
|
||||
sure you're aware 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.
|
75
worlds/marioland2/docs/setup_en.md
Normal file
75
worlds/marioland2/docs/setup_en.md
Normal file
@@ -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 this.
|
||||
|
||||
## 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 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.
|
||||
|
||||
## 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 Options Page](/games/Super%20Mario%20Land%202/player-options)
|
||||
|
||||
## Joining a MultiWorld Game
|
||||
|
||||
### Generating and Patching a Game
|
||||
|
||||
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`
|
||||
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 `<address>:<port>` on the textfield on top and press enter (if the
|
||||
server uses password, type in the bottom textfield `/connect <address>:<port> [password]`)
|
79
worlds/marioland2/items.py
Normal file
79
worlds/marioland2/items.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from BaseClasses import ItemClassification
|
||||
from .locations import level_name_to_id
|
||||
from .options import CoinsanityChecks
|
||||
|
||||
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,
|
||||
"Mario Coin Fragment": 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,
|
||||
"Water Physics": 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,
|
||||
**{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,
|
||||
"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,
|
||||
"Mario's Castle Midway Bell": ItemClassification.progression_skip_balancing,
|
||||
"1 Coin": ItemClassification.filler,
|
||||
**{f"{i} Coins": ItemClassification.filler for i in range(2, CoinsanityChecks.range_end + 1)}
|
||||
}
|
||||
|
||||
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
|
498
worlds/marioland2/locations.py
Normal file
498
worlds/marioland2/locations.py
Normal file
@@ -0,0 +1,498 @@
|
||||
START_IDS = 1
|
||||
|
||||
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"},
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
# 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"
|
||||
}
|
||||
|
||||
level_name_to_id = {name: level_id for level_id, name in level_id_to_name.items()}
|
||||
|
||||
auto_scroll_max = {
|
||||
"Mushroom Zone": 84,
|
||||
"Hippo Zone": 160,
|
||||
"Tree Zone 1": 87,
|
||||
"Tree Zone 2": 68,
|
||||
"Tree Zone 3": 4,
|
||||
"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,
|
||||
"Macro Zone 4": 59,
|
||||
"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": 60,
|
||||
"Turtle Zone 1": 66,
|
||||
"Turtle Zone 2": 8,
|
||||
}
|
608
worlds/marioland2/logic.py
Normal file
608
worlds/marioland2/logic.py
Normal file
@@ -0,0 +1,608 @@
|
||||
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 state.multiworld.worlds[player].auto_scroll_levels[level_id] > 0
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def mushroom_zone_coins(state, player, coins):
|
||||
auto_scroll = is_auto_scroll(state, player, "Mushroom Zone")
|
||||
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).
|
||||
# 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_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)
|
||||
|
||||
|
||||
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
|
||||
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_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
|
||||
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_normal_exit(state, player):
|
||||
return has_pipe_down(state, player) and tree_zone_4_midway_bell(state, player)
|
||||
|
||||
|
||||
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
|
||||
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 += 10
|
||||
if not auto_scroll:
|
||||
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 += 10
|
||||
if has_pipe_up(state, player):
|
||||
reachable_coins += 46
|
||||
elif has_pipe_down(state, player):
|
||||
reachable_coins += 10
|
||||
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
|
||||
# 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 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:
|
||||
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_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_2_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
|
||||
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("Water Physics", 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. I was able to reach 43 while small.
|
||||
if coins <= 40:
|
||||
return True
|
||||
if state.has("Carrot", player):
|
||||
if auto_scroll:
|
||||
return coins <= 172
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def pumpkin_zone_3_secret_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
|
||||
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_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
|
||||
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_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
|
||||
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 = 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 += 22
|
||||
if auto_scroll:
|
||||
reachable_coins -= 3 + reachable_spike_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 <= 60 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_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
|
||||
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:
|
||||
reachable_coins -= 10
|
||||
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 ((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):
|
||||
auto_scroll = is_auto_scroll(state, player, "Turtle Zone 2")
|
||||
reachable_coins = 2
|
||||
if auto_scroll:
|
||||
if state.has("Water Physics", player):
|
||||
reachable_coins += 6
|
||||
else:
|
||||
reachable_coins += 2
|
||||
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(["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("Water Physics", player):
|
||||
reachable_coins += 1
|
||||
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):
|
||||
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_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:
|
||||
reachable_coins = 12
|
||||
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", 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))
|
||||
|
||||
|
||||
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):
|
||||
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
|
||||
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_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
|
||||
if has_pipe_down(state, player):
|
||||
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 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
|
||||
|
||||
|
||||
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:
|
||||
return True
|
||||
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):
|
||||
# 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))
|
||||
or state.has("Macro Zone 3 Midway Bell", player))
|
||||
|
||||
|
||||
def macro_zone_3_midway_bell(state, player):
|
||||
return macro_zone_3_normal_exit(state, player)
|
||||
|
||||
|
||||
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 += 17
|
||||
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
|
||||
if state.has("Macro Zone 3 - Midway Bell", player):
|
||||
reachable_coins = max(reachable_coins, 30)
|
||||
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
|
||||
if auto_scroll:
|
||||
reachable_coins -= 8
|
||||
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))
|
||||
or state.has("Mario's Castle Midway Bell", 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))
|
198
worlds/marioland2/options.py
Normal file
198
worlds/marioland2/options.py
Normal file
@@ -0,0 +1,198 @@
|
||||
from Options import Toggle, Choice, NamedRange, Range, PerGameCommonOptions, ItemsAccessibility
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class ShuffleGoldenCoins(Choice):
|
||||
"""
|
||||
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
|
||||
option_shuffle = 1
|
||||
option_mario_coin_fragment_hunt = 2
|
||||
|
||||
|
||||
class GoldenCoinsRequired(Range):
|
||||
"""
|
||||
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 options will greatly impact the number of replaceable filler items.
|
||||
"""
|
||||
display_name = "Mario Coin Fragment Percentage"
|
||||
range_start = 1
|
||||
range_end = 50
|
||||
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 Midway Bells into the item pool. You can always start at the beginning of a level after obtaining the
|
||||
Midway Bell by holding SELECT while entering the level (until you load into the level).
|
||||
The Midway Bells in levels will trigger location checks whether this option is on or not, but they will only
|
||||
set the checkpoint if this is off, otherwise you must obtain the Midway Bell item from the item pool.
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
display_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 Full, auto-scroll levels may have a lower maximum count, which may lead to this
|
||||
value being limited.
|
||||
"""
|
||||
display_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.
|
||||
"""
|
||||
display_name = "Difficulty Mode"
|
||||
option_normal = 0
|
||||
option_easy = 1
|
||||
option_normal_to_easy = 2
|
||||
option_easy_to_normal = 3
|
||||
default = 0
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
Randomize enemies throughout levels.
|
||||
"""
|
||||
display_name = "Randomize Enemies"
|
||||
|
||||
|
||||
class RandomizePlatforms(Toggle):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
display_name = "Auto Scroll Chance"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
special_range_names = {"vanilla": -1, "none": 0, "all": 100}
|
||||
default = -1
|
||||
|
||||
|
||||
class AutoScrollMode(Choice):
|
||||
"""
|
||||
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.
|
||||
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 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.
|
||||
"""
|
||||
display_name = "Auto Scroll Mode"
|
||||
option_always = 0
|
||||
option_global_trap_item = 1
|
||||
option_level_trap_items = 2
|
||||
option_global_cancel_item = 3
|
||||
option_level_cancel_items = 4
|
||||
option_chaos = 5
|
||||
default = 0
|
||||
|
||||
|
||||
class RandomizeMusic(Toggle):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
display_name = "Energy Link"
|
||||
default = 1
|
||||
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class SML2Options(PerGameCommonOptions):
|
||||
accessibility: ItemsAccessibility
|
||||
shuffle_golden_coins: ShuffleGoldenCoins
|
||||
required_golden_coins: GoldenCoinsRequired
|
||||
mario_coin_fragment_percentage: MarioCoinFragmentPercentage
|
||||
mario_coin_fragments_required_percentage: MarioCoinFragmentsRequiredPercentage
|
||||
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
|
||||
difficulty_mode: DifficultyMode
|
||||
randomize_enemies: RandomizeEnemies
|
||||
randomize_platforms: RandomizePlatforms
|
||||
randomize_music: RandomizeMusic
|
||||
energy_link: EnergyLink
|
146
worlds/marioland2/rom.py
Normal file
146
worlds/marioland2/rom.py
Normal file
@@ -0,0 +1,146 @@
|
||||
import hashlib
|
||||
import os
|
||||
import pkgutil
|
||||
|
||||
import Utils
|
||||
|
||||
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
|
||||
|
||||
|
||||
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):
|
||||
patch.write_bytes(i, track)
|
||||
# levels
|
||||
for i in range(0x5619, 0x5899, 0x14):
|
||||
patch.write_bytes(i, random.choice([0x01, 0x0B, 0x11, 0x13, 0x14, 0x17, 0x1D, 0x1F, 0x28]))
|
||||
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
|
||||
patch = SuperMarioLand2ProcedurePatch(player=self.player, player_name=self.player_name)
|
||||
|
||||
patch.write_file("basepatch.bsdiff4", pkgutil.get_data(__name__, "basepatch.bsdiff4"))
|
||||
random = self.random
|
||||
|
||||
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
|
||||
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 = [((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
|
||||
patch.write_bytes(i, 255)
|
||||
i += 1
|
||||
|
||||
if self.options.randomize_music:
|
||||
randomize_music(patch, random)
|
||||
|
||||
if self.options.shuffle_golden_coins:
|
||||
patch.write_bytes(rom_addresses["Coin_Shuffle"], 0x40)
|
||||
if self.options.shuffle_midway_bells:
|
||||
patch.write_bytes(rom_addresses["Disable_Midway_Bell"], 0xC9)
|
||||
|
||||
if self.options.coinsanity:
|
||||
for section in ("A", "B"):
|
||||
for i in range(0, 30):
|
||||
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)
|
||||
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":
|
||||
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:
|
||||
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.
|
||||
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.
|
||||
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)))
|
||||
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 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()
|
||||
with open(file_name, "rb") as file:
|
||||
base_rom_bytes = bytes(file.read())
|
||||
|
||||
basemd5 = hashlib.md5()
|
||||
basemd5.update(base_rom_bytes)
|
||||
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
|
||||
|
||||
|
||||
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
|
39
worlds/marioland2/rom_addresses.py
Normal file
39
worlds/marioland2/rom_addresses.py
Normal file
@@ -0,0 +1,39 @@
|
||||
rom_addresses = {
|
||||
"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,
|
||||
"Coinsanity_B": 0x1d86,
|
||||
"Auto_Scroll_Levels": 0x1f71,
|
||||
"Starting_Lives": 0x2920,
|
||||
"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,
|
||||
"Enable_Bubble": 0x34e5,
|
||||
"Coinsanity_A": 0x591f,
|
||||
"Coin_Shuffle": 0x304ce,
|
||||
"Required_Golden_Coins": 0x306e9,
|
||||
"Disable_Midway_Bell": 0x3ef1e,
|
||||
"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,
|
||||
"Coins_Required": 0x80139,
|
||||
"Difficulty_Mode": 0x8013b,
|
||||
"Star_Count": 0x8013c,
|
||||
"Midway_Bells": 0x8013e,
|
||||
"Energy_Link": 0x8013f,
|
||||
"Coin_Mode": 0x80140,
|
||||
"Auto_Scroll_Levels_B": 0x80141,
|
||||
}
|
131
worlds/marioland2/sprite_randomizer.py
Normal file
131
worlds/marioland2/sprite_randomizer.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# 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():
|
||||
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 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")
|
||||
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")
|
||||
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)")
|
||||
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"):
|
||||
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):
|
||||
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)
|
1016
worlds/marioland2/sprites.py
Normal file
1016
worlds/marioland2/sprites.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user