Merge branch 'main' into rework_accessibility

# Conflicts:
#	worlds/alttp/Rules.py
#	worlds/messenger/options.py
This commit is contained in:
alwaysintreble
2024-03-06 17:23:53 -06:00
178 changed files with 13247 additions and 2156 deletions

1
.github/labeler.yml vendored
View File

@@ -27,4 +27,5 @@
- '!WebHostLib/**'
- any-glob-to-any-file: # exceptions to the above rules of "stuff that isn't core"
- 'worlds/generic/**/*.py'
- 'worlds/*.py'
- 'CommonClient.py'

65
.github/workflows/scan-build.yml vendored Normal file
View File

@@ -0,0 +1,65 @@
name: Native Code Static Analysis
on:
push:
paths:
- '**.c'
- '**.cc'
- '**.cpp'
- '**.cxx'
- '**.h'
- '**.hh'
- '**.hpp'
- '**.pyx'
- 'setup.py'
- 'requirements.txt'
- '.github/workflows/scan-build.yml'
pull_request:
paths:
- '**.c'
- '**.cc'
- '**.cpp'
- '**.cxx'
- '**.h'
- '**.hh'
- '**.hpp'
- '**.pyx'
- 'setup.py'
- 'requirements.txt'
- '.github/workflows/scan-build.yml'
jobs:
scan-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install newer Clang
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x ./llvm.sh
sudo ./llvm.sh 17
- name: Install scan-build command
run: |
sudo apt install clang-tools-17
- name: Get a recent python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m venv venv
source venv/bin/activate
python -m pip install --upgrade pip -r requirements.txt
- name: scan-build
run: |
source venv/bin/activate
scan-build-17 --status-bugs -o scan-build-reports -disable-checker deadcode.DeadStores python setup.py build -y
- name: Store report
if: failure()
uses: actions/upload-artifact@v4
with:
name: scan-build-reports
path: scan-build-reports

View File

@@ -110,10 +110,14 @@ class MultiWorld():
return self
def append(self, region: Region):
assert region.name not in self.region_cache[region.player], \
f"{region.name} already exists in region cache."
self.region_cache[region.player][region.name] = region
def extend(self, regions: Iterable[Region]):
for region in regions:
assert region.name not in self.region_cache[region.player], \
f"{region.name} already exists in region cache."
self.region_cache[region.player][region.name] = region
def add_group(self, new_id: int):
@@ -714,14 +718,23 @@ class CollectionState():
assert isinstance(player, int), "can_reach: player is required if spot is str"
# try to resolve a name
if resolution_hint == 'Location':
spot = self.multiworld.get_location(spot, player)
return self.can_reach_location(spot, player)
elif resolution_hint == 'Entrance':
spot = self.multiworld.get_entrance(spot, player)
return self.can_reach_entrance(spot, player)
else:
# default to Region
spot = self.multiworld.get_region(spot, player)
return self.can_reach_region(spot, player)
return spot.can_reach(self)
def can_reach_location(self, spot: str, player: int) -> bool:
return self.multiworld.get_location(spot, player).can_reach(self)
def can_reach_entrance(self, spot: str, player: int) -> bool:
return self.multiworld.get_entrance(spot, player).can_reach(self)
def can_reach_region(self, spot: str, player: int) -> bool:
return self.multiworld.get_region(spot, player).can_reach(self)
def sweep_for_events(self, key_only: bool = False, locations: Optional[Iterable[Location]] = None) -> None:
if locations is None:
locations = self.multiworld.get_filled_locations()
@@ -878,6 +891,8 @@ class Region:
del(self.region_manager.location_cache[location.player][location.name])
def insert(self, index: int, value: Location) -> None:
assert value.name not in self.region_manager.location_cache[value.player], \
f"{value.name} already exists in the location cache."
self._list.insert(index, value)
self.region_manager.location_cache[value.player][value.name] = value
@@ -888,6 +903,8 @@ class Region:
del(self.region_manager.entrance_cache[entrance.player][entrance.name])
def insert(self, index: int, value: Entrance) -> None:
assert value.name not in self.region_manager.entrance_cache[value.player], \
f"{value.name} already exists in the entrance cache."
self._list.insert(index, value)
self.region_manager.entrance_cache[value.player][value.name] = value
@@ -1273,12 +1290,12 @@ class Spoiler:
for location in sphere:
state.collect(location.item, True, location)
required_locations -= sphere
collection_spheres.append(sphere)
logging.debug('Calculated final sphere %i, containing %i of %i progress items.', len(collection_spheres),
len(sphere), len(required_locations))
required_locations -= sphere
if not sphere:
raise RuntimeError(f'Not all required items reachable. Unreachable locations: {required_locations}')

View File

@@ -302,7 +302,9 @@ def handle_name(name: str, player: int, name_counter: Counter):
NUMBER=(number if number > 1 else ''),
player=player,
PLAYER=(player if player > 1 else '')))
new_name = new_name.strip()[:16]
# Run .strip twice for edge case where after the initial .slice new_name has a leading whitespace.
# Could cause issues for some clients that cannot handle the additional whitespace.
new_name = new_name.strip()[:16].strip()
if new_name == "Archipelago":
raise Exception(f"You cannot name yourself \"{new_name}\"")
return new_name
@@ -462,20 +464,18 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
handle_option(ret, game_weights, option_key, option, plando_options)
if PlandoOptions.items in plando_options:
ret.plando_items = game_weights.get("plando_items", [])
if ret.game == "Minecraft" or ret.game == "Ocarina of Time":
# bad hardcoded behavior to make this work for now
ret.plando_connections = []
if PlandoOptions.connections in plando_options:
options = game_weights.get("plando_connections", [])
for placement in options:
if roll_percentage(get_choice("percentage", placement, 100)):
ret.plando_connections.append(PlandoConnection(
get_choice("entrance", placement),
get_choice("exit", placement),
get_choice("direction", placement)
))
elif ret.game == "A Link to the Past":
if ret.game == "A Link to the Past":
roll_alttp_settings(ret, game_weights, plando_options)
if PlandoOptions.connections in plando_options:
ret.plando_connections = []
options = game_weights.get("plando_connections", [])
for placement in options:
if roll_percentage(get_choice("percentage", placement, 100)):
ret.plando_connections.append(PlandoConnection(
get_choice("entrance", placement),
get_choice("exit", placement),
get_choice("direction", placement, "both")
))
return ret
@@ -494,17 +494,6 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
raise Exception(f"No text target \"{at}\" found.")
ret.plando_texts[at] = str(get_choice_legacy("text", placement))
ret.plando_connections = []
if PlandoOptions.connections in plando_options:
options = weights.get("plando_connections", [])
for placement in options:
if roll_percentage(get_choice_legacy("percentage", placement, 100)):
ret.plando_connections.append(PlandoConnection(
get_choice_legacy("entrance", placement),
get_choice_legacy("exit", placement),
get_choice_legacy("direction", placement, "both")
))
ret.sprite_pool = weights.get('sprite_pool', [])
ret.sprite = get_choice_legacy('sprite', weights, "Link")
if 'random_sprite_on_event' in weights:

View File

@@ -161,7 +161,7 @@ def launch(exe, in_terminal=False):
def run_gui():
from kvui import App, ContainerLayout, GridLayout, Button, Label
from kvui import App, ContainerLayout, GridLayout, Button, Label, ScrollBox, Widget
from kivy.uix.image import AsyncImage
from kivy.uix.relativelayout import RelativeLayout
@@ -185,11 +185,16 @@ def run_gui():
self.container = ContainerLayout()
self.grid = GridLayout(cols=2)
self.container.add_widget(self.grid)
self.grid.add_widget(Label(text="General"))
self.grid.add_widget(Label(text="Clients"))
button_layout = self.grid # make buttons fill the window
self.grid.add_widget(Label(text="General", size_hint_y=None, height=40))
self.grid.add_widget(Label(text="Clients", size_hint_y=None, height=40))
tool_layout = ScrollBox()
tool_layout.layout.orientation = "vertical"
self.grid.add_widget(tool_layout)
client_layout = ScrollBox()
client_layout.layout.orientation = "vertical"
self.grid.add_widget(client_layout)
def build_button(component: Component):
def build_button(component: Component) -> Widget:
"""
Builds a button widget for a given component.
@@ -200,31 +205,26 @@ def run_gui():
None. The button is added to the parent grid layout.
"""
button = Button(text=component.display_name)
button = Button(text=component.display_name, size_hint_y=None, height=40)
button.component = component
button.bind(on_release=self.component_action)
if component.icon != "icon":
image = AsyncImage(source=icon_paths[component.icon],
size=(38, 38), size_hint=(None, 1), pos=(5, 0))
box_layout = RelativeLayout()
box_layout = RelativeLayout(size_hint_y=None, height=40)
box_layout.add_widget(button)
box_layout.add_widget(image)
button_layout.add_widget(box_layout)
else:
button_layout.add_widget(button)
return box_layout
return button
for (tool, client) in itertools.zip_longest(itertools.chain(
self._tools.items(), self._miscs.items(), self._adjusters.items()), self._clients.items()):
# column 1
if tool:
build_button(tool[1])
else:
button_layout.add_widget(Label())
tool_layout.layout.add_widget(build_button(tool[1]))
# column 2
if client:
build_button(client[1])
else:
button_layout.add_widget(Label())
client_layout.layout.add_widget(build_button(client[1]))
return self.container

View File

@@ -656,7 +656,8 @@ class Context:
else:
return self.player_names[team, slot]
def notify_hints(self, team: int, hints: typing.List[NetUtils.Hint], only_new: bool = False):
def notify_hints(self, team: int, hints: typing.List[NetUtils.Hint], only_new: bool = False,
recipients: typing.Sequence[int] = None):
"""Send and remember hints."""
if only_new:
hints = [hint for hint in hints if hint not in self.hints[team, hint.finding_player]]
@@ -685,12 +686,13 @@ class Context:
for slot in new_hint_events:
self.on_new_hint(team, slot)
for slot, hint_data in concerns.items():
clients = self.clients[team].get(slot)
if not clients:
continue
client_hints = [datum[1] for datum in sorted(hint_data, key=lambda x: x[0].finding_player == slot)]
for client in clients:
async_start(self.send_msgs(client, client_hints))
if recipients is None or slot in recipients:
clients = self.clients[team].get(slot)
if not clients:
continue
client_hints = [datum[1] for datum in sorted(hint_data, key=lambda x: x[0].finding_player == slot)]
for client in clients:
async_start(self.send_msgs(client, client_hints))
# "events"
@@ -1429,9 +1431,13 @@ class ClientMessageProcessor(CommonCommandProcessor):
hints = {hint.re_check(self.ctx, self.client.team) for hint in
self.ctx.hints[self.client.team, self.client.slot]}
self.ctx.hints[self.client.team, self.client.slot] = hints
self.ctx.notify_hints(self.client.team, list(hints))
self.ctx.notify_hints(self.client.team, list(hints), recipients=(self.client.slot,))
self.output(f"A hint costs {self.ctx.get_hint_cost(self.client.slot)} points. "
f"You have {points_available} points.")
if hints and Utils.version_tuple < (0, 5, 0):
self.output("It was recently changed, so that the above hints are only shown to you. "
"If you meant to alert another player of an above hint, "
"please let them know of the content or to run !hint themselves.")
return True
elif input_text.isnumeric():

View File

@@ -1,19 +1,18 @@
from __future__ import annotations
import abc
import logging
from copy import deepcopy
from dataclasses import dataclass
import functools
import logging
import math
import numbers
import random
import typing
from copy import deepcopy
from dataclasses import dataclass
from schema import And, Optional, Or, Schema
from Utils import get_fuzzy_results
from Utils import get_fuzzy_results, is_iterable_of_str
if typing.TYPE_CHECKING:
from BaseClasses import PlandoOptions
@@ -59,6 +58,7 @@ class AssembleOptions(abc.ABCMeta):
def verify(self, *args, **kwargs) -> None:
for f in verifiers:
f(self, *args, **kwargs)
attrs["verify"] = verify
else:
assert verifiers, "class Option is supposed to implement def verify"
@@ -183,6 +183,7 @@ class FreeText(Option[str]):
class NumericOption(Option[int], numbers.Integral, abc.ABC):
default = 0
# note: some of the `typing.Any`` here is a result of unresolved issue in python standards
# `int` is not a `numbers.Integral` according to the official typestubs
# (even though isinstance(5, numbers.Integral) == True)
@@ -598,7 +599,7 @@ class PlandoBosses(TextChoice, metaclass=BossMeta):
if isinstance(self.value, int):
return
from BaseClasses import PlandoOptions
if not(PlandoOptions.bosses & plando_options):
if not (PlandoOptions.bosses & plando_options):
# plando is disabled but plando options were given so pull the option and change it to an int
option = self.value.split(";")[-1]
self.value = self.options[option]
@@ -727,7 +728,7 @@ class SpecialRange(NamedRange):
"Consider switching to NamedRange, which supports all use-cases of SpecialRange, and more. In "
"NamedRange, range_start specifies the lower end of the regular range, while special values can be "
"placed anywhere (below, inside, or above the regular range).")
return super().__new__(cls, value)
return super().__new__(cls)
@classmethod
def weighted_range(cls, text) -> Range:
@@ -765,7 +766,7 @@ class VerifyKeys(metaclass=FreezeValidKeys):
value: typing.Any
@classmethod
def verify_keys(cls, data: typing.List[str]):
def verify_keys(cls, data: typing.Iterable[str]) -> None:
if cls.valid_keys:
data = set(data)
dataset = set(word.casefold() for word in data) if cls.valid_keys_casefold else set(data)
@@ -843,11 +844,11 @@ class OptionList(Option[typing.List[typing.Any]], VerifyKeys):
# If only unique entries are needed and input order of elements does not matter, OptionSet should be used instead.
# Not a docstring so it doesn't get grabbed by the options system.
default: typing.List[typing.Any] = []
default: typing.Union[typing.List[typing.Any], typing.Tuple[typing.Any, ...]] = ()
supports_weighting = False
def __init__(self, value: typing.List[typing.Any]):
self.value = deepcopy(value)
def __init__(self, value: typing.Iterable[str]):
self.value = list(deepcopy(value))
super(OptionList, self).__init__()
@classmethod
@@ -856,7 +857,7 @@ class OptionList(Option[typing.List[typing.Any]], VerifyKeys):
@classmethod
def from_any(cls, data: typing.Any):
if type(data) == list:
if is_iterable_of_str(data):
cls.verify_keys(data)
return cls(data)
return cls.from_text(str(data))
@@ -882,7 +883,7 @@ class OptionSet(Option[typing.Set[str]], VerifyKeys):
@classmethod
def from_any(cls, data: typing.Any):
if isinstance(data, (list, set, frozenset)):
if is_iterable_of_str(data):
cls.verify_keys(data)
return cls(data)
return cls.from_text(str(data))
@@ -947,7 +948,7 @@ class OptionsMetaProperty(type):
bases: typing.Tuple[type, ...],
attrs: typing.Dict[str, typing.Any]) -> "OptionsMetaProperty":
for attr_type in attrs.values():
assert not isinstance(attr_type, AssembleOptions),\
assert not isinstance(attr_type, AssembleOptions), \
f"Options for {name} should be type hinted on the class, not assigned"
return super().__new__(mcs, name, bases, attrs)
@@ -1125,6 +1126,11 @@ class PerGameCommonOptions(CommonOptions):
item_links: ItemLinks
@dataclass
class DeathLinkMixin:
death_link: DeathLink
def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], generate_hidden: bool = True):
import os

View File

@@ -59,6 +59,8 @@ Currently, the following games are supported:
* Landstalker: The Treasures of King Nole
* Final Fantasy Mystic Quest
* TUNIC
* Kirby's Dream Land 3
* Celeste 64
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

View File

@@ -19,14 +19,13 @@ import warnings
from argparse import Namespace
from settings import Settings, get_settings
from typing import BinaryIO, Coroutine, Optional, Set, Dict, Any, Union
from yaml import load, load_all, dump, SafeLoader
from typing_extensions import TypeGuard
from yaml import load, load_all, dump
try:
from yaml import CLoader as UnsafeLoader
from yaml import CDumper as Dumper
from yaml import CLoader as UnsafeLoader, CSafeLoader as SafeLoader, CDumper as Dumper
except ImportError:
from yaml import Loader as UnsafeLoader
from yaml import Dumper
from yaml import Loader as UnsafeLoader, SafeLoader, Dumper
if typing.TYPE_CHECKING:
import tkinter
@@ -968,3 +967,13 @@ class RepeatableChain:
def __len__(self):
return sum(len(iterable) for iterable in self.iterable)
def is_iterable_of_str(obj: object) -> TypeGuard[typing.Iterable[str]]:
""" but not a `str` (because technically, `str` is `Iterable[str]`) """
if isinstance(obj, str):
return False
if not isinstance(obj, typing.Iterable):
return False
obj_it: typing.Iterable[object] = obj
return all(isinstance(v, str) for v in obj_it)

View File

@@ -48,6 +48,7 @@ cdef struct IndexEntry:
size_t count
@cython.auto_pickle(False)
cdef class LocationStore:
"""Compact store for locations and their items in a MultiServer"""
# The original implementation uses Dict[int, Dict[int, Tuple(int, int, int]]
@@ -78,18 +79,6 @@ cdef class LocationStore:
size += sizeof(self._raw_proxies[0]) * self.sender_index_size
return size
def __cinit__(self, locations_dict: Dict[int, Dict[int, Sequence[int]]]) -> None:
self._mem = None
self._keys = None
self._items = None
self._proxies = None
self._len = 0
self.entries = NULL
self.entry_count = 0
self.sender_index = NULL
self.sender_index_size = 0
self._raw_proxies = NULL
def __init__(self, locations_dict: Dict[int, Dict[int, Sequence[int]]]) -> None:
self._mem = Pool()
cdef object key
@@ -281,6 +270,7 @@ cdef class LocationStore:
entry.location not in checked])
@cython.auto_pickle(False)
@cython.internal # unsafe. disable direct import
cdef class PlayerLocationProxy:
cdef LocationStore _store

View File

@@ -17,10 +17,10 @@
# A. This is a .yaml file. You are allowed to use most characters.
# To test if your yaml is valid or not, you can use this website:
# http://www.yamllint.com/
# You can also verify your Archipelago settings are valid at this site:
# You can also verify that your Archipelago options are valid at this site:
# https://archipelago.gg/check
# Your name in-game. Spaces will be replaced with underscores and there is a 16-character limit.
# Your name in-game, limited to 16 characters.
# {player} will be replaced with the player's slot number.
# {PLAYER} will be replaced with the player's slot number, if that slot number is greater than 1.
# {number} will be replaced with the counter value of the name.

View File

@@ -28,6 +28,9 @@
# Bumper Stickers
/worlds/bumpstik/ @FelicitusNeko
# Celeste 64
/worlds/celeste64/ @PoryGone
# ChecksFinder
/worlds/checksfinder/ @jonloveslegos
@@ -67,6 +70,9 @@
# Hylics 2
/worlds/hylics2/ @TRPG0
# Kirby's Dream Land 3
/worlds/kdl3/ @Silvris
# Kingdom Hearts 2
/worlds/kh2/ @JaredWeakStrike

View File

@@ -17,7 +17,17 @@ It is recommended that automated github actions are turned on in your fork to ha
You can turn them on here:
![Github actions example](./img/github-actions-example.png)
Other than these requests, we tend to judge code on a case by case basis.
* **When reviewing PRs, please leave a message about what was done.**
We don't have full test coverage, so manual testing can help.
For code changes that could affect multiple worlds or that could have changes in unexpected code paths, manual testing
or checking if all code paths are covered by automated tests is desired. The original author may not have been able
to test all possibly affected worlds, or didn't know it would affect another world. In such cases, it is helpful to
state which games or settings were rolled, if any.
Please also tell us if you looked at code, just did functional testing, did both, or did neither.
If testing the PR depends on other PRs, please state what you merged into what for testing.
We cannot determine what "LGTM" means without additional context, so that should not be the norm.
Other than these requests, we tend to judge code on a case-by-case basis.
For contribution to the website, please refer to the [WebHost README](/WebHostLib/README.md).

View File

@@ -327,7 +327,11 @@ Sent to server to inform it of locations that the client has checked. Used to in
| locations | list\[int\] | The ids of the locations checked by the client. May contain any number of checks, even ones sent before; duplicates do not cause issues with the Archipelago server. |
### LocationScouts
Sent to the server to inform it of locations the client has seen, but not checked. Useful in cases in which the item may appear in the game world, such as 'ledge items' in A Link to the Past. The server will always respond with a [LocationInfo](#LocationInfo) packet with the items located in the scouted location.
Sent to the server to retrieve the items that are on a specified list of locations. The server will respond with a [LocationInfo](#LocationInfo) packet containing the items located in the scouted locations.
Fully remote clients without a patch file may use this to "place" items onto their in-game locations, most commonly to display their names or item classifications before/upon pickup.
LocationScouts can also be used to inform the server of locations the client has seen, but not checked. This creates a hint as if the player had run `!hint_location` on a location, but without deducting hint points.
This is useful in cases where an item appears in the game world, such as 'ledge items' in _A Link to the Past_. To do this, set the `create_as_hint` parameter to a non-zero value.
#### Arguments
| Name | Type | Notes |
@@ -341,7 +345,7 @@ Sent to the server to update on the sender's status. Examples include readiness
#### Arguments
| Name | Type | Notes |
| ---- | ---- | ----- |
| status | ClientStatus\[int\] | One of [Client States](#Client-States). Send as int. Follow the link for more information. |
| status | ClientStatus\[int\] | One of [Client States](#ClientStatus). Send as int. Follow the link for more information. |
### Say
Basic chat command which sends text to the server to be distributed to other clients.

View File

@@ -24,8 +24,11 @@ display as `Value1` on the webhost.
(i.e. `alias_value_1 = option_value1`) which will allow users to use either `value_1` or `value1` in their yaml
files, and both will resolve as `value1`. This should be used when changing options around, i.e. changing a Toggle to a
Choice, and defining `alias_true = option_full`.
- All options support `random` as a generic option. `random` chooses from any of the available values for that option,
and is reserved by AP. You can set this as your default value, but you cannot define your own `option_random`.
- All options with a fixed set of possible values (i.e. those which inherit from `Toggle`, `(Text)Choice` or
`(Named/Special)Range`) support `random` as a generic option. `random` chooses from any of the available values for that
option, and is reserved by AP. You can set this as your default value, but you cannot define your own `option_random`.
However, you can override `from_text` and handle `text == "random"` to customize its behavior or
implement it for additional option types.
As an example, suppose we want an option that lets the user start their game with a sword in their inventory, an option
to let the player choose the difficulty, and an option to choose how much health the final boss has. Let's create our

View File

@@ -121,6 +121,10 @@ Path to a single file. Automatically resolves as user_path:
Source folder or AP install path on Windows. ~/Archipelago for the AppImage.
Will open a file browser if the file is missing when in GUI mode.
If the file is used in the world's `generate_output`, make sure to add a `stage_assert_generate` that checks if the
file is available, otherwise generation may fail at the very end.
See also [world api.md](https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/world%20api.md#generation).
#### class method validate(cls, path: str)
Override this and raise ValueError if validation fails.

View File

@@ -170,6 +170,7 @@ could also be progress in a research tree, or even something more abstract like
Each location has a `name` and an `address` (hereafter referred to as an `id`), is placed in a Region, has access rules,
and has a classification. The name needs to be unique within each game and must not be numeric (must contain least 1
letter or symbol). The ID needs to be unique across all games, and is best kept in the same range as the item IDs.
Locations and items can share IDs, so typically a game's locations and items start at the same ID.
World-specific IDs must be in the range 1 to 2<sup>53</sup>-1; IDs ≤ 0 are global and reserved.
@@ -737,8 +738,9 @@ def generate_output(self, output_directory: str) -> None:
If the game client needs to know information about the generated seed, a preferred method of transferring the data
is through the slot data. This is filled with the `fill_slot_data` method of your world by returning
a `Dict[str, Any]`, but, to not waste resources, should be limited to data that is absolutely necessary. Slot data is
sent to your client once it has successfully [connected](network%20protocol.md#connected).
a `dict` with `str` keys that can be serialized with json.
But, to not waste resources, it should be limited to data that is absolutely necessary. Slot data is sent to your client
once it has successfully [connected](network%20protocol.md#connected).
If you need to know information about locations in your world, instead of propagating the slot data, it is preferable
to use [LocationScouts](network%20protocol.md#locationscouts), since that data already exists on the server. The most
common usage of slot data is sending option results that the client needs to be aware of.

View File

@@ -131,6 +131,11 @@ Root: HKCR; Subkey: "{#MyAppName}l2acpatch"; ValueData: "Arc
Root: HKCR; Subkey: "{#MyAppName}l2acpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}l2acpatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: "";
Root: HKCR; Subkey: ".apkdl3"; ValueData: "{#MyAppName}kdl3patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}kdl3patch"; ValueData: "Archipelago Kirby's Dream Land 3 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}kdl3patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}kdl3patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: ".apmc"; ValueData: "{#MyAppName}mcdata"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mcdata"; ValueData: "Archipelago Minecraft Data"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mcdata\DefaultIcon"; ValueData: "{app}\ArchipelagoMinecraftClient.exe,0"; ValueType: string; ValueName: "";

13
kvui.py
View File

@@ -38,11 +38,13 @@ from kivy.clock import Clock
from kivy.factory import Factory
from kivy.properties import BooleanProperty, ObjectProperty
from kivy.metrics import dp
from kivy.effects.scroll import ScrollEffect
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.layout import Layout
from kivy.uix.textinput import TextInput
from kivy.uix.scrollview import ScrollView
from kivy.uix.recycleview import RecycleView
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelItem
from kivy.uix.boxlayout import BoxLayout
@@ -118,6 +120,17 @@ class ServerToolTip(ToolTip):
pass
class ScrollBox(ScrollView):
layout: BoxLayout
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.layout = BoxLayout(size_hint_y=None)
self.layout.bind(minimum_height=self.layout.setter("height"))
self.add_widget(self.layout)
self.effect_cls = ScrollEffect
class HovererableLabel(HoverBehavior, Label):
pass

View File

@@ -11,3 +11,4 @@ certifi>=2023.11.17
cython>=3.0.8
cymem>=2.0.8
orjson>=3.9.10
typing-extensions>=4.7.0

View File

@@ -80,7 +80,6 @@ non_apworlds: set = {
"Super Mario 64",
"VVVVVV",
"Wargroove",
"Zillion",
}
# LogicMixin is broken before 3.10 import revamp

View File

@@ -29,8 +29,8 @@ class TestHelpers(unittest.TestCase):
"event_loc": None,
},
"TestRegion2": {
"loc_1": 321,
"loc_2": 654,
"loc_3": 321,
"loc_4": 654,
}
}

68
test/utils/test_yaml.py Normal file
View File

@@ -0,0 +1,68 @@
# Tests that yaml wrappers in Utils.py do what they should
import unittest
from typing import cast, Any, ClassVar, Dict
from Utils import dump, Dumper # type: ignore[attr-defined]
from Utils import parse_yaml, parse_yamls, unsafe_parse_yaml
class AClass:
def __eq__(self, other: Any) -> bool:
return isinstance(other, self.__class__)
class TestYaml(unittest.TestCase):
safe_data: ClassVar[Dict[str, Any]] = {
"a": [1, 2, 3],
"b": None,
"c": True,
}
unsafe_data: ClassVar[Dict[str, Any]] = {
"a": AClass()
}
@property
def safe_str(self) -> str:
return cast(str, dump(self.safe_data, Dumper=Dumper))
@property
def unsafe_str(self) -> str:
return cast(str, dump(self.unsafe_data, Dumper=Dumper))
def assertIsNonEmptyString(self, string: str) -> None:
self.assertTrue(string)
self.assertIsInstance(string, str)
def test_dump(self) -> None:
self.assertIsNonEmptyString(self.safe_str)
self.assertIsNonEmptyString(self.unsafe_str)
def test_safe_parse(self) -> None:
self.assertEqual(self.safe_data, parse_yaml(self.safe_str))
with self.assertRaises(Exception):
parse_yaml(self.unsafe_str)
with self.assertRaises(Exception):
parse_yaml("1\n---\n2\n")
def test_unsafe_parse(self) -> None:
self.assertEqual(self.safe_data, unsafe_parse_yaml(self.safe_str))
self.assertEqual(self.unsafe_data, unsafe_parse_yaml(self.unsafe_str))
with self.assertRaises(Exception):
unsafe_parse_yaml("1\n---\n2\n")
def test_multi_parse(self) -> None:
self.assertEqual(self.safe_data, next(parse_yamls(self.safe_str)))
with self.assertRaises(Exception):
next(parse_yamls(self.unsafe_str))
self.assertEqual(2, len(list(parse_yamls("1\n---\n2\n"))))
def test_unique_key(self) -> None:
s = """
a: 1
a: 2
"""
with self.assertRaises(Exception):
parse_yaml(s)
with self.assertRaises(Exception):
next(parse_yamls(s))

View File

@@ -10,4 +10,4 @@ class FillType_Drawable:
class Texture:
pass
size: FillType_Vec

View File

@@ -7,15 +7,15 @@ import re
import sys
import time
from dataclasses import make_dataclass
from typing import Any, Callable, ClassVar, Dict, Set, Tuple, FrozenSet, List, Optional, TYPE_CHECKING, TextIO, Type, \
Union
from typing import (Any, Callable, ClassVar, Dict, FrozenSet, List, Mapping,
Optional, Set, TextIO, Tuple, TYPE_CHECKING, Type, Union)
from Options import PerGameCommonOptions
from BaseClasses import CollectionState
if TYPE_CHECKING:
import random
from BaseClasses import MultiWorld, Item, Location, Tutorial
from BaseClasses import MultiWorld, Item, Location, Tutorial, Region, Entrance
from . import GamesPackage
from settings import Group
@@ -365,13 +365,19 @@ class World(metaclass=AutoWorldRegister):
If you need any last-second randomization, use self.random instead."""
pass
def fill_slot_data(self) -> Dict[str, Any]: # json of WebHostLib.models.Slot
"""Fill in the `slot_data` field in the `Connected` network package.
def fill_slot_data(self) -> Mapping[str, Any]: # json of WebHostLib.models.Slot
"""What is returned from this function will be in the `slot_data` field
in the `Connected` network package.
It should be a `dict` with `str` keys, and should be serializable with json.
This is a way the generator can give custom data to the client.
The client will receive this as JSON in the `Connected` response.
The generation does not wait for `generate_output` to complete before calling this.
`threading.Event` can be used if you need to wait for something from `generate_output`."""
# The reason for the `Mapping` type annotation, rather than `dict`
# is so that type checkers won't worry about the mutability of `dict`,
# so you can have more specific typing in your world implementation.
return {}
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]):
@@ -458,6 +464,16 @@ class World(metaclass=AutoWorldRegister):
def create_filler(self) -> "Item":
return self.create_item(self.get_filler_item_name())
# convenience methods
def get_location(self, location_name: str) -> "Location":
return self.multiworld.get_location(location_name, self.player)
def get_entrance(self, entrance_name: str) -> "Entrance":
return self.multiworld.get_entrance(entrance_name, self.player)
def get_region(self, region_name: str) -> "Region":
return self.multiworld.get_region(region_name, self.player)
@classmethod
def get_data_package_data(cls) -> "GamesPackage":
sorted_item_name_groups = {

View File

@@ -41,6 +41,13 @@ class AutoPatchRegister(abc.ABCMeta):
current_patch_version: int = 5
class InvalidDataError(Exception):
"""
Since games can override `read_contents` in APContainer,
this is to report problems in that process.
"""
class APContainer:
"""A zipfile containing at least archipelago.json"""
version: int = current_patch_version
@@ -89,7 +96,15 @@ class APContainer:
with zipfile.ZipFile(zip_file, "r") as zf:
if file:
self.path = zf.filename
self.read_contents(zf)
try:
self.read_contents(zf)
except Exception as e:
message = ""
if len(e.args):
arg0 = e.args[0]
if isinstance(arg0, str):
message = f"{arg0} - "
raise InvalidDataError(f"{message}This might be the incorrect world version for this file") from e
def read_contents(self, opened_zipfile: zipfile.ZipFile) -> None:
with opened_zipfile.open("archipelago.json", "r") as f:

View File

@@ -88,7 +88,7 @@ components: List[Component] = [
# SNI
Component('SNI Client', 'SNIClient',
file_identifier=SuffixIdentifier('.apz3', '.apm3', '.apsoe', '.aplttp', '.apsm', '.apsmz3', '.apdkc3',
'.apsmw', '.apl2ac')),
'.apsmw', '.apl2ac', '.apkdl3')),
Component('Links Awakening DX Client', 'LinksAwakeningClient',
file_identifier=SuffixIdentifier('.apladx')),
Component('LttP Adjuster', 'LttPAdjuster'),

279
worlds/_bizhawk/README.md Normal file
View File

@@ -0,0 +1,279 @@
# BizHawk Client
`BizHawkClient` is an abstract base class for a client that can access the memory of a ROM running in BizHawk. It does
the legwork of connecting Python to a Lua connector script, letting you focus on the loop of checking locations and
making on-the-fly modifications based on updates from the server. It also provides the same experience to users across
multiple games that use it, and was built in response to a growing number of similar but separate bespoke game clients
which are/were largely exclusive to BizHawk anyway.
It's similar to `SNIClient`, but where `SNIClient` is designed to work for specifically SNES games across different
emulators/hardware, `BizHawkClient` is designed to work for specifically BizHawk across the different systems BizHawk
supports.
The idea is that `BizHawkClient` connects to and communicates with a Lua script running in BizHawk. It provides an API
that will call BizHawk functions for you to do things like read and write memory. And on an interval, control will be
handed to a function you write for your game (`game_watcher`) which should interact with the game's memory to check what
locations have been checked, give the player items, detect and send deathlinks, etc...
Table of Contents:
- [Connector Requests](#connector-requests)
- [Requests that depend on other requests](#requests-that-depend-on-other-requests)
- [Implementing a Client](#implementing-a-client)
- [Example](#example)
- [Tips](#tips)
## Connector Requests
Communication with BizHawk is done through `connector_bizhawk_generic.lua`. The client sends requests to the Lua script
via sockets; the Lua script processes the request and sends the corresponding responses.
The Lua script includes its own documentation, but you probably don't need to worry about the specifics. Instead, you'll
be using the functions in `worlds/_bizhawk/__init__.py`. If you do need more control over the specific requests being
sent or their order, you can still use `send_requests` to directly communicate with the connector script.
It's not necessary to use the UI or client context if you only want to interact with the connector script. You can
import and use just `worlds/_bizhawk/__init__.py`, which only depends on default modules.
Here's a list of the included classes and functions. I would highly recommend looking at the actual function signatures
and docstrings to learn more about each function.
```
class ConnectionStatus
class BizHawkContext
class NotConnectedError
class RequestFailedError
class ConnectorError
class SyncError
async def read(ctx, read_list) -> list[bytes]
async def write(ctx, write_list) -> None:
async def guarded_read(ctx, read_list, guard_list) -> (list[bytes] | None)
async def guarded_write(ctx, write_list, guard_list) -> bool
async def lock(ctx) -> None
async def unlock(ctx) -> None
async def get_hash(ctx) -> str
async def get_system(ctx) -> str
async def get_cores(ctx) -> dict[str, str]
async def ping(ctx) -> None
async def display_message(ctx, message: str) -> None
async def set_message_interval(ctx, value: float) -> None
async def connect(ctx) -> bool
def disconnect(ctx) -> None
async def get_script_version(ctx) -> int
async def send_requests(ctx, req_list) -> list[dict[str, Any]]
```
`send_requests` is what actually communicates with the connector, and any functions like `guarded_read` will build the
requests and then call `send_requests` for you. You can call `send_requests` yourself for more direct control, but make
sure to read the docs in `connector_bizhawk_generic.lua`.
A bundle of requests sent by `send_requests` will all be executed on the same frame, and by extension, so will any
helper that calls `send_requests`. For example, if you were to call `read` with 3 items on your `read_list`, all 3
addresses will be read on the same frame and then sent back.
It also means that, by default, the only way to run multiple requests on the same frame is for them to be included in
the same `send_requests` call. As soon as the connector finishes responding to a list of requests, it will advance the
frame before checking for the next batch.
### Requests that depend on other requests
The fact that you have to wait at least a frame to act on any response may raise concerns. For example, Pokemon
Emerald's save data is at a dynamic location in memory; it moves around when you load a new map. There is a static
variable that holds the address of the save data, so we want to read the static variable to get the save address, and
then use that address in a `write` to send the player an item. But between the `read` that tells us the address of the
save data and the `write` to save data itself, an arbitrary number of frames have been executed, and the player may have
loaded a new map, meaning we've written data to who knows where.
There are two solutions to this problem.
1. Use `guarded_write` instead of `write`. We can include a guard against the address changing, and the script will only
perform the write if the data in memory matches what's in the guard. In the below example, `write_result` will be `True`
if the guard validated and the data was written, and `False` if the guard failed to validate.
```py
# Get the address of the save data
read_result: bytes = (await _bizhawk.read(ctx, [(0x3001111, 4, "System Bus")]))[0]
save_data_address = int.from_bytes(read_result, "little")
# Write to `save_data_address` if it hasn't changed
write_result: bool = await _bizhawk.guarded_write(
ctx,
[(save_data_address, [0xAA, 0xBB], "System Bus")],
[(0x3001111, read_result, "System Bus")]
)
if write_result:
# The data at 0x3001111 was still the same value as
# what was returned from the first `_bizhawk.read`,
# so the data was written.
...
else:
# The data at 0x3001111 has changed since the
# first `_bizhawk.read`, so the data was not written.
...
```
2. Use `lock` and `unlock` (discouraged if not necessary). When you call `lock`, you tell the emulator to stop advancing
frames and just process requests until it receives an unlock request. This means you can lock, read the address, write
the data, and then unlock on a single frame. **However**, this is _slow_. If you can't get in and get out quickly
enough, players will notice a stutter in the emulation.
```py
# Pause emulation
await _bizhawk.lock(ctx)
# Get the address of the save data
read_result: bytes = (await _bizhawk.read(ctx, [(0x3001111, 4, "System Bus")]))[0]
save_data_address = int.from_bytes(read_result, "little")
# Write to `save_data_address`
await _bizhawk.write(ctx, [(save_data_address, [0xAA, 0xBB], "System Bus")])
# Resume emulation
await _bizhawk.unlock(ctx)
```
You should always use `guarded_read` and `guarded_write` instead of locking the emulator if possible. It may be
unreliable, but that's by design. Most of the time you should have no problem giving up and retrying. Data that is
volatile but only changes occasionally is the perfect use case.
If data is almost guaranteed to change between frames, locking may be the better solution. You can lower the time spent
locked by using `send_requests` directly to include as many requests alongside the `LOCK` and `UNLOCK` requests as
possible. But in general it's probably worth doing some extra asm hacking and designing to make guards work instead.
## Implementing a Client
`BizHawkClient` itself is built on `CommonClient` and inspired heavily by `SNIClient`. Your world's client should
inherit from `BizHawkClient` in `worlds/_bizhawk/client.py`. It must implement `validate_rom` and `game_watcher`, and
must define values for `system` and `game`.
As with the functions and classes in the previous section, I would highly recommend looking at the types and docstrings
of the code itself.
`game` should be the same value you use for your world definition.
`system` can either be a string or a tuple of strings. This is the system (or systems) that your client is intended to
handle games on (SNES, GBA, etc.). It's used to prevent validators from running on unknown systems and crashing. The
actual abbreviation corresponds to whatever BizHawk returns from `emu.getsystemid()`.
`patch_suffix` is an optional `ClassVar` meant to specify the file extensions you want to register. It can be a string
or tuple of strings. When a player clicks "Open Patch" in a launcher, the suffix(es) will be whitelisted in the file
select dialog and they will be associated with BizHawkClient. This does not affect whether the user's computer will
associate the file extension with Archipelago.
`validate_rom` is called to figure out whether a given ROM belongs to your client. It will only be called when a ROM is
running on a system you specified in your `system` class variable. In most cases, that will be a single system and you
can be sure that you're not about to try to read from nonexistent domains or out of bounds. If you decide to claim this
ROM as yours, this is where you should do setup for things like `items_handling`.
`game_watcher` is the "main loop" of your client where you should be checking memory and sending new items to the ROM.
`BizHawkClient` will make sure that your `game_watcher` only runs when your client has validated the ROM, and will do
its best to make sure you're connected to the connector script before calling your watcher. It runs this loop either
immediately once it receives a message from the server, or a specified amount of time after the last iteration of the
loop finished.
`validate_rom`, `game_watcher`, and other methods will be passed an instance of `BizHawkClientContext`, which is a
subclass of `CommonContext`. It additionally includes `slot_data` (if you are connected and asked for slot data),
`bizhawk_ctx` (the instance of `BizHawkContext` that you should be giving to functions like `guarded_read`), and
`watcher_timeout` (the amount of time in seconds between iterations of the game watcher loop).
### Example
A very simple client might look like this. All addresses here are made up; you should instead be using addresses that
make sense for your specific ROM. The `validate_rom` here tries to read the name of the ROM. If it gets the value it
wanted, it sets a couple values on `ctx` and returns `True`. The `game_watcher` reads some data from memory and acts on
it by sending messages to AP. You should be smarter than this example, which will send `LocationChecks` messages even if
there's nothing new since the last loop.
```py
from typing import TYPE_CHECKING
from NetUtils import ClientStatus
import worlds._bizhawk as bizhawk
from worlds._bizhawk.client import BizHawkClient
if TYPE_CHECKING:
from worlds._bizhawk.context import BizHawkClientContext
class MyGameClient(BizHawkClient):
game = "My Game"
system = "GBA"
patch_suffix = ".apextension"
async def validate_rom(self, ctx: "BizHawkClientContext") -> bool:
try:
# Check ROM name/patch version
rom_name = ((await bizhawk.read(ctx.bizhawk_ctx, [(0x100, 6, "ROM")]))[0]).decode("ascii")
if rom_name != "MYGAME":
return False # Not a MYGAME ROM
except bizhawk.RequestFailedError:
return False # Not able to get a response, say no for now
# This is a MYGAME ROM
ctx.game = self.game
ctx.items_handling = 0b001
ctx.want_slot_data = True
return True
async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
try:
# Read save data
save_data = await bizhawk.read(
ctx.bizhawk_ctx,
[(0x3000100, 20, "System Bus")]
)[0]
# Check locations
if save_data[2] & 0x04:
await ctx.send_msgs([{
"cmd": "LocationChecks",
"locations": [23]
}])
# Send game clear
if not ctx.finished_game and (save_data[5] & 0x01):
await ctx.send_msgs([{
"cmd": "StatusUpdate",
"status": ClientStatus.CLIENT_GOAL
}])
except bizhawk.RequestFailedError:
# The connector didn't respond. Exit handler and return to main loop to reconnect
pass
```
### Tips
- Make sure your client gets imported when your world is imported. You probably don't need to actually use anything in
your `client.py` elsewhere, but you still have to import the file for your client to register itself.
- When it comes to performance, there are two directions to optimize:
1. If you need to execute multiple commands on the same frame, do as little work as possible. Only read and write necessary data,
and if you have to use locks, unlock as soon as it's okay to advance frames. This is probably the obvious one.
2. Multiple things that don't have to happen on the same frame should be split up if they're likely to be slow.
Remember, the game watcher runs only a few times per second. Extra function calls on the client aren't that big of a
deal; the player will not notice if your `game_watcher` is slow. But the emulator has to be done with any given set of
commands in 1/60th of a second to avoid hiccups (faster still if your players use speedup). Too many reads of too much
data at the same time is more likely to cause a bad user experience.
- Your `game_watcher` will be called regardless of the status of the client's connection to the server. Double-check the
server connection before trying to interact with it.
- By default, the player will be asked to provide their slot name after connecting to the server and validating, and
that input will be used to authenticate with the `Connect` command. You can override `set_auth` in your own client to
set it automatically based on data in the ROM or on your client instance.
- You can override `on_package` in your client to watch raw packages, but don't forget you also have access to a
subclass of `CommonContext` and its API.
- You can import `BizHawkClientContext` for type hints using `typing.TYPE_CHECKING`. Importing it without conditions at
the top of the file will probably cause a circular dependency.
- Your game's system may have multiple usable cores in BizHawk. You can use `get_cores` to try to determine which one is
currently loaded (it's the best we can do). Some cores may differ in the names of memory domains. It's good to check all
the available cores to find differences before your users do.
- The connector script includes a DEBUG variable that you can use to log requests/responses. (Be aware that as the log
grows in size in BizHawk, it begins to stutter while trying to print it.)

View File

@@ -43,8 +43,7 @@ class Goal(Choice):
Triforce Hunt: Collect Triforce pieces spread throughout the worlds, then turn them in to Murahadala in front of Hyrule Castle
Local Triforce Hunt: Collect Triforce pieces spread throughout your world, then turn them in to Murahadala in front of Hyrule Castle
Ganon Triforce Hunt: Collect Triforce pieces spread throughout the worlds, then kill Ganon
Local Ganon Triforce Hunt: Collect Triforce pieces spread throughout your world, then kill Ganon
Ice Rod Hunt: You start with everything except Ice Rod. Find the Ice rod, then kill Trinexx at Turtle rock."""
Local Ganon Triforce Hunt: Collect Triforce pieces spread throughout your world, then kill Ganon"""
display_name = "Goal"
default = 0
option_ganon = 0
@@ -154,9 +153,9 @@ class OpenPyramid(Choice):
def to_bool(self, world: MultiWorld, player: int) -> bool:
if self.value == self.option_goal:
return world.goal[player] in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'}
return world.goal[player].current_key in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'}
elif self.value == self.option_auto:
return world.goal[player] in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'} \
return world.goal[player].current_key in {'crystals', 'ganon_triforce_hunt', 'local_ganon_triforce_hunt', 'ganon_pedestal'} \
and (world.entrance_shuffle[player] in {'vanilla', 'dungeons_simple', 'dungeons_full', 'dungeons_crossed'} or not
world.shuffle_ganon)
elif self.value == self.option_open:
@@ -211,13 +210,12 @@ class map_shuffle(DungeonItem):
display_name = "Map Shuffle"
class key_drop_shuffle(Toggle):
class key_drop_shuffle(DefaultOnToggle):
"""Shuffle keys found in pots and dropped from killed enemies,
respects the small key and big key shuffle options."""
display_name = "Key Drop Shuffle"
class DungeonCounters(Choice):
"""On: Always display amount of items checked in a dungeon. Pickup: Show when compass is picked up.
Default: Show when compass is picked up if the compass itself is shuffled. Off: Never show item count in dungeons."""

View File

@@ -398,9 +398,9 @@ def global_rules(world, player):
set_rule(world.get_entrance('Blind Fight', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player, 3) and can_use_bombs(state, player))
set_rule(world.get_location('Thieves\' Town - Big Chest', player),
lambda state: (state._lttp_has_key('Small Key (Thieves Town)', player, 3)) and state.has('Hammer', player))
lambda state: ((state._lttp_has_key('Small Key (Thieves Town)', player, 3)) or (location_item_name(state, 'Thieves\' Town - Big Chest', player) == ("Small Key (Thieves Town)", player)) and state._lttp_has_key('Small Key (Thieves Town)', player, 2)) and state.has('Hammer', player))
if world.accessibility[player] != 'full':
allow_self_locking_items(world.get_location('Thieves\' Town - Big Chest', player), 'Small Key (Thieves Town)')
set_always_allow(world.get_location('Thieves\' Town - Big Chest', player), lambda state, item: item.name == 'Small Key (Thieves Town)' and item.player == player)
set_rule(world.get_location('Thieves\' Town - Attic', player), lambda state: state._lttp_has_key('Small Key (Thieves Town)', player, 3))
set_rule(world.get_location('Thieves\' Town - Spike Switch Pot Key', player),
@@ -421,11 +421,7 @@ def global_rules(world, player):
set_rule(world.get_location('Ice Palace - Jelly Key Drop', player), lambda state: can_melt_things(state, player))
set_rule(world.get_location('Ice Palace - Compass Chest', player), lambda state: can_melt_things(state, player) and state._lttp_has_key('Small Key (Ice Palace)', player))
set_rule(world.get_entrance('Ice Palace (Second Section)', player), lambda state: can_melt_things(state, player) and state._lttp_has_key('Small Key (Ice Palace)', player) and can_use_bombs(state, player))
if not world.enemy_shuffle[player]:
# Stalfos Knights can be killed by damaging them repeatedly with boomerang, swords, etc. if bombs are
# unavailable. If bombs are available, the pots can be thrown at them, so no other weapons are needed
add_rule(world.get_entrance('Ice Palace (Second Section)', player), lambda state: (can_use_bombs(state, player)
or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) or has_sword(state, player) or state.has("Hammer", player)))
set_rule(world.get_entrance('Ice Palace (Main)', player), lambda state: state._lttp_has_key('Small Key (Ice Palace)', player, 2))
set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player))
set_rule(world.get_entrance('Ice Palace (Kholdstare)', player), lambda state: can_lift_rocks(state, player) and state.has('Hammer', player) and state.has('Big Key (Ice Palace)', player) and (state._lttp_has_key('Small Key (Ice Palace)', player, 6) or (state.has('Cane of Somaria', player) and state._lttp_has_key('Small Key (Ice Palace)', player, 5))))

View File

@@ -176,6 +176,9 @@ def push_shop_inventories(multiworld):
get_price(multiworld, location.shop.inventory[location.shop_slot], location.player,
location.shop_price_type)[1])
for world in multiworld.get_game_worlds("A Link to the Past"):
world.pushed_shop_inventories.set()
def create_shops(multiworld, player: int):
@@ -450,16 +453,16 @@ def get_price(multiworld, item, player: int, price_type=None):
price = item["price"]
if multiworld.randomize_shop_prices[player]:
adjust = 2 if price < 100 else 5
price = int((price / adjust) * (0.5 + multiworld.random.random() * 1.5)) * adjust
multiworld.random.shuffle(price_types)
price = int((price / adjust) * (0.5 + multiworld.per_slot_randoms[player].random() * 1.5)) * adjust
multiworld.per_slot_randoms[player].shuffle(price_types)
for p_type in price_types:
if any(x in item['item'] for x in price_blacklist[p_type]):
continue
return p_type, price_chart[p_type](price, diff)
else:
# This is an AP location and the price will be adjusted after an item is shuffled into it
p_type = multiworld.random.choice(price_types)
return p_type, price_chart[p_type](min(int(multiworld.random.randint(8, 56)
p_type = multiworld.per_slot_randoms[player].choice(price_types)
return p_type, price_chart[p_type](min(int(multiworld.per_slot_randoms[player].randint(8, 56)
* multiworld.shop_price_modifier[player] / 100) * 5, 9999), diff)

View File

@@ -256,6 +256,7 @@ class ALTTPWorld(World):
self.dungeon_local_item_names = set()
self.dungeon_specific_item_names = set()
self.rom_name_available_event = threading.Event()
self.pushed_shop_inventories = threading.Event()
self.has_progressive_bows = False
self.dungeons = {}
self.waterfall_fairy_bottle_fill = "Bottle"
@@ -508,8 +509,8 @@ class ALTTPWorld(World):
fill_dungeons_restrictive(world)
@classmethod
def stage_post_fill(cls, world):
push_shop_inventories(world)
def stage_generate_output(cls, multiworld, output_directory):
push_shop_inventories(multiworld)
@property
def use_enemizer(self) -> bool:
@@ -523,6 +524,9 @@ class ALTTPWorld(World):
def generate_output(self, output_directory: str):
multiworld = self.multiworld
player = self.player
self.pushed_shop_inventories.wait()
try:
use_enemizer = self.use_enemizer

View File

@@ -23,14 +23,14 @@ class TestAgahnimsTower(TestDungeon):
["Castle Tower - Dark Archer Key Drop", True, ['Progressive Sword', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Lamp']],
["Castle Tower - Circle of Pots Key Drop", False, []],
["Castle Tower - Circle of Pots Key Drop", False, ['Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)']],
["Castle Tower - Circle of Pots Key Drop", False, ['Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)']],
["Castle Tower - Circle of Pots Key Drop", False, [], ['Lamp']],
["Castle Tower - Circle of Pots Key Drop", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)', 'Progressive Sword', 'Hammer', 'Progressive Bow', 'Fire Rod', 'Ice Rod', 'Cane of Somaria', 'Cane of Byrna']],
["Castle Tower - Circle of Pots Key Drop", True, ['Progressive Sword', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Lamp']],
["Castle Tower - Circle of Pots Key Drop", True, ['Progressive Sword', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Lamp']],
["Agahnim 1", False, []],
["Agahnim 1", False, ['Small Key (Agahnims Tower)'], ['Small Key (Agahnims Tower)']],
["Agahnim 1", False, ['Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)']],
["Agahnim 1", False, [], ['Progressive Sword']],
["Agahnim 1", False, [], ['Lamp']],
["Agahnim 1", True, ['Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Lamp', 'Progressive Sword']],
["Agahnim 1", True, ['Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Small Key (Agahnims Tower)', 'Lamp', 'Progressive Sword']],
])

View File

@@ -19,35 +19,35 @@ class TestDesertPalace(TestDungeon):
["Desert Palace - Compass Chest", False, []],
["Desert Palace - Compass Chest", False, [], ['Small Key (Desert Palace)']],
["Desert Palace - Compass Chest", False, ['Progressive Sword', 'Hammer', 'Fire Rod', 'Ice Rod', 'Progressive Bow', 'Cane of Somaria', 'Cane of Byrna']],
["Desert Palace - Compass Chest", False, ['Small Key (Desert Palace)']],
["Desert Palace - Compass Chest", True, ['Progressive Sword', 'Small Key (Desert Palace)']],
["Desert Palace - Compass Chest", True, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)']],
["Desert Palace - Compass Chest", True, ['Progressive Sword', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)']],
["Desert Palace - Big Key Chest", False, []],
["Desert Palace - Big Key Chest", False, [], ['Small Key (Desert Palace)']],
["Desert Palace - Big Key Chest", False, ['Progressive Sword', 'Hammer', 'Fire Rod', 'Ice Rod', 'Progressive Bow', 'Cane of Somaria', 'Cane of Byrna']],
["Desert Palace - Big Key Chest", False, ['Small Key (Desert Palace)']],
["Desert Palace - Big Key Chest", True, ['Progressive Sword', 'Small Key (Desert Palace)']],
["Desert Palace - Big Key Chest", False, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)']],
["Desert Palace - Big Key Chest", True, ['Progressive Sword', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)']],
["Desert Palace - Desert Tiles 1 Pot Key", True, []],
["Desert Palace - Beamos Hall Pot Key", False, []],
["Desert Palace - Beamos Hall Pot Key", False, [], ['Small Key (Desert Palace)']],
["Desert Palace - Beamos Hall Pot Key", False, ['Small Key (Desert Palace)']],
["Desert Palace - Beamos Hall Pot Key", False, ['Progressive Sword', 'Hammer', 'Fire Rod', 'Ice Rod', 'Progressive Bow', 'Cane of Somaria', 'Cane of Byrna']],
["Desert Palace - Beamos Hall Pot Key", True, ['Small Key (Desert Palace)', 'Progressive Sword']],
["Desert Palace - Beamos Hall Pot Key", True, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Progressive Sword']],
["Desert Palace - Desert Tiles 2 Pot Key", False, []],
["Desert Palace - Desert Tiles 2 Pot Key", False, ['Small Key (Desert Palace)']],
["Desert Palace - Desert Tiles 2 Pot Key", False, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)']],
["Desert Palace - Desert Tiles 2 Pot Key", False, ['Progressive Sword', 'Hammer', 'Fire Rod', 'Ice Rod', 'Progressive Bow', 'Cane of Somaria', 'Cane of Byrna']],
["Desert Palace - Desert Tiles 2 Pot Key", True, ['Small Key (Desert Palace)', 'Progressive Sword']],
["Desert Palace - Desert Tiles 2 Pot Key", True, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Progressive Sword']],
["Desert Palace - Boss", False, []],
["Desert Palace - Boss", False, [], ['Small Key (Desert Palace)']],
["Desert Palace - Boss", False, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)']],
["Desert Palace - Boss", False, [], ['Big Key (Desert Palace)']],
["Desert Palace - Boss", False, [], ['Lamp', 'Fire Rod']],
["Desert Palace - Boss", False, [], ['Progressive Sword', 'Hammer', 'Fire Rod', 'Ice Rod', 'Progressive Bow', 'Cane of Somaria', 'Cane of Byrna']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Fire Rod']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Progressive Sword']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Hammer']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Cane of Somaria']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Cane of Byrna']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Fire Rod']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Progressive Sword']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Hammer']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Cane of Somaria']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Lamp', 'Cane of Byrna']],
])

View File

@@ -19,12 +19,12 @@ class TestEasternPalace(TestDungeon):
["Eastern Palace - Big Key Chest", False, []],
["Eastern Palace - Big Key Chest", False, [], ['Lamp']],
["Eastern Palace - Big Key Chest", True, ['Lamp', 'Small Key (Eastern Palace)', 'Small Key (Eastern Palace)', 'Progressive Sword']],
["Eastern Palace - Big Key Chest", True, ['Lamp', 'Big Key (Eastern Palace)', 'Progressive Sword']],
#@todo: Advanced?
["Eastern Palace - Boss", False, []],
["Eastern Palace - Boss", False, [], ['Lamp']],
["Eastern Palace - Boss", False, [], ['Progressive Bow']],
["Eastern Palace - Boss", False, [], ['Big Key (Eastern Palace)']],
["Eastern Palace - Boss", True, ['Lamp', 'Progressive Bow', 'Big Key (Eastern Palace)']]
["Eastern Palace - Boss", False, ['Small Key (Eastern Palace)', 'Small Key (Eastern Palace)']],
["Eastern Palace - Boss", True, ['Lamp', 'Small Key (Eastern Palace)', 'Small Key (Eastern Palace)', 'Progressive Bow', 'Big Key (Eastern Palace)']]
])

View File

@@ -33,50 +33,46 @@ class TestGanonsTower(TestDungeon):
["Ganons Tower - Randomizer Room - Top Left", False, []],
["Ganons Tower - Randomizer Room - Top Left", False, [], ['Hammer']],
["Ganons Tower - Randomizer Room - Top Left", False, [], ['Hookshot']],
["Ganons Tower - Randomizer Room - Top Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Randomizer Room - Top Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Randomizer Room - Top Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Randomizer Room - Top Right", False, []],
["Ganons Tower - Randomizer Room - Top Right", False, [], ['Hammer']],
["Ganons Tower - Randomizer Room - Top Right", False, [], ['Hookshot']],
["Ganons Tower - Randomizer Room - Top Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Randomizer Room - Top Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Randomizer Room - Top Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Randomizer Room - Bottom Left", False, []],
["Ganons Tower - Randomizer Room - Bottom Left", False, [], ['Hammer']],
["Ganons Tower - Randomizer Room - Bottom Left", False, [], ['Hookshot']],
["Ganons Tower - Randomizer Room - Bottom Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Randomizer Room - Bottom Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Randomizer Room - Bottom Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Randomizer Room - Bottom Right", False, []],
["Ganons Tower - Randomizer Room - Bottom Right", False, [], ['Hammer']],
["Ganons Tower - Randomizer Room - Bottom Right", False, [], ['Hookshot']],
["Ganons Tower - Randomizer Room - Bottom Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Randomizer Room - Bottom Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Randomizer Room - Bottom Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Firesnake Room", False, []],
["Ganons Tower - Firesnake Room", False, [], ['Hammer']],
["Ganons Tower - Firesnake Room", False, [], ['Hookshot']],
["Ganons Tower - Firesnake Room", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Firesnake Room", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Map Chest", False, []],
["Ganons Tower - Map Chest", False, [], ['Hammer']],
["Ganons Tower - Map Chest", False, [], ['Hookshot', 'Pegasus Boots']],
["Ganons Tower - Map Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Map Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hammer', 'Pegasus Boots']],
["Ganons Tower - Map Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Map Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hammer', 'Pegasus Boots']],
["Ganons Tower - Big Chest", False, []],
["Ganons Tower - Big Chest", False, [], ['Big Key (Ganons Tower)']],
["Ganons Tower - Big Chest", True, ['Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Chest", True, ['Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Big Chest", True, ['Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Chest", True, ['Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Hope Room - Left", True, []],
["Ganons Tower - Hope Room - Right", True, []],
["Ganons Tower - Bob's Chest", False, []],
["Ganons Tower - Bob's Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Bob's Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Bob's Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Bob's Chest", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Tile Room", False, []],
["Ganons Tower - Tile Room", False, [], ['Cane of Somaria']],
@@ -85,34 +81,34 @@ class TestGanonsTower(TestDungeon):
["Ganons Tower - Compass Room - Top Left", False, []],
["Ganons Tower - Compass Room - Top Left", False, [], ['Cane of Somaria']],
["Ganons Tower - Compass Room - Top Left", False, [], ['Fire Rod']],
["Ganons Tower - Compass Room - Top Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Compass Room - Top Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Compass Room - Top Right", False, []],
["Ganons Tower - Compass Room - Top Right", False, [], ['Cane of Somaria']],
["Ganons Tower - Compass Room - Top Right", False, [], ['Fire Rod']],
["Ganons Tower - Compass Room - Top Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Compass Room - Top Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Compass Room - Bottom Left", False, []],
["Ganons Tower - Compass Room - Bottom Left", False, [], ['Cane of Somaria']],
["Ganons Tower - Compass Room - Bottom Left", False, [], ['Fire Rod']],
["Ganons Tower - Compass Room - Bottom Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Compass Room - Bottom Left", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Compass Room - Bottom Right", False, []],
["Ganons Tower - Compass Room - Bottom Right", False, [], ['Cane of Somaria']],
["Ganons Tower - Compass Room - Bottom Right", False, [], ['Fire Rod']],
["Ganons Tower - Compass Room - Bottom Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Compass Room - Bottom Right", True, ['Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Cane of Somaria']],
["Ganons Tower - Big Key Chest", False, []],
["Ganons Tower - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Big Key Room - Left", False, []],
["Ganons Tower - Big Key Room - Left", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Room - Left", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Big Key Room - Left", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Room - Left", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Big Key Room - Right", False, []],
["Ganons Tower - Big Key Room - Right", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Room - Right", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Big Key Room - Right", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Cane of Somaria', 'Fire Rod']],
["Ganons Tower - Big Key Room - Right", True, ['Bomb Upgrade (+5)', 'Progressive Bow', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Hookshot', 'Hammer']],
["Ganons Tower - Mini Helmasaur Room - Left", False, []],
["Ganons Tower - Mini Helmasaur Room - Left", False, [], ['Progressive Bow']],
@@ -132,8 +128,8 @@ class TestGanonsTower(TestDungeon):
["Ganons Tower - Pre-Moldorm Chest", False, [], ['Progressive Bow']],
["Ganons Tower - Pre-Moldorm Chest", False, [], ['Big Key (Ganons Tower)']],
["Ganons Tower - Pre-Moldorm Chest", False, [], ['Lamp', 'Fire Rod']],
["Ganons Tower - Pre-Moldorm Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Lamp']],
["Ganons Tower - Pre-Moldorm Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod']],
["Ganons Tower - Pre-Moldorm Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Lamp']],
["Ganons Tower - Pre-Moldorm Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod']],
["Ganons Tower - Validation Chest", False, []],
["Ganons Tower - Validation Chest", False, [], ['Hookshot']],
@@ -141,8 +137,8 @@ class TestGanonsTower(TestDungeon):
["Ganons Tower - Validation Chest", False, [], ['Big Key (Ganons Tower)']],
["Ganons Tower - Validation Chest", False, [], ['Lamp', 'Fire Rod']],
["Ganons Tower - Validation Chest", False, [], ['Progressive Sword', 'Hammer']],
["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Lamp', 'Hookshot', 'Progressive Sword']],
["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Hookshot', 'Progressive Sword']],
["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Lamp', 'Hookshot', 'Hammer']],
["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Hookshot', 'Hammer']],
["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Lamp', 'Hookshot', 'Progressive Sword']],
["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Hookshot', 'Progressive Sword']],
["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Lamp', 'Hookshot', 'Hammer']],
["Ganons Tower - Validation Chest", True, ['Progressive Bow', 'Big Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Small Key (Ganons Tower)', 'Fire Rod', 'Hookshot', 'Hammer']],
])

View File

@@ -12,8 +12,8 @@ class TestIcePalace(TestDungeon):
["Ice Palace - Big Key Chest", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Big Key Chest", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Big Key Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
["Ice Palace - Big Key Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#@todo: Change from item randomizer - Right side key door is only in logic if big key is in there
#["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#["Ice Palace - Big Key Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
@@ -23,8 +23,8 @@ class TestIcePalace(TestDungeon):
["Ice Palace - Compass Chest", False, []],
["Ice Palace - Compass Chest", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Compass Chest", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Compass Chest", True, ['Fire Rod']],
["Ice Palace - Compass Chest", True, ['Bombos', 'Progressive Sword']],
["Ice Palace - Compass Chest", True, ['Small Key (Ice Palace)', 'Fire Rod']],
["Ice Palace - Compass Chest", True, ['Small Key (Ice Palace)', 'Bombos', 'Progressive Sword']],
["Ice Palace - Map Chest", False, []],
["Ice Palace - Map Chest", False, [], ['Hammer']],
@@ -32,8 +32,8 @@ class TestIcePalace(TestDungeon):
["Ice Palace - Map Chest", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Map Chest", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Map Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Map Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Map Chest", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Map Chest", True, ['Small Key (Ice Palace)', 'Bomb Upgrade (+5)', 'Progressive Glove', 'Fire Rod', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Map Chest", True, ['Small Key (Ice Palace)', 'Bomb Upgrade (+5)', 'Progressive Glove', 'Bombos', 'Progressive Sword', 'Hammer', 'Hookshot', 'Small Key (Ice Palace)']],
#["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cane of Byrna', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#["Ice Palace - Map Chest", True, ['Progressive Glove', 'Cape', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
@@ -43,8 +43,8 @@ class TestIcePalace(TestDungeon):
["Ice Palace - Spike Room", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Spike Room", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Spike Room", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Spike Room", True, ['Bomb Upgrade (+5)', 'Fire Rod', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Spike Room", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword', 'Hookshot', 'Small Key (Ice Palace)']],
["Ice Palace - Spike Room", True, ['Bomb Upgrade (+5)', 'Fire Rod', 'Hookshot', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
["Ice Palace - Spike Room", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword', 'Hookshot', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#["Ice Palace - Spike Room", True, ['Cape', 'Fire Rod', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#["Ice Palace - Spike Room", True, ['Cape', 'Bombos', 'Progressive Sword', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
#["Ice Palace - Spike Room", True, ['Cane of Byrna', 'Fire Rod', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
@@ -54,23 +54,23 @@ class TestIcePalace(TestDungeon):
["Ice Palace - Freezor Chest", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Freezor Chest", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Freezor Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Freezor Chest", True, ['Bomb Upgrade (+5)', 'Fire Rod']],
["Ice Palace - Freezor Chest", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword']],
["Ice Palace - Freezor Chest", True, ['Bomb Upgrade (+5)', 'Fire Rod', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
["Ice Palace - Freezor Chest", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
["Ice Palace - Iced T Room", False, []],
["Ice Palace - Iced T Room", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Iced T Room", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Iced T Room", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Iced T Room", True, ['Bomb Upgrade (+5)', 'Fire Rod']],
["Ice Palace - Iced T Room", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword']],
["Ice Palace - Iced T Room", True, ['Bomb Upgrade (+5)', 'Fire Rod', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
["Ice Palace - Iced T Room", True, ['Bomb Upgrade (+5)', 'Bombos', 'Progressive Sword', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)']],
["Ice Palace - Big Chest", False, []],
["Ice Palace - Big Chest", False, [], ['Big Key (Ice Palace)']],
["Ice Palace - Big Chest", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Big Chest", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Big Chest", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Ice Palace - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Ice Palace)', 'Fire Rod']],
["Ice Palace - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword']],
["Ice Palace - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Fire Rod']],
["Ice Palace - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Bombos', 'Progressive Sword']],
["Ice Palace - Boss", False, []],
["Ice Palace - Boss", False, [], ['Hammer']],
@@ -80,8 +80,8 @@ class TestIcePalace(TestDungeon):
["Ice Palace - Boss", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Boss", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
# need hookshot now to reach the right side for the 6th key
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Fire Rod', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']],
["Ice Palace - Boss", True, ['Bomb Upgrade (+5)', 'Progressive Glove', 'Big Key (Ice Palace)', 'Bombos', 'Progressive Sword', 'Hammer', 'Cane of Somaria', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Small Key (Ice Palace)', 'Hookshot']],
])

View File

@@ -32,36 +32,32 @@ class TestMiseryMire(TestDungeon):
["Misery Mire - Main Lobby", False, []],
["Misery Mire - Main Lobby", False, [], ['Pegasus Boots', 'Hookshot']],
["Misery Mire - Main Lobby", False, [], ['Small Key (Misery Mire)', 'Big Key (Misery Mire)']],
["Misery Mire - Main Lobby", True, ['Small Key (Misery Mire)', 'Hookshot', 'Progressive Sword']],
["Misery Mire - Main Lobby", True, ['Small Key (Misery Mire)', 'Pegasus Boots', 'Progressive Sword']],
["Misery Mire - Main Lobby", True, ['Big Key (Misery Mire)', 'Hookshot', 'Progressive Sword']],
["Misery Mire - Main Lobby", True, ['Big Key (Misery Mire)', 'Pegasus Boots', 'Progressive Sword']],
["Misery Mire - Main Lobby", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Hookshot', 'Progressive Sword']],
["Misery Mire - Main Lobby", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Pegasus Boots', 'Progressive Sword']],
["Misery Mire - Big Key Chest", False, []],
["Misery Mire - Big Key Chest", False, [], ['Fire Rod', 'Lamp']],
["Misery Mire - Big Key Chest", False, [], ['Pegasus Boots', 'Hookshot']],
["Misery Mire - Big Key Chest", False, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)'], ['Small Key (Misery Mire)']],
["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Hookshot']],
["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Hookshot']],
["Misery Mire - Big Key Chest", False, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)']],
["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Hookshot']],
["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Big Key Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Hookshot']],
["Misery Mire - Compass Chest", False, []],
["Misery Mire - Compass Chest", False, [], ['Fire Rod', 'Lamp']],
["Misery Mire - Compass Chest", False, [], ['Pegasus Boots', 'Hookshot']],
["Misery Mire - Compass Chest", False, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)'], ['Small Key (Misery Mire)']],
["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Hookshot']],
["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Hookshot']],
["Misery Mire - Compass Chest", False, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)']],
["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Lamp', 'Progressive Sword', 'Hookshot']],
["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Compass Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Fire Rod', 'Progressive Sword', 'Hookshot']],
["Misery Mire - Map Chest", False, []],
["Misery Mire - Map Chest", False, [], ['Small Key (Misery Mire)', 'Big Key (Misery Mire)']],
["Misery Mire - Map Chest", False, [], ['Small Key (Misery Mire)']],
["Misery Mire - Map Chest", False, [], ['Pegasus Boots', 'Hookshot']],
["Misery Mire - Map Chest", True, ['Small Key (Misery Mire)', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Map Chest", True, ['Small Key (Misery Mire)', 'Progressive Sword', 'Hookshot']],
["Misery Mire - Map Chest", True, ['Big Key (Misery Mire)', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Map Chest", True, ['Big Key (Misery Mire)', 'Progressive Sword', 'Hookshot']],
["Misery Mire - Map Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Progressive Sword', 'Pegasus Boots']],
["Misery Mire - Map Chest", True, ['Small Key (Misery Mire)', 'Small Key (Misery Mire)', 'Progressive Sword', 'Hookshot']],
["Misery Mire - Spike Chest", False, []],
["Misery Mire - Spike Chest", False, [], ['Pegasus Boots', 'Hookshot']],

View File

@@ -96,6 +96,6 @@ class TestSkullWoods(TestDungeon):
["Skull Woods - Boss", False, []],
["Skull Woods - Boss", False, [], ['Fire Rod']],
["Skull Woods - Boss", False, [], ['Progressive Sword']],
["Skull Woods - Boss", False, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)'], ['Small Key (Skull Woods)']],
["Skull Woods - Boss", True, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Fire Rod', 'Progressive Sword']],
["Skull Woods - Boss", False, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)'], ['Small Key (Skull Woods)']],
["Skull Woods - Boss", True, ['Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Small Key (Skull Woods)', 'Fire Rod', 'Progressive Sword']],
])

View File

@@ -16,15 +16,15 @@ class TestSwampPalace(TestDungeon):
["Swamp Palace - Big Chest", False, [], ['Open Floodgate']],
["Swamp Palace - Big Chest", False, [], ['Hammer']],
["Swamp Palace - Big Chest", False, [], ['Big Key (Swamp Palace)']],
["Swamp Palace - Big Chest", False, [], ['Small Key (Swamp Palace)']],
["Swamp Palace - Big Chest", True, ['Open Floodgate', 'Big Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']],
["Swamp Palace - Big Chest", False, [], ['Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)']],
["Swamp Palace - Big Chest", True, ['Open Floodgate', 'Big Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']],
["Swamp Palace - Big Key Chest", False, []],
["Swamp Palace - Big Key Chest", False, [], ['Flippers']],
["Swamp Palace - Big Key Chest", False, [], ['Open Floodgate']],
["Swamp Palace - Big Key Chest", False, [], ['Hammer']],
["Swamp Palace - Big Key Chest", False, [], ['Small Key (Swamp Palace)']],
["Swamp Palace - Big Key Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']],
["Swamp Palace - Big Key Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']],
["Swamp Palace - Map Chest", False, []],
["Swamp Palace - Map Chest", False, [], ['Flippers']],
@@ -38,14 +38,14 @@ class TestSwampPalace(TestDungeon):
["Swamp Palace - West Chest", False, [], ['Open Floodgate']],
["Swamp Palace - West Chest", False, [], ['Hammer']],
["Swamp Palace - West Chest", False, [], ['Small Key (Swamp Palace)']],
["Swamp Palace - West Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']],
["Swamp Palace - West Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']],
["Swamp Palace - Compass Chest", False, []],
["Swamp Palace - Compass Chest", False, [], ['Flippers']],
["Swamp Palace - Compass Chest", False, [], ['Open Floodgate']],
["Swamp Palace - Compass Chest", False, [], ['Hammer']],
["Swamp Palace - Compass Chest", False, [], ['Small Key (Swamp Palace)']],
["Swamp Palace - Compass Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']],
["Swamp Palace - Compass Chest", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer']],
["Swamp Palace - Flooded Room - Left", False, []],
["Swamp Palace - Flooded Room - Left", False, [], ['Flippers']],
@@ -53,7 +53,7 @@ class TestSwampPalace(TestDungeon):
["Swamp Palace - Flooded Room - Left", False, [], ['Hammer']],
["Swamp Palace - Flooded Room - Left", False, [], ['Hookshot']],
["Swamp Palace - Flooded Room - Left", False, [], ['Small Key (Swamp Palace)']],
["Swamp Palace - Flooded Room - Left", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']],
["Swamp Palace - Flooded Room - Left", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']],
["Swamp Palace - Flooded Room - Right", False, []],
["Swamp Palace - Flooded Room - Right", False, [], ['Flippers']],
@@ -61,7 +61,7 @@ class TestSwampPalace(TestDungeon):
["Swamp Palace - Flooded Room - Right", False, [], ['Hammer']],
["Swamp Palace - Flooded Room - Right", False, [], ['Hookshot']],
["Swamp Palace - Flooded Room - Right", False, [], ['Small Key (Swamp Palace)']],
["Swamp Palace - Flooded Room - Right", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']],
["Swamp Palace - Flooded Room - Right", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']],
["Swamp Palace - Waterfall Room", False, []],
["Swamp Palace - Waterfall Room", False, [], ['Flippers']],
@@ -69,7 +69,7 @@ class TestSwampPalace(TestDungeon):
["Swamp Palace - Waterfall Room", False, [], ['Hammer']],
["Swamp Palace - Waterfall Room", False, [], ['Hookshot']],
["Swamp Palace - Waterfall Room", False, [], ['Small Key (Swamp Palace)']],
["Swamp Palace - Waterfall Room", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']],
["Swamp Palace - Waterfall Room", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']],
["Swamp Palace - Boss", False, []],
["Swamp Palace - Boss", False, [], ['Flippers']],
@@ -77,5 +77,5 @@ class TestSwampPalace(TestDungeon):
["Swamp Palace - Boss", False, [], ['Hammer']],
["Swamp Palace - Boss", False, [], ['Hookshot']],
["Swamp Palace - Boss", False, [], ['Small Key (Swamp Palace)']],
["Swamp Palace - Boss", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']],
["Swamp Palace - Boss", True, ['Open Floodgate', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Small Key (Swamp Palace)', 'Flippers', 'Hammer', 'Hookshot']],
])

View File

@@ -21,18 +21,19 @@ class TestThievesTown(TestDungeon):
["Thieves' Town - Spike Switch Pot Key", False, []],
["Thieves' Town - Spike Switch Pot Key", False, [], ['Big Key (Thieves Town)']],
["Thieves' Town - Spike Switch Pot Key", True, ['Big Key (Thieves Town)']],
["Thieves' Town - Spike Switch Pot Key", False, [], ['Small Key (Thieves Town)']],
["Thieves' Town - Spike Switch Pot Key", True, ['Big Key (Thieves Town)', 'Small Key (Thieves Town)']],
["Thieves' Town - Attic", False, []],
["Thieves' Town - Attic", False, [], ['Big Key (Thieves Town)']],
["Thieves' Town - Attic", False, [], ['Small Key (Thieves Town)']],
["Thieves' Town - Attic", True, ['Big Key (Thieves Town)', 'Small Key (Thieves Town)']],
["Thieves' Town - Attic", True, ['Big Key (Thieves Town)', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)']],
["Thieves' Town - Big Chest", False, []],
["Thieves' Town - Big Chest", False, [], ['Big Key (Thieves Town)']],
["Thieves' Town - Big Chest", False, [], ['Small Key (Thieves Town)']],
["Thieves' Town - Big Chest", False, [], ['Hammer']],
["Thieves' Town - Big Chest", True, ['Hammer', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)']],
["Thieves' Town - Big Chest", True, ['Hammer', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)']],
["Thieves' Town - Blind's Cell", False, []],
["Thieves' Town - Blind's Cell", False, [], ['Big Key (Thieves Town)']],
@@ -42,8 +43,8 @@ class TestThievesTown(TestDungeon):
["Thieves' Town - Boss", False, [], ['Big Key (Thieves Town)']],
["Thieves' Town - Boss", False, [], ['Hammer', 'Progressive Sword', 'Cane of Somaria', 'Cane of Byrna']],
["Thieves' Town - Boss", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Hammer']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Progressive Sword']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Somaria']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Byrna']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Hammer']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Progressive Sword']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Somaria']],
["Thieves' Town - Boss", True, ['Bomb Upgrade (+5)', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)', 'Small Key (Thieves Town)', 'Big Key (Thieves Town)', 'Cane of Byrna']],
])

View File

@@ -11,16 +11,16 @@ class TestInvertedTurtleRock(TestInverted):
["Turtle Rock - Compass Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Quake', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Compass Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Compass Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", False, []],
["Turtle Rock - Chain Chomps", False, [], ['Magic Mirror', 'Cane of Somaria']],
["Turtle Rock - Chain Chomps", False, ['Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Bomb Upgrade (+5)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']],
["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot']],
@@ -33,10 +33,10 @@ class TestInvertedTurtleRock(TestInverted):
["Turtle Rock - Roller Room - Left", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Quake', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Moon Pearl', 'Fire Rod', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Moon Pearl', 'Fire Rod', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", False, []],
["Turtle Rock - Roller Room - Right", False, [], ['Cane of Somaria']],
@@ -45,17 +45,17 @@ class TestInvertedTurtleRock(TestInverted):
["Turtle Rock - Roller Room - Right", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Quake', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Moon Pearl', 'Fire Rod', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Moon Pearl', 'Fire Rod', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", False, []],
["Turtle Rock - Big Chest", False, [], ['Big Key (Turtle Rock)']],
["Turtle Rock - Big Chest", False, [], ['Magic Mirror', 'Cane of Somaria']],
["Turtle Rock - Big Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hookshot']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']],
@@ -66,12 +66,12 @@ class TestInvertedTurtleRock(TestInverted):
["Turtle Rock - Big Key Chest", False, []],
["Turtle Rock - Big Key Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
# Mirror in from ledge, use left side entrance, have enough keys to get to the chest
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", False, []],
["Turtle Rock - Crystaroller Room", False, [], ['Big Key (Turtle Rock)', 'Magic Mirror']],
@@ -80,7 +80,7 @@ class TestInvertedTurtleRock(TestInverted):
["Turtle Rock - Crystaroller Room", False, [], ['Magic Mirror', 'Cane of Somaria']],
["Turtle Rock - Crystaroller Room", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot']],
@@ -98,10 +98,10 @@ class TestInvertedTurtleRock(TestInverted):
["Turtle Rock - Boss", False, [], ['Magic Mirror', 'Lamp']],
["Turtle Rock - Boss", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Flute', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Magic Upgrade (1/2)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)','Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Flute', 'Magic Mirror', 'Moon Pearl', 'Hookshot', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']]
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Magic Upgrade (1/2)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)','Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Flute', 'Magic Mirror', 'Moon Pearl', 'Hookshot', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']]
])
@@ -118,11 +118,11 @@ class TestInvertedTurtleRock(TestInverted):
[location, False, [], ['Magic Mirror', 'Lamp']],
[location, False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
[location, True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cane of Byrna']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cane of Byrna']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cane of Byrna']],
[location, True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cape']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cape']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cape']],
[location, True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']],
# Mirroring into Eye Bridge does not require Cane of Somaria
[location, True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Byrna']],

View File

@@ -11,17 +11,17 @@ class TestInvertedTurtleRock(TestInvertedMinor):
["Turtle Rock - Compass Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Quake', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Compass Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Compass Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", False, []],
["Turtle Rock - Chain Chomps", False, [], ['Magic Mirror', 'Cane of Somaria']],
# Item rando only needs 1 key. ER needs to consider the case when the back is accessible, but not the middle (key wasted on Trinexx door)
["Turtle Rock - Chain Chomps", False, ['Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", True, ['Bomb Upgrade (+5)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Turtle Rock - Chain Chomps", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']],
["Turtle Rock - Chain Chomps", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot']],
@@ -34,10 +34,10 @@ class TestInvertedTurtleRock(TestInvertedMinor):
["Turtle Rock - Roller Room - Left", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Quake', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Moon Pearl', 'Fire Rod', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Moon Pearl', 'Fire Rod', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Left", True, ['Fire Rod', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", False, []],
["Turtle Rock - Roller Room - Right", False, [], ['Cane of Somaria']],
@@ -46,17 +46,17 @@ class TestInvertedTurtleRock(TestInvertedMinor):
["Turtle Rock - Roller Room - Right", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Quake', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Moon Pearl', 'Fire Rod', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Moon Pearl', 'Fire Rod', 'Flute', 'Magic Mirror', 'Hookshot', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Roller Room - Right", True, ['Fire Rod', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", False, []],
["Turtle Rock - Big Chest", False, [], ['Big Key (Turtle Rock)']],
["Turtle Rock - Big Chest", False, [], ['Magic Mirror', 'Cane of Somaria']],
["Turtle Rock - Big Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Bomb Upgrade (+5)', 'Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Somaria']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hookshot']],
["Turtle Rock - Big Chest", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']],
@@ -67,12 +67,12 @@ class TestInvertedTurtleRock(TestInvertedMinor):
["Turtle Rock - Big Key Chest", False, []],
["Turtle Rock - Big Key Chest", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
# Mirror in from ledge, use left side entrance, have enough keys to get to the chest
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Big Key Chest", True, ['Flute', 'Progressive Glove', 'Progressive Glove', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", False, []],
["Turtle Rock - Crystaroller Room", False, [], ['Big Key (Turtle Rock)', 'Magic Mirror']],
@@ -81,7 +81,7 @@ class TestInvertedTurtleRock(TestInvertedMinor):
["Turtle Rock - Crystaroller Room", False, [], ['Magic Mirror', 'Cane of Somaria']],
["Turtle Rock - Crystaroller Room", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Moon Pearl', 'Hookshot']],
["Turtle Rock - Crystaroller Room", True, ['Big Key (Turtle Rock)', 'Moon Pearl', 'Flute', 'Magic Mirror', 'Hookshot']],
@@ -99,10 +99,10 @@ class TestInvertedTurtleRock(TestInvertedMinor):
["Turtle Rock - Boss", False, [], ['Magic Mirror', 'Lamp']],
["Turtle Rock - Boss", False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Flute', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Magic Upgrade (1/2)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)','Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Flute', 'Magic Mirror', 'Moon Pearl', 'Hookshot', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']]
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Bottle', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Progressive Sword', 'Cane of Somaria', 'Magic Upgrade (1/2)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)','Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']],
["Turtle Rock - Boss", True, ['Ice Rod', 'Fire Rod', 'Flute', 'Magic Mirror', 'Moon Pearl', 'Hookshot', 'Hammer', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Big Key (Turtle Rock)']]
])
@@ -117,11 +117,11 @@ class TestInvertedTurtleRock(TestInvertedMinor):
[location, False, [], ['Magic Mirror', 'Lamp']],
[location, False, ['Small Key (Turtle Rock)', 'Small Key (Turtle Rock)'], ['Magic Mirror', 'Small Key (Turtle Rock)']],
[location, True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cane of Byrna']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cane of Byrna']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cane of Byrna']],
[location, True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Cape']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cape']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Cape']],
[location, True, ['Big Key (Turtle Rock)', 'Flute', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Lamp', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']],
[location, True, ['Big Key (Turtle Rock)', 'Lamp', 'Progressive Glove', 'Quake', 'Progressive Sword', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Progressive Shield', 'Progressive Shield', 'Progressive Shield']],
# Mirroring into Eye Bridge does not require Cane of Somaria
[location, True, ['Lamp', 'Magic Mirror', 'Progressive Glove', 'Progressive Glove', 'Cane of Byrna']],

View File

@@ -13,16 +13,15 @@ class TestDungeons(TestInvertedOWG):
["Sanctuary", False, []],
["Sanctuary", False, ['Beat Agahnim 1']],
["Sanctuary", True, ['Magic Mirror', 'Beat Agahnim 1']],
["Sanctuary", True, ['Progressive Sword', 'Lamp', 'Beat Agahnim 1', 'Small Key (Hyrule Castle)']],
["Sanctuary", True, ['Lamp', 'Beat Agahnim 1', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)']],
["Sanctuary", True, ['Moon Pearl', 'Pegasus Boots']],
["Sanctuary", True, ['Magic Mirror', 'Pegasus Boots']],
["Sewers - Secret Room - Left", False, []],
["Sewers - Secret Room - Left", True, ['Moon Pearl', 'Progressive Glove', 'Pegasus Boots']],
["Sewers - Secret Room - Left", True, ['Progressive Sword', 'Moon Pearl', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Progressive Sword', 'Magic Mirror', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Bomb Upgrade (+5)', 'Progressive Sword', 'Beat Agahnim 1', 'Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Bomb Upgrade (+10)', 'Beat Agahnim 1', 'Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Moon Pearl', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Magic Mirror', 'Pegasus Boots', 'Lamp', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Bomb Upgrade (+5)', 'Beat Agahnim 1', 'Lamp', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)']],
["Eastern Palace - Compass Chest", False, []],
["Eastern Palace - Compass Chest", True, ['Moon Pearl', 'Pegasus Boots']],
@@ -37,8 +36,8 @@ class TestDungeons(TestInvertedOWG):
["Desert Palace - Boss", False, [], ['Small Key (Desert Palace)']],
["Desert Palace - Boss", False, [], ['Big Key (Desert Palace)']],
["Desert Palace - Boss", False, [], ['Lamp', 'Fire Rod']],
["Desert Palace - Boss", True, ['Progressive Sword', 'Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Moon Pearl', 'Pegasus Boots', 'Lamp']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Moon Pearl', 'Pegasus Boots', 'Fire Rod']],
["Desert Palace - Boss", True, ['Progressive Sword', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Moon Pearl', 'Pegasus Boots', 'Lamp']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Big Key (Desert Palace)', 'Moon Pearl', 'Pegasus Boots', 'Fire Rod']],
["Tower of Hera - Basement Cage", False, []],
["Tower of Hera - Basement Cage", False, [], ['Moon Pearl']],
@@ -76,8 +75,8 @@ class TestDungeons(TestInvertedOWG):
["Ice Palace - Compass Chest", False, []],
["Ice Palace - Compass Chest", False, [], ['Fire Rod', 'Bombos', 'Progressive Sword']],
# Qirn Jump
["Ice Palace - Compass Chest", True, ['Fire Rod']],
["Ice Palace - Compass Chest", True, ['Bombos', 'Progressive Sword']],
["Ice Palace - Compass Chest", True, ['Fire Rod', 'Small Key (Ice Palace)']],
["Ice Palace - Compass Chest", True, ['Bombos', 'Progressive Sword', 'Small Key (Ice Palace)']],
["Misery Mire - Bridge Chest", False, []],
["Misery Mire - Bridge Chest", False, [], ['Ether']],
@@ -86,7 +85,7 @@ class TestDungeons(TestInvertedOWG):
["Turtle Rock - Compass Chest", False, []],
["Turtle Rock - Compass Chest", False, [], ['Cane of Somaria']],
["Turtle Rock - Compass Chest", True, ['Pegasus Boots', 'Magic Mirror', 'Moon Pearl', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Pegasus Boots', 'Magic Mirror', 'Moon Pearl', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Pegasus Boots', 'Quake', 'Progressive Sword', 'Cane of Somaria']],
["Turtle Rock - Chain Chomps", False, []],

View File

@@ -49,7 +49,7 @@ class TestDeathMountain(TestMinor):
["Mimic Cave", False, [], ['Cane of Somaria']],
["Mimic Cave", False, ['Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']],
["Mimic Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Spiral Cave", False, []],
["Spiral Cave", False, [], ['Progressive Glove', 'Flute']],

View File

@@ -23,7 +23,7 @@ class GoalPyramidTest(PyramidTestBase):
}
def testCrystalsGoalAccess(self):
self.multiworld.goal[1] = "crystals"
self.multiworld.goal[1].value = 1 # crystals
self.assertFalse(self.can_reach_entrance("Pyramid Hole"))
self.collect_by_name(["Hammer", "Progressive Glove", "Moon Pearl"])
self.assertTrue(self.can_reach_entrance("Pyramid Hole"))

View File

@@ -13,7 +13,7 @@ class TestDungeons(TestVanillaOWG):
["Sewers - Secret Room - Left", False, []],
["Sewers - Secret Room - Left", True, ['Pegasus Boots', 'Progressive Glove']],
["Sewers - Secret Room - Left", True, ['Progressive Sword', 'Bomb Upgrade (+5)', 'Lamp', 'Small Key (Hyrule Castle)']],
["Sewers - Secret Room - Left", True, ['Bomb Upgrade (+5)', 'Lamp', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)', 'Small Key (Hyrule Castle)']],
["Eastern Palace - Compass Chest", True, []],
@@ -26,8 +26,8 @@ class TestDungeons(TestVanillaOWG):
["Desert Palace - Boss", False, [], ['Small Key (Desert Palace)']],
["Desert Palace - Boss", False, [], ['Big Key (Desert Palace)']],
["Desert Palace - Boss", False, [], ['Lamp', 'Fire Rod']],
["Desert Palace - Boss", True, ['Progressive Sword', 'Small Key (Desert Palace)', 'Pegasus Boots', 'Lamp', 'Big Key (Desert Palace)']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Pegasus Boots', 'Fire Rod', 'Big Key (Desert Palace)']],
["Desert Palace - Boss", True, ['Progressive Sword', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Pegasus Boots', 'Lamp', 'Big Key (Desert Palace)']],
["Desert Palace - Boss", True, ['Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Small Key (Desert Palace)', 'Pegasus Boots', 'Fire Rod', 'Big Key (Desert Palace)']],
["Tower of Hera - Basement Cage", False, []],
["Tower of Hera - Basement Cage", False, [], ['Pegasus Boots', "Flute", "Progressive Glove"]],
@@ -90,10 +90,10 @@ class TestDungeons(TestVanillaOWG):
["Ice Palace - Compass Chest", False, []],
["Ice Palace - Compass Chest", False, [], ['Fire Rod', 'Bombos']],
["Ice Palace - Compass Chest", False, [], ['Fire Rod', 'Progressive Sword']],
["Ice Palace - Compass Chest", True, ['Moon Pearl', 'Pegasus Boots', 'Flippers', 'Fire Rod']],
["Ice Palace - Compass Chest", True, ['Moon Pearl', 'Pegasus Boots', 'Flippers', 'Bombos', 'Progressive Sword']],
["Ice Palace - Compass Chest", True, ['Progressive Glove', 'Progressive Glove', 'Fire Rod']],
["Ice Palace - Compass Chest", True, ['Progressive Glove', 'Progressive Glove', 'Bombos', 'Progressive Sword']],
["Ice Palace - Compass Chest", True, ['Small Key (Ice Palace)', 'Moon Pearl', 'Pegasus Boots', 'Flippers', 'Fire Rod']],
["Ice Palace - Compass Chest", True, ['Small Key (Ice Palace)', 'Moon Pearl', 'Pegasus Boots', 'Flippers', 'Bombos', 'Progressive Sword']],
["Ice Palace - Compass Chest", True, ['Small Key (Ice Palace)', 'Progressive Glove', 'Progressive Glove', 'Fire Rod']],
["Ice Palace - Compass Chest", True, ['Small Key (Ice Palace)', 'Progressive Glove', 'Progressive Glove', 'Bombos', 'Progressive Sword']],
["Misery Mire - Bridge Chest", False, []],
["Misery Mire - Bridge Chest", False, [], ['Moon Pearl']],
@@ -105,9 +105,9 @@ class TestDungeons(TestVanillaOWG):
["Turtle Rock - Compass Chest", False, [], ['Cane of Somaria']],
#todo: does clip require sword?
#["Turtle Rock - Compass Chest", True, ['Moon Pearl', 'Pegasus Boots', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Moon Pearl', 'Pegasus Boots', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Progressive Sword']],
["Turtle Rock - Compass Chest", True, ['Moon Pearl', 'Pegasus Boots', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Progressive Sword']],
["Turtle Rock - Compass Chest", True, ['Moon Pearl', 'Pegasus Boots', 'Cane of Somaria', 'Progressive Sword', 'Quake']],
["Turtle Rock - Compass Chest", True, ['Pegasus Boots', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Compass Chest", True, ['Pegasus Boots', 'Magic Mirror', 'Cane of Somaria', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Turtle Rock - Chain Chomps", False, []],
#todo: does clip require sword?

View File

@@ -49,7 +49,7 @@ class TestDeathMountain(TestVanilla):
["Mimic Cave", False, [], ['Cane of Somaria']],
["Mimic Cave", False, ['Small Key (Turtle Rock)'], ['Small Key (Turtle Rock)']],
["Mimic Cave", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Mimic Cave", True, ['Bomb Upgrade (+5)', 'Quake', 'Progressive Sword', 'Flute', 'Progressive Glove', 'Progressive Glove', 'Hammer', 'Moon Pearl', 'Cane of Somaria', 'Magic Mirror', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)', 'Small Key (Turtle Rock)']],
["Spiral Cave", False, []],
["Spiral Cave", False, [], ['Progressive Glove', 'Flute']],

View File

@@ -15,7 +15,6 @@ Optional:
- Quick Prie Dieu warp mod: [GitHub](https://github.com/BadMagic100/Blasphemous-PrieWarp)
- Boots of Pleading mod: [GitHub](https://github.com/BrandenEK/Blasphemous-Boots-of-Pleading)
- Double Jump mod: [GitHub](https://github.com/BrandenEK/Blasphemous-Double-Jump)
- PopTracker pack: [GitHub](https://github.com/sassyvania/Blasphemous-Randomizer-Maptracker)
## Mod Installer (Recommended)

58
worlds/celeste64/Items.py Normal file
View File

@@ -0,0 +1,58 @@
from typing import Dict, NamedTuple, Optional
from BaseClasses import Item, ItemClassification
from .Names import ItemName
celeste_64_base_id: int = 0xCA0000
class Celeste64Item(Item):
game = "Celeste 64"
class Celeste64ItemData(NamedTuple):
code: Optional[int] = None
type: ItemClassification = ItemClassification.filler
item_data_table: Dict[str, Celeste64ItemData] = {
ItemName.strawberry: Celeste64ItemData(
code = celeste_64_base_id + 0,
type=ItemClassification.progression_skip_balancing,
),
ItemName.dash_refill: Celeste64ItemData(
code = celeste_64_base_id + 1,
type=ItemClassification.progression,
),
ItemName.double_dash_refill: Celeste64ItemData(
code = celeste_64_base_id + 2,
type=ItemClassification.progression,
),
ItemName.feather: Celeste64ItemData(
code = celeste_64_base_id + 3,
type=ItemClassification.progression,
),
ItemName.coin: Celeste64ItemData(
code = celeste_64_base_id + 4,
type=ItemClassification.progression,
),
ItemName.cassette: Celeste64ItemData(
code = celeste_64_base_id + 5,
type=ItemClassification.progression,
),
ItemName.traffic_block: Celeste64ItemData(
code = celeste_64_base_id + 6,
type=ItemClassification.progression,
),
ItemName.spring: Celeste64ItemData(
code = celeste_64_base_id + 7,
type=ItemClassification.progression,
),
ItemName.breakables: Celeste64ItemData(
code = celeste_64_base_id + 8,
type=ItemClassification.progression,
)
}
item_table = {name: data.code for name, data in item_data_table.items() if data.code is not None}

View File

@@ -0,0 +1,142 @@
from typing import Dict, NamedTuple, Optional
from BaseClasses import Location
from .Names import LocationName
celeste_64_base_id: int = 0xCA0000
class Celeste64Location(Location):
game = "Celeste 64"
class Celeste64LocationData(NamedTuple):
region: str
address: Optional[int] = None
location_data_table: Dict[str, Celeste64LocationData] = {
LocationName.strawberry_1 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 0,
),
LocationName.strawberry_2 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 1,
),
LocationName.strawberry_3 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 2,
),
LocationName.strawberry_4 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 3,
),
LocationName.strawberry_5 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 4,
),
LocationName.strawberry_6 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 5,
),
LocationName.strawberry_7 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 6,
),
LocationName.strawberry_8 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 7,
),
LocationName.strawberry_9 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 8,
),
LocationName.strawberry_10 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 9,
),
LocationName.strawberry_11 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 10,
),
LocationName.strawberry_12 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 11,
),
LocationName.strawberry_13 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 12,
),
LocationName.strawberry_14 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 13,
),
LocationName.strawberry_15 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 14,
),
LocationName.strawberry_16 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 15,
),
LocationName.strawberry_17 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 16,
),
LocationName.strawberry_18 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 17,
),
LocationName.strawberry_19 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 18,
),
LocationName.strawberry_20 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 19,
),
LocationName.strawberry_21 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 20,
),
LocationName.strawberry_22 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 21,
),
LocationName.strawberry_23 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 22,
),
LocationName.strawberry_24 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 23,
),
LocationName.strawberry_25 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 24,
),
LocationName.strawberry_26 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 25,
),
LocationName.strawberry_27 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 26,
),
LocationName.strawberry_28 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 27,
),
LocationName.strawberry_29 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 28,
),
LocationName.strawberry_30 : Celeste64LocationData(
region = "Forsaken City",
address = celeste_64_base_id + 29,
)
}
location_table = {name: data.address for name, data in location_data_table.items() if data.address is not None}

View File

@@ -0,0 +1,11 @@
strawberry = "Strawberry"
dash_refill = "Dash Refills"
double_dash_refill = "Double Dash Refills"
feather = "Feathers"
coin = "Coins"
cassette = "Cassettes"
traffic_block = "Traffic Blocks"
spring = "Springs"
breakables = "Breakable Blocks"

View File

@@ -0,0 +1,31 @@
# Strawberry Locations
strawberry_1 = "First Strawberry"
strawberry_2 = "Floating Blocks Strawberry"
strawberry_3 = "South-East Tower Top Strawberry"
strawberry_4 = "Theo Strawberry"
strawberry_5 = "Fall Through Spike Floor Strawberry"
strawberry_6 = "Troll Strawberry"
strawberry_7 = "Falling Blocks Strawberry"
strawberry_8 = "Traffic Block Strawberry"
strawberry_9 = "South-West Dash Refills Strawberry"
strawberry_10 = "South-East Tower Side Strawberry"
strawberry_11 = "Girders Strawberry"
strawberry_12 = "North-East Tower Bottom Strawberry"
strawberry_13 = "Breakable Blocks Strawberry"
strawberry_14 = "Feather Maze Strawberry"
strawberry_15 = "Feather Chain Strawberry"
strawberry_16 = "Feather Hidden Strawberry"
strawberry_17 = "Double Dash Puzzle Strawberry"
strawberry_18 = "Double Dash Spike Climb Strawberry"
strawberry_19 = "Double Dash Spring Strawberry"
strawberry_20 = "North-East Tower Breakable Bottom Strawberry"
strawberry_21 = "Theo Tower Lower Cassette Strawberry"
strawberry_22 = "Theo Tower Upper Cassette Strawberry"
strawberry_23 = "South End of Bridge Cassette Strawberry"
strawberry_24 = "You Are Ready Cassette Strawberry"
strawberry_25 = "Cassette Hidden in the House Strawberry"
strawberry_26 = "North End of Bridge Cassette Strawberry"
strawberry_27 = "Distant Feather Cassette Strawberry"
strawberry_28 = "Feather Arches Cassette Strawberry"
strawberry_29 = "North-East Tower Cassette Strawberry"
strawberry_30 = "Badeline Cassette Strawberry"

View File

View File

@@ -0,0 +1,25 @@
from dataclasses import dataclass
from Options import Range, DeathLink, PerGameCommonOptions
class StrawberriesRequired(Range):
"""How many Strawberries you must receive to finish"""
display_name = "Strawberries Required"
range_start = 0
range_end = 20
default = 15
class DeathLinkAmnesty(Range):
"""How many deaths it takes to send a DeathLink"""
display_name = "Death Link Amnesty"
range_start = 1
range_end = 30
default = 10
@dataclass
class Celeste64Options(PerGameCommonOptions):
death_link: DeathLink
death_link_amnesty: DeathLinkAmnesty
strawberries_required: StrawberriesRequired

View File

@@ -0,0 +1,11 @@
from typing import Dict, List, NamedTuple
class Celeste64RegionData(NamedTuple):
connecting_regions: List[str] = []
region_data_table: Dict[str, Celeste64RegionData] = {
"Menu": Celeste64RegionData(["Forsaken City"]),
"Forsaken City": Celeste64RegionData(),
}

104
worlds/celeste64/Rules.py Normal file
View File

@@ -0,0 +1,104 @@
from worlds.generic.Rules import set_rule
from . import Celeste64World
from .Names import ItemName, LocationName
def set_rules(world: Celeste64World):
set_rule(world.multiworld.get_location(LocationName.strawberry_4, world.player),
lambda state: state.has_all({ItemName.traffic_block,
ItemName.breakables}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_5, world.player),
lambda state: state.has(ItemName.breakables, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_6, world.player),
lambda state: state.has(ItemName.dash_refill, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_8, world.player),
lambda state: state.has(ItemName.traffic_block, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_9, world.player),
lambda state: state.has(ItemName.dash_refill, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_11, world.player),
lambda state: state.has(ItemName.dash_refill, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_12, world.player),
lambda state: state.has_all({ItemName.dash_refill,
ItemName.double_dash_refill}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_13, world.player),
lambda state: state.has_all({ItemName.dash_refill,
ItemName.breakables}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_14, world.player),
lambda state: state.has_all({ItemName.dash_refill,
ItemName.feather}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_15, world.player),
lambda state: state.has_all({ItemName.dash_refill,
ItemName.feather}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_16, world.player),
lambda state: state.has_all({ItemName.dash_refill,
ItemName.feather}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_17, world.player),
lambda state: state.has_all({ItemName.dash_refill,
ItemName.double_dash_refill,
ItemName.feather,
ItemName.traffic_block}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_18, world.player),
lambda state: state.has(ItemName.double_dash_refill, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_19, world.player),
lambda state: state.has_all({ItemName.double_dash_refill,
ItemName.spring}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_20, world.player),
lambda state: state.has_all({ItemName.dash_refill,
ItemName.feather,
ItemName.breakables}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_21, world.player),
lambda state: state.has_all({ItemName.cassette,
ItemName.traffic_block,
ItemName.breakables}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_22, world.player),
lambda state: state.has_all({ItemName.cassette,
ItemName.dash_refill,
ItemName.breakables}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_23, world.player),
lambda state: state.has_all({ItemName.cassette,
ItemName.dash_refill,
ItemName.coin}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_24, world.player),
lambda state: state.has_all({ItemName.cassette,
ItemName.traffic_block,
ItemName.dash_refill}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_25, world.player),
lambda state: state.has_all({ItemName.cassette,
ItemName.dash_refill,
ItemName.double_dash_refill}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_26, world.player),
lambda state: state.has_all({ItemName.cassette,
ItemName.dash_refill}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_27, world.player),
lambda state: state.has_all({ItemName.cassette,
ItemName.feather,
ItemName.coin,
ItemName.dash_refill}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_28, world.player),
lambda state: state.has_all({ItemName.cassette,
ItemName.feather,
ItemName.coin,
ItemName.dash_refill}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_29, world.player),
lambda state: state.has_all({ItemName.cassette,
ItemName.feather,
ItemName.coin,
ItemName.dash_refill}, world.player))
set_rule(world.multiworld.get_location(LocationName.strawberry_30, world.player),
lambda state: state.has_all({ItemName.cassette,
ItemName.feather,
ItemName.traffic_block,
ItemName.spring,
ItemName.breakables,
ItemName.dash_refill,
ItemName.double_dash_refill}, world.player))
# Completion condition.
world.multiworld.completion_condition[world.player] = lambda state: (state.has(ItemName.strawberry,world.player,world.options.strawberries_required.value) and
state.has_all({ItemName.feather,
ItemName.traffic_block,
ItemName.breakables,
ItemName.dash_refill,
ItemName.double_dash_refill}, world.player))

View File

@@ -0,0 +1,92 @@
from typing import List
from BaseClasses import ItemClassification, Region, Tutorial
from worlds.AutoWorld import WebWorld, World
from .Items import Celeste64Item, item_data_table, item_table
from .Locations import Celeste64Location, location_data_table, location_table
from .Names import ItemName
from .Options import Celeste64Options
class Celeste64WebWorld(WebWorld):
theme = "ice"
setup_en = Tutorial(
tutorial_name="Start Guide",
description="A guide to playing Celeste 64 in Archipelago.",
language="English",
file_name="guide_en.md",
link="guide/en",
authors=["PoryGone"]
)
tutorials = [setup_en]
class Celeste64World(World):
"""Relive the magic of Celeste Mountain alongside Madeline in this small, heartfelt 3D platformer.
Created in a week(ish) by the Celeste team to celebrate the games sixth anniversary 🍓✨"""
game = "Celeste 64"
web = Celeste64WebWorld()
options_dataclass = Celeste64Options
options: Celeste64Options
location_name_to_id = location_table
item_name_to_id = item_table
def create_item(self, name: str) -> Celeste64Item:
# Only make required amount of strawberries be Progression
if getattr(self, "options", None) and name == ItemName.strawberry:
classification: ItemClassification = ItemClassification.filler
self.prog_strawberries = getattr(self, "prog_strawberries", 0)
if self.prog_strawberries < self.options.strawberries_required.value:
classification = ItemClassification.progression_skip_balancing
self.prog_strawberries += 1
return Celeste64Item(name, classification, item_data_table[name].code, self.player)
else:
return Celeste64Item(name, item_data_table[name].type, item_data_table[name].code, self.player)
def create_items(self) -> None:
item_pool: List[Celeste64Item] = []
item_pool += [self.create_item(name) for name in item_data_table.keys()]
item_pool += [self.create_item(ItemName.strawberry) for _ in range(21)]
self.multiworld.itempool += item_pool
def create_regions(self) -> None:
from .Regions import region_data_table
# Create regions.
for region_name in region_data_table.keys():
region = Region(region_name, self.player, self.multiworld)
self.multiworld.regions.append(region)
# Create locations.
for region_name, region_data in region_data_table.items():
region = self.multiworld.get_region(region_name, self.player)
region.add_locations({
location_name: location_data.address for location_name, location_data in location_data_table.items()
if location_data.region == region_name
}, Celeste64Location)
region.add_exits(region_data_table[region_name].connecting_regions)
def get_filler_item_name(self) -> str:
return ItemName.strawberry
def set_rules(self) -> None:
from .Rules import set_rules
set_rules(self)
def fill_slot_data(self):
return {
"death_link": self.options.death_link.value,
"death_link_amnesty": self.options.death_link_amnesty.value,
"strawberries_required": self.options.strawberries_required.value
}

View File

@@ -0,0 +1,24 @@
# Celeste 64
## What is this game?
Relive the magic of Celeste Mountain alongside Madeline in this small, heartfelt 3D platformer.
Created in a week(ish) by the Celeste team to celebrate the game's sixth anniversary.
Ported to Archipelago in a week(ish) by PoryGone, this World provides the following as unlockable items:
- Strawberries
- Dash Refills
- Double Dash Refills
- Feathers
- Coins
- Cassettes
- Traffic Blocks
- Springs
- Breakable Blocks
The goal is to collect a certain number of Strawberries, then visit Badeline on her floating island.
## 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.

View File

@@ -0,0 +1,32 @@
# Celeste 64 Setup Guide
## Required Software
- Archipelago Build of Celeste 64 from: [Celeste 64 Archipelago Releases Page](https://github.com/PoryGoneDev/Celeste64/releases/)
## Installation Procedures (Windows)
1. Download the above release and extract it.
## Joining a MultiWorld Game
1. Before launching the game, edit the `AP.json` file in the root of the Celeste 64 install.
2. For the `Url` field, enter the address of the server, such as `archipelago.gg:38281`. Your server host should be able to tell you this.
3. For the `SlotName` field, enter your "name" field from the yaml or website config.
4. For the `Password` field, enter the server password if one exists; otherwise leave this field blank.
5. Save the file, and run `Celeste64.exe`. If you can continue past the title screen, then you are successfully connected.
An Example `AP.json` file:
```
{
"Url": "archipelago:12345",
"SlotName": "Maddy",
"Password": ""
}
```

View File

@@ -14,6 +14,7 @@ from .Options import RandomizeWeaponLevelOption, PoolTypeOption, EarlySmallLothr
class DarkSouls3Web(WebWorld):
bug_report_page = "https://github.com/Marechal-L/Dark-Souls-III-Archipelago-client/issues"
theme = "stone"
setup_en = Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the Archipelago Dark Souls III randomizer on your computer.",

View File

@@ -11,7 +11,7 @@
## General Concept
<span style="color:tomato">
<span style="color:#ff7800">
**This mod can ban you permanently from the FromSoftware servers if used online.**
</span>
The Dark Souls III AP Client is a dinput8.dll triggered when launching Dark Souls III. This .dll file will launch a command

View File

@@ -12,7 +12,7 @@ permettant de lire des informations de la partie et écrire des commandes pour i
## Procédures d'installation
<span style="color:tomato">
<span style="color:#ff7800">
**Il y a des risques de bannissement permanent des serveurs FromSoftware si ce mod est utilisé en ligne.**
</span>
Ce client a été testé sur la version Steam officielle du jeu (v1.15/1.35), peu importe les DLCs actuellement installés.

View File

@@ -2,6 +2,7 @@ import typing
from BaseClasses import Location
from .Names import LocationName
from worlds.AutoWorld import World
class DKC3Location(Location):
@@ -321,13 +322,13 @@ all_locations = {
location_table = {}
def setup_locations(world, player: int):
def setup_locations(world: World):
location_table = {**level_location_table, **boss_location_table, **secret_cave_location_table}
if False:#world.include_trade_sequence[player].value:
if False:#world.options.include_trade_sequence:
location_table.update({**brothers_bear_location_table})
if world.kongsanity[player].value:
if world.options.kongsanity:
location_table.update({**kong_location_table})
return location_table

View File

@@ -1,6 +1,7 @@
from dataclasses import dataclass
import typing
from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionList
from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionList, PerGameCommonOptions
class Goal(Choice):
@@ -158,21 +159,21 @@ class StartingLifeCount(Range):
default = 5
dkc3_options: typing.Dict[str, type(Option)] = {
#"death_link": DeathLink, # Disabled
"goal": Goal,
#"include_trade_sequence": IncludeTradeSequence, # Disabled
"dk_coins_for_gyrocopter": DKCoinsForGyrocopter,
"krematoa_bonus_coin_cost": KrematoaBonusCoinCost,
"percentage_of_extra_bonus_coins": PercentageOfExtraBonusCoins,
"number_of_banana_birds": NumberOfBananaBirds,
"percentage_of_banana_birds": PercentageOfBananaBirds,
"kongsanity": KONGsanity,
"level_shuffle": LevelShuffle,
"difficulty": Difficulty,
"autosave": Autosave,
"merry": MERRY,
"music_shuffle": MusicShuffle,
"kong_palette_swap": KongPaletteSwap,
"starting_life_count": StartingLifeCount,
}
@dataclass
class DKC3Options(PerGameCommonOptions):
#death_link: DeathLink # Disabled
goal: Goal
#include_trade_sequence: IncludeTradeSequence # Disabled
dk_coins_for_gyrocopter: DKCoinsForGyrocopter
krematoa_bonus_coin_cost: KrematoaBonusCoinCost
percentage_of_extra_bonus_coins: PercentageOfExtraBonusCoins
number_of_banana_birds: NumberOfBananaBirds
percentage_of_banana_birds: PercentageOfBananaBirds
kongsanity: KONGsanity
level_shuffle: LevelShuffle
difficulty: Difficulty
autosave: Autosave
merry: MERRY
music_shuffle: MusicShuffle
kong_palette_swap: KongPaletteSwap
starting_life_count: StartingLifeCount

View File

@@ -4,38 +4,39 @@ from BaseClasses import MultiWorld, Region, Entrance
from .Items import DKC3Item
from .Locations import DKC3Location
from .Names import LocationName, ItemName
from worlds.AutoWorld import World
def create_regions(world, player: int, active_locations):
menu_region = create_region(world, player, active_locations, 'Menu', None)
def create_regions(world: World, active_locations):
menu_region = create_region(world, active_locations, 'Menu', None)
overworld_1_region_locations = {}
if world.goal[player] != "knautilus":
if world.options.goal != "knautilus":
overworld_1_region_locations.update({LocationName.banana_bird_mother: []})
overworld_1_region = create_region(world, player, active_locations, LocationName.overworld_1_region,
overworld_1_region = create_region(world, active_locations, LocationName.overworld_1_region,
overworld_1_region_locations)
overworld_2_region_locations = {}
overworld_2_region = create_region(world, player, active_locations, LocationName.overworld_2_region,
overworld_2_region = create_region(world, active_locations, LocationName.overworld_2_region,
overworld_2_region_locations)
overworld_3_region_locations = {}
overworld_3_region = create_region(world, player, active_locations, LocationName.overworld_3_region,
overworld_3_region = create_region(world, active_locations, LocationName.overworld_3_region,
overworld_3_region_locations)
overworld_4_region_locations = {}
overworld_4_region = create_region(world, player, active_locations, LocationName.overworld_4_region,
overworld_4_region = create_region(world, active_locations, LocationName.overworld_4_region,
overworld_4_region_locations)
lake_orangatanga_region = create_region(world, player, active_locations, LocationName.lake_orangatanga_region, None)
kremwood_forest_region = create_region(world, player, active_locations, LocationName.kremwood_forest_region, None)
cotton_top_cove_region = create_region(world, player, active_locations, LocationName.cotton_top_cove_region, None)
mekanos_region = create_region(world, player, active_locations, LocationName.mekanos_region, None)
k3_region = create_region(world, player, active_locations, LocationName.k3_region, None)
razor_ridge_region = create_region(world, player, active_locations, LocationName.razor_ridge_region, None)
kaos_kore_region = create_region(world, player, active_locations, LocationName.kaos_kore_region, None)
krematoa_region = create_region(world, player, active_locations, LocationName.krematoa_region, None)
lake_orangatanga_region = create_region(world, active_locations, LocationName.lake_orangatanga_region, None)
kremwood_forest_region = create_region(world, active_locations, LocationName.kremwood_forest_region, None)
cotton_top_cove_region = create_region(world, active_locations, LocationName.cotton_top_cove_region, None)
mekanos_region = create_region(world, active_locations, LocationName.mekanos_region, None)
k3_region = create_region(world, active_locations, LocationName.k3_region, None)
razor_ridge_region = create_region(world, active_locations, LocationName.razor_ridge_region, None)
kaos_kore_region = create_region(world, active_locations, LocationName.kaos_kore_region, None)
krematoa_region = create_region(world, active_locations, LocationName.krematoa_region, None)
lakeside_limbo_region_locations = {
@@ -44,9 +45,9 @@ def create_regions(world, player: int, active_locations):
LocationName.lakeside_limbo_bonus_2 : [0x657, 3],
LocationName.lakeside_limbo_dk : [0x657, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
lakeside_limbo_region_locations[LocationName.lakeside_limbo_kong] = []
lakeside_limbo_region = create_region(world, player, active_locations, LocationName.lakeside_limbo_region,
lakeside_limbo_region = create_region(world, active_locations, LocationName.lakeside_limbo_region,
lakeside_limbo_region_locations)
doorstop_dash_region_locations = {
@@ -55,9 +56,9 @@ def create_regions(world, player: int, active_locations):
LocationName.doorstop_dash_bonus_2 : [0x65A, 3],
LocationName.doorstop_dash_dk : [0x65A, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
doorstop_dash_region_locations[LocationName.doorstop_dash_kong] = []
doorstop_dash_region = create_region(world, player, active_locations, LocationName.doorstop_dash_region,
doorstop_dash_region = create_region(world, active_locations, LocationName.doorstop_dash_region,
doorstop_dash_region_locations)
tidal_trouble_region_locations = {
@@ -66,9 +67,9 @@ def create_regions(world, player: int, active_locations):
LocationName.tidal_trouble_bonus_2 : [0x659, 3],
LocationName.tidal_trouble_dk : [0x659, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
tidal_trouble_region_locations[LocationName.tidal_trouble_kong] = []
tidal_trouble_region = create_region(world, player, active_locations, LocationName.tidal_trouble_region,
tidal_trouble_region = create_region(world, active_locations, LocationName.tidal_trouble_region,
tidal_trouble_region_locations)
skiddas_row_region_locations = {
@@ -77,9 +78,9 @@ def create_regions(world, player: int, active_locations):
LocationName.skiddas_row_bonus_2 : [0x65D, 3],
LocationName.skiddas_row_dk : [0x65D, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
skiddas_row_region_locations[LocationName.skiddas_row_kong] = []
skiddas_row_region = create_region(world, player, active_locations, LocationName.skiddas_row_region,
skiddas_row_region = create_region(world, active_locations, LocationName.skiddas_row_region,
skiddas_row_region_locations)
murky_mill_region_locations = {
@@ -88,9 +89,9 @@ def create_regions(world, player: int, active_locations):
LocationName.murky_mill_bonus_2 : [0x65C, 3],
LocationName.murky_mill_dk : [0x65C, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
murky_mill_region_locations[LocationName.murky_mill_kong] = []
murky_mill_region = create_region(world, player, active_locations, LocationName.murky_mill_region,
murky_mill_region = create_region(world, active_locations, LocationName.murky_mill_region,
murky_mill_region_locations)
barrel_shield_bust_up_region_locations = {
@@ -99,9 +100,9 @@ def create_regions(world, player: int, active_locations):
LocationName.barrel_shield_bust_up_bonus_2 : [0x662, 3],
LocationName.barrel_shield_bust_up_dk : [0x662, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
barrel_shield_bust_up_region_locations[LocationName.barrel_shield_bust_up_kong] = []
barrel_shield_bust_up_region = create_region(world, player, active_locations,
barrel_shield_bust_up_region = create_region(world, active_locations,
LocationName.barrel_shield_bust_up_region,
barrel_shield_bust_up_region_locations)
@@ -111,9 +112,9 @@ def create_regions(world, player: int, active_locations):
LocationName.riverside_race_bonus_2 : [0x664, 3],
LocationName.riverside_race_dk : [0x664, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
riverside_race_region_locations[LocationName.riverside_race_kong] = []
riverside_race_region = create_region(world, player, active_locations, LocationName.riverside_race_region,
riverside_race_region = create_region(world, active_locations, LocationName.riverside_race_region,
riverside_race_region_locations)
squeals_on_wheels_region_locations = {
@@ -122,9 +123,9 @@ def create_regions(world, player: int, active_locations):
LocationName.squeals_on_wheels_bonus_2 : [0x65B, 3],
LocationName.squeals_on_wheels_dk : [0x65B, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
squeals_on_wheels_region_locations[LocationName.squeals_on_wheels_kong] = []
squeals_on_wheels_region = create_region(world, player, active_locations, LocationName.squeals_on_wheels_region,
squeals_on_wheels_region = create_region(world, active_locations, LocationName.squeals_on_wheels_region,
squeals_on_wheels_region_locations)
springin_spiders_region_locations = {
@@ -133,9 +134,9 @@ def create_regions(world, player: int, active_locations):
LocationName.springin_spiders_bonus_2 : [0x661, 3],
LocationName.springin_spiders_dk : [0x661, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
springin_spiders_region_locations[LocationName.springin_spiders_kong] = []
springin_spiders_region = create_region(world, player, active_locations, LocationName.springin_spiders_region,
springin_spiders_region = create_region(world, active_locations, LocationName.springin_spiders_region,
springin_spiders_region_locations)
bobbing_barrel_brawl_region_locations = {
@@ -144,9 +145,9 @@ def create_regions(world, player: int, active_locations):
LocationName.bobbing_barrel_brawl_bonus_2 : [0x666, 3],
LocationName.bobbing_barrel_brawl_dk : [0x666, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
bobbing_barrel_brawl_region_locations[LocationName.bobbing_barrel_brawl_kong] = []
bobbing_barrel_brawl_region = create_region(world, player, active_locations,
bobbing_barrel_brawl_region = create_region(world, active_locations,
LocationName.bobbing_barrel_brawl_region,
bobbing_barrel_brawl_region_locations)
@@ -156,9 +157,9 @@ def create_regions(world, player: int, active_locations):
LocationName.bazzas_blockade_bonus_2 : [0x667, 3],
LocationName.bazzas_blockade_dk : [0x667, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
bazzas_blockade_region_locations[LocationName.bazzas_blockade_kong] = []
bazzas_blockade_region = create_region(world, player, active_locations, LocationName.bazzas_blockade_region,
bazzas_blockade_region = create_region(world, active_locations, LocationName.bazzas_blockade_region,
bazzas_blockade_region_locations)
rocket_barrel_ride_region_locations = {
@@ -167,9 +168,9 @@ def create_regions(world, player: int, active_locations):
LocationName.rocket_barrel_ride_bonus_2 : [0x66A, 3],
LocationName.rocket_barrel_ride_dk : [0x66A, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
rocket_barrel_ride_region_locations[LocationName.rocket_barrel_ride_kong] = []
rocket_barrel_ride_region = create_region(world, player, active_locations, LocationName.rocket_barrel_ride_region,
rocket_barrel_ride_region = create_region(world, active_locations, LocationName.rocket_barrel_ride_region,
rocket_barrel_ride_region_locations)
kreeping_klasps_region_locations = {
@@ -178,9 +179,9 @@ def create_regions(world, player: int, active_locations):
LocationName.kreeping_klasps_bonus_2 : [0x658, 3],
LocationName.kreeping_klasps_dk : [0x658, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
kreeping_klasps_region_locations[LocationName.kreeping_klasps_kong] = []
kreeping_klasps_region = create_region(world, player, active_locations, LocationName.kreeping_klasps_region,
kreeping_klasps_region = create_region(world, active_locations, LocationName.kreeping_klasps_region,
kreeping_klasps_region_locations)
tracker_barrel_trek_region_locations = {
@@ -189,9 +190,9 @@ def create_regions(world, player: int, active_locations):
LocationName.tracker_barrel_trek_bonus_2 : [0x66B, 3],
LocationName.tracker_barrel_trek_dk : [0x66B, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
tracker_barrel_trek_region_locations[LocationName.tracker_barrel_trek_kong] = []
tracker_barrel_trek_region = create_region(world, player, active_locations, LocationName.tracker_barrel_trek_region,
tracker_barrel_trek_region = create_region(world, active_locations, LocationName.tracker_barrel_trek_region,
tracker_barrel_trek_region_locations)
fish_food_frenzy_region_locations = {
@@ -200,9 +201,9 @@ def create_regions(world, player: int, active_locations):
LocationName.fish_food_frenzy_bonus_2 : [0x668, 3],
LocationName.fish_food_frenzy_dk : [0x668, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
fish_food_frenzy_region_locations[LocationName.fish_food_frenzy_kong] = []
fish_food_frenzy_region = create_region(world, player, active_locations, LocationName.fish_food_frenzy_region,
fish_food_frenzy_region = create_region(world, active_locations, LocationName.fish_food_frenzy_region,
fish_food_frenzy_region_locations)
fire_ball_frenzy_region_locations = {
@@ -211,9 +212,9 @@ def create_regions(world, player: int, active_locations):
LocationName.fire_ball_frenzy_bonus_2 : [0x66D, 3],
LocationName.fire_ball_frenzy_dk : [0x66D, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
fire_ball_frenzy_region_locations[LocationName.fire_ball_frenzy_kong] = []
fire_ball_frenzy_region = create_region(world, player, active_locations, LocationName.fire_ball_frenzy_region,
fire_ball_frenzy_region = create_region(world, active_locations, LocationName.fire_ball_frenzy_region,
fire_ball_frenzy_region_locations)
demolition_drain_pipe_region_locations = {
@@ -222,9 +223,9 @@ def create_regions(world, player: int, active_locations):
LocationName.demolition_drain_pipe_bonus_2 : [0x672, 3],
LocationName.demolition_drain_pipe_dk : [0x672, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
demolition_drain_pipe_region_locations[LocationName.demolition_drain_pipe_kong] = []
demolition_drain_pipe_region = create_region(world, player, active_locations,
demolition_drain_pipe_region = create_region(world, active_locations,
LocationName.demolition_drain_pipe_region,
demolition_drain_pipe_region_locations)
@@ -234,9 +235,9 @@ def create_regions(world, player: int, active_locations):
LocationName.ripsaw_rage_bonus_2 : [0x660, 3],
LocationName.ripsaw_rage_dk : [0x660, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
ripsaw_rage_region_locations[LocationName.ripsaw_rage_kong] = []
ripsaw_rage_region = create_region(world, player, active_locations, LocationName.ripsaw_rage_region,
ripsaw_rage_region = create_region(world, active_locations, LocationName.ripsaw_rage_region,
ripsaw_rage_region_locations)
blazing_bazookas_region_locations = {
@@ -245,9 +246,9 @@ def create_regions(world, player: int, active_locations):
LocationName.blazing_bazookas_bonus_2 : [0x66E, 3],
LocationName.blazing_bazookas_dk : [0x66E, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
blazing_bazookas_region_locations[LocationName.blazing_bazookas_kong] = []
blazing_bazookas_region = create_region(world, player, active_locations, LocationName.blazing_bazookas_region,
blazing_bazookas_region = create_region(world, active_locations, LocationName.blazing_bazookas_region,
blazing_bazookas_region_locations)
low_g_labyrinth_region_locations = {
@@ -256,9 +257,9 @@ def create_regions(world, player: int, active_locations):
LocationName.low_g_labyrinth_bonus_2 : [0x670, 3],
LocationName.low_g_labyrinth_dk : [0x670, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
low_g_labyrinth_region_locations[LocationName.low_g_labyrinth_kong] = []
low_g_labyrinth_region = create_region(world, player, active_locations, LocationName.low_g_labyrinth_region,
low_g_labyrinth_region = create_region(world, active_locations, LocationName.low_g_labyrinth_region,
low_g_labyrinth_region_locations)
krevice_kreepers_region_locations = {
@@ -267,9 +268,9 @@ def create_regions(world, player: int, active_locations):
LocationName.krevice_kreepers_bonus_2 : [0x673, 3],
LocationName.krevice_kreepers_dk : [0x673, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
krevice_kreepers_region_locations[LocationName.krevice_kreepers_kong] = []
krevice_kreepers_region = create_region(world, player, active_locations, LocationName.krevice_kreepers_region,
krevice_kreepers_region = create_region(world, active_locations, LocationName.krevice_kreepers_region,
krevice_kreepers_region_locations)
tearaway_toboggan_region_locations = {
@@ -278,9 +279,9 @@ def create_regions(world, player: int, active_locations):
LocationName.tearaway_toboggan_bonus_2 : [0x65F, 3],
LocationName.tearaway_toboggan_dk : [0x65F, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
tearaway_toboggan_region_locations[LocationName.tearaway_toboggan_kong] = []
tearaway_toboggan_region = create_region(world, player, active_locations, LocationName.tearaway_toboggan_region,
tearaway_toboggan_region = create_region(world, active_locations, LocationName.tearaway_toboggan_region,
tearaway_toboggan_region_locations)
barrel_drop_bounce_region_locations = {
@@ -289,9 +290,9 @@ def create_regions(world, player: int, active_locations):
LocationName.barrel_drop_bounce_bonus_2 : [0x66C, 3],
LocationName.barrel_drop_bounce_dk : [0x66C, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
barrel_drop_bounce_region_locations[LocationName.barrel_drop_bounce_kong] = []
barrel_drop_bounce_region = create_region(world, player, active_locations, LocationName.barrel_drop_bounce_region,
barrel_drop_bounce_region = create_region(world, active_locations, LocationName.barrel_drop_bounce_region,
barrel_drop_bounce_region_locations)
krack_shot_kroc_region_locations = {
@@ -300,9 +301,9 @@ def create_regions(world, player: int, active_locations):
LocationName.krack_shot_kroc_bonus_2 : [0x66F, 3],
LocationName.krack_shot_kroc_dk : [0x66F, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
krack_shot_kroc_region_locations[LocationName.krack_shot_kroc_kong] = []
krack_shot_kroc_region = create_region(world, player, active_locations, LocationName.krack_shot_kroc_region,
krack_shot_kroc_region = create_region(world, active_locations, LocationName.krack_shot_kroc_region,
krack_shot_kroc_region_locations)
lemguin_lunge_region_locations = {
@@ -311,9 +312,9 @@ def create_regions(world, player: int, active_locations):
LocationName.lemguin_lunge_bonus_2 : [0x65E, 3],
LocationName.lemguin_lunge_dk : [0x65E, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
lemguin_lunge_region_locations[LocationName.lemguin_lunge_kong] = []
lemguin_lunge_region = create_region(world, player, active_locations, LocationName.lemguin_lunge_region,
lemguin_lunge_region = create_region(world, active_locations, LocationName.lemguin_lunge_region,
lemguin_lunge_region_locations)
buzzer_barrage_region_locations = {
@@ -322,9 +323,9 @@ def create_regions(world, player: int, active_locations):
LocationName.buzzer_barrage_bonus_2 : [0x676, 3],
LocationName.buzzer_barrage_dk : [0x676, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
buzzer_barrage_region_locations[LocationName.buzzer_barrage_kong] = []
buzzer_barrage_region = create_region(world, player, active_locations, LocationName.buzzer_barrage_region,
buzzer_barrage_region = create_region(world, active_locations, LocationName.buzzer_barrage_region,
buzzer_barrage_region_locations)
kong_fused_cliffs_region_locations = {
@@ -333,9 +334,9 @@ def create_regions(world, player: int, active_locations):
LocationName.kong_fused_cliffs_bonus_2 : [0x674, 3],
LocationName.kong_fused_cliffs_dk : [0x674, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
kong_fused_cliffs_region_locations[LocationName.kong_fused_cliffs_kong] = []
kong_fused_cliffs_region = create_region(world, player, active_locations, LocationName.kong_fused_cliffs_region,
kong_fused_cliffs_region = create_region(world, active_locations, LocationName.kong_fused_cliffs_region,
kong_fused_cliffs_region_locations)
floodlit_fish_region_locations = {
@@ -344,9 +345,9 @@ def create_regions(world, player: int, active_locations):
LocationName.floodlit_fish_bonus_2 : [0x669, 3],
LocationName.floodlit_fish_dk : [0x669, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
floodlit_fish_region_locations[LocationName.floodlit_fish_kong] = []
floodlit_fish_region = create_region(world, player, active_locations, LocationName.floodlit_fish_region,
floodlit_fish_region = create_region(world, active_locations, LocationName.floodlit_fish_region,
floodlit_fish_region_locations)
pothole_panic_region_locations = {
@@ -355,9 +356,9 @@ def create_regions(world, player: int, active_locations):
LocationName.pothole_panic_bonus_2 : [0x677, 3],
LocationName.pothole_panic_dk : [0x677, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
pothole_panic_region_locations[LocationName.pothole_panic_kong] = []
pothole_panic_region = create_region(world, player, active_locations, LocationName.pothole_panic_region,
pothole_panic_region = create_region(world, active_locations, LocationName.pothole_panic_region,
pothole_panic_region_locations)
ropey_rumpus_region_locations = {
@@ -366,9 +367,9 @@ def create_regions(world, player: int, active_locations):
LocationName.ropey_rumpus_bonus_2 : [0x675, 3],
LocationName.ropey_rumpus_dk : [0x675, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
ropey_rumpus_region_locations[LocationName.ropey_rumpus_kong] = []
ropey_rumpus_region = create_region(world, player, active_locations, LocationName.ropey_rumpus_region,
ropey_rumpus_region = create_region(world, active_locations, LocationName.ropey_rumpus_region,
ropey_rumpus_region_locations)
konveyor_rope_clash_region_locations = {
@@ -377,9 +378,9 @@ def create_regions(world, player: int, active_locations):
LocationName.konveyor_rope_clash_bonus_2 : [0x657, 3],
LocationName.konveyor_rope_clash_dk : [0x657, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
konveyor_rope_clash_region_locations[LocationName.konveyor_rope_clash_kong] = []
konveyor_rope_clash_region = create_region(world, player, active_locations, LocationName.konveyor_rope_clash_region,
konveyor_rope_clash_region = create_region(world, active_locations, LocationName.konveyor_rope_clash_region,
konveyor_rope_clash_region_locations)
creepy_caverns_region_locations = {
@@ -388,9 +389,9 @@ def create_regions(world, player: int, active_locations):
LocationName.creepy_caverns_bonus_2 : [0x678, 3],
LocationName.creepy_caverns_dk : [0x678, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
creepy_caverns_region_locations[LocationName.creepy_caverns_kong] = []
creepy_caverns_region = create_region(world, player, active_locations, LocationName.creepy_caverns_region,
creepy_caverns_region = create_region(world, active_locations, LocationName.creepy_caverns_region,
creepy_caverns_region_locations)
lightning_lookout_region_locations = {
@@ -399,9 +400,9 @@ def create_regions(world, player: int, active_locations):
LocationName.lightning_lookout_bonus_2 : [0x665, 3],
LocationName.lightning_lookout_dk : [0x665, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
lightning_lookout_region_locations[LocationName.lightning_lookout_kong] = []
lightning_lookout_region = create_region(world, player, active_locations, LocationName.lightning_lookout_region,
lightning_lookout_region = create_region(world, active_locations, LocationName.lightning_lookout_region,
lightning_lookout_region_locations)
koindozer_klamber_region_locations = {
@@ -410,9 +411,9 @@ def create_regions(world, player: int, active_locations):
LocationName.koindozer_klamber_bonus_2 : [0x679, 3],
LocationName.koindozer_klamber_dk : [0x679, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
koindozer_klamber_region_locations[LocationName.koindozer_klamber_kong] = []
koindozer_klamber_region = create_region(world, player, active_locations, LocationName.koindozer_klamber_region,
koindozer_klamber_region = create_region(world, active_locations, LocationName.koindozer_klamber_region,
koindozer_klamber_region_locations)
poisonous_pipeline_region_locations = {
@@ -421,9 +422,9 @@ def create_regions(world, player: int, active_locations):
LocationName.poisonous_pipeline_bonus_2 : [0x671, 3],
LocationName.poisonous_pipeline_dk : [0x671, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
poisonous_pipeline_region_locations[LocationName.poisonous_pipeline_kong] = []
poisonous_pipeline_region = create_region(world, player, active_locations, LocationName.poisonous_pipeline_region,
poisonous_pipeline_region = create_region(world, active_locations, LocationName.poisonous_pipeline_region,
poisonous_pipeline_region_locations)
stampede_sprint_region_locations = {
@@ -433,9 +434,9 @@ def create_regions(world, player: int, active_locations):
LocationName.stampede_sprint_bonus_3 : [0x67B, 4],
LocationName.stampede_sprint_dk : [0x67B, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
stampede_sprint_region_locations[LocationName.stampede_sprint_kong] = []
stampede_sprint_region = create_region(world, player, active_locations, LocationName.stampede_sprint_region,
stampede_sprint_region = create_region(world, active_locations, LocationName.stampede_sprint_region,
stampede_sprint_region_locations)
criss_cross_cliffs_region_locations = {
@@ -444,9 +445,9 @@ def create_regions(world, player: int, active_locations):
LocationName.criss_cross_cliffs_bonus_2 : [0x67C, 3],
LocationName.criss_cross_cliffs_dk : [0x67C, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
criss_cross_cliffs_region_locations[LocationName.criss_cross_cliffs_kong] = []
criss_cross_cliffs_region = create_region(world, player, active_locations, LocationName.criss_cross_cliffs_region,
criss_cross_cliffs_region = create_region(world, active_locations, LocationName.criss_cross_cliffs_region,
criss_cross_cliffs_region_locations)
tyrant_twin_tussle_region_locations = {
@@ -456,9 +457,9 @@ def create_regions(world, player: int, active_locations):
LocationName.tyrant_twin_tussle_bonus_3 : [0x67D, 4],
LocationName.tyrant_twin_tussle_dk : [0x67D, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
tyrant_twin_tussle_region_locations[LocationName.tyrant_twin_tussle_kong] = []
tyrant_twin_tussle_region = create_region(world, player, active_locations, LocationName.tyrant_twin_tussle_region,
tyrant_twin_tussle_region = create_region(world, active_locations, LocationName.tyrant_twin_tussle_region,
tyrant_twin_tussle_region_locations)
swoopy_salvo_region_locations = {
@@ -468,147 +469,147 @@ def create_regions(world, player: int, active_locations):
LocationName.swoopy_salvo_bonus_3 : [0x663, 4],
LocationName.swoopy_salvo_dk : [0x663, 5],
}
if world.kongsanity[player]:
if world.options.kongsanity:
swoopy_salvo_region_locations[LocationName.swoopy_salvo_kong] = []
swoopy_salvo_region = create_region(world, player, active_locations, LocationName.swoopy_salvo_region,
swoopy_salvo_region = create_region(world, active_locations, LocationName.swoopy_salvo_region,
swoopy_salvo_region_locations)
rocket_rush_region_locations = {
LocationName.rocket_rush_flag : [0x67E, 1],
LocationName.rocket_rush_dk : [0x67E, 5],
}
rocket_rush_region = create_region(world, player, active_locations, LocationName.rocket_rush_region,
rocket_rush_region = create_region(world, active_locations, LocationName.rocket_rush_region,
rocket_rush_region_locations)
belchas_barn_region_locations = {
LocationName.belchas_barn: [0x64F, 1],
}
belchas_barn_region = create_region(world, player, active_locations, LocationName.belchas_barn_region,
belchas_barn_region = create_region(world, active_locations, LocationName.belchas_barn_region,
belchas_barn_region_locations)
arichs_ambush_region_locations = {
LocationName.arichs_ambush: [0x650, 1],
}
arichs_ambush_region = create_region(world, player, active_locations, LocationName.arichs_ambush_region,
arichs_ambush_region = create_region(world, active_locations, LocationName.arichs_ambush_region,
arichs_ambush_region_locations)
squirts_showdown_region_locations = {
LocationName.squirts_showdown: [0x651, 1],
}
squirts_showdown_region = create_region(world, player, active_locations, LocationName.squirts_showdown_region,
squirts_showdown_region = create_region(world, active_locations, LocationName.squirts_showdown_region,
squirts_showdown_region_locations)
kaos_karnage_region_locations = {
LocationName.kaos_karnage: [0x652, 1],
}
kaos_karnage_region = create_region(world, player, active_locations, LocationName.kaos_karnage_region,
kaos_karnage_region = create_region(world, active_locations, LocationName.kaos_karnage_region,
kaos_karnage_region_locations)
bleaks_house_region_locations = {
LocationName.bleaks_house: [0x653, 1],
}
bleaks_house_region = create_region(world, player, active_locations, LocationName.bleaks_house_region,
bleaks_house_region = create_region(world, active_locations, LocationName.bleaks_house_region,
bleaks_house_region_locations)
barboss_barrier_region_locations = {
LocationName.barboss_barrier: [0x654, 1],
}
barboss_barrier_region = create_region(world, player, active_locations, LocationName.barboss_barrier_region,
barboss_barrier_region = create_region(world, active_locations, LocationName.barboss_barrier_region,
barboss_barrier_region_locations)
kastle_kaos_region_locations = {
LocationName.kastle_kaos: [0x655, 1],
}
kastle_kaos_region = create_region(world, player, active_locations, LocationName.kastle_kaos_region,
kastle_kaos_region = create_region(world, active_locations, LocationName.kastle_kaos_region,
kastle_kaos_region_locations)
knautilus_region_locations = {
LocationName.knautilus: [0x656, 1],
}
knautilus_region = create_region(world, player, active_locations, LocationName.knautilus_region,
knautilus_region = create_region(world, active_locations, LocationName.knautilus_region,
knautilus_region_locations)
belchas_burrow_region_locations = {
LocationName.belchas_burrow: [0x647, 1],
}
belchas_burrow_region = create_region(world, player, active_locations, LocationName.belchas_burrow_region,
belchas_burrow_region = create_region(world, active_locations, LocationName.belchas_burrow_region,
belchas_burrow_region_locations)
kong_cave_region_locations = {
LocationName.kong_cave: [0x645, 1],
}
kong_cave_region = create_region(world, player, active_locations, LocationName.kong_cave_region,
kong_cave_region = create_region(world, active_locations, LocationName.kong_cave_region,
kong_cave_region_locations)
undercover_cove_region_locations = {
LocationName.undercover_cove: [0x644, 1],
}
undercover_cove_region = create_region(world, player, active_locations, LocationName.undercover_cove_region,
undercover_cove_region = create_region(world, active_locations, LocationName.undercover_cove_region,
undercover_cove_region_locations)
ks_cache_region_locations = {
LocationName.ks_cache: [0x642, 1],
}
ks_cache_region = create_region(world, player, active_locations, LocationName.ks_cache_region,
ks_cache_region = create_region(world, active_locations, LocationName.ks_cache_region,
ks_cache_region_locations)
hill_top_hoard_region_locations = {
LocationName.hill_top_hoard: [0x643, 1],
}
hill_top_hoard_region = create_region(world, player, active_locations, LocationName.hill_top_hoard_region,
hill_top_hoard_region = create_region(world, active_locations, LocationName.hill_top_hoard_region,
hill_top_hoard_region_locations)
bounty_beach_region_locations = {
LocationName.bounty_beach: [0x646, 1],
}
bounty_beach_region = create_region(world, player, active_locations, LocationName.bounty_beach_region,
bounty_beach_region = create_region(world, active_locations, LocationName.bounty_beach_region,
bounty_beach_region_locations)
smugglers_cove_region_locations = {
LocationName.smugglers_cove: [0x648, 1],
}
smugglers_cove_region = create_region(world, player, active_locations, LocationName.smugglers_cove_region,
smugglers_cove_region = create_region(world, active_locations, LocationName.smugglers_cove_region,
smugglers_cove_region_locations)
arichs_hoard_region_locations = {
LocationName.arichs_hoard: [0x649, 1],
}
arichs_hoard_region = create_region(world, player, active_locations, LocationName.arichs_hoard_region,
arichs_hoard_region = create_region(world, active_locations, LocationName.arichs_hoard_region,
arichs_hoard_region_locations)
bounty_bay_region_locations = {
LocationName.bounty_bay: [0x64A, 1],
}
bounty_bay_region = create_region(world, player, active_locations, LocationName.bounty_bay_region,
bounty_bay_region = create_region(world, active_locations, LocationName.bounty_bay_region,
bounty_bay_region_locations)
sky_high_secret_region_locations = {}
if False:#world.include_trade_sequence[player]:
if False:#world.options.include_trade_sequence:
sky_high_secret_region_locations[LocationName.sky_high_secret] = [0x64B, 1]
sky_high_secret_region = create_region(world, player, active_locations, LocationName.sky_high_secret_region,
sky_high_secret_region = create_region(world, active_locations, LocationName.sky_high_secret_region,
sky_high_secret_region_locations)
glacial_grotto_region_locations = {
LocationName.glacial_grotto: [0x64C, 1],
}
glacial_grotto_region = create_region(world, player, active_locations, LocationName.glacial_grotto_region,
glacial_grotto_region = create_region(world, active_locations, LocationName.glacial_grotto_region,
glacial_grotto_region_locations)
cifftop_cache_region_locations = {}
if False:#world.include_trade_sequence[player]:
if False:#world.options.include_trade_sequence:
cifftop_cache_region_locations[LocationName.cifftop_cache] = [0x64D, 1]
cifftop_cache_region = create_region(world, player, active_locations, LocationName.cifftop_cache_region,
cifftop_cache_region = create_region(world, active_locations, LocationName.cifftop_cache_region,
cifftop_cache_region_locations)
sewer_stockpile_region_locations = {
LocationName.sewer_stockpile: [0x64E, 1],
}
sewer_stockpile_region = create_region(world, player, active_locations, LocationName.sewer_stockpile_region,
sewer_stockpile_region = create_region(world, active_locations, LocationName.sewer_stockpile_region,
sewer_stockpile_region_locations)
# Set up the regions correctly.
world.regions += [
world.multiworld.regions += [
menu_region,
overworld_1_region,
overworld_2_region,
@@ -693,7 +694,7 @@ def create_regions(world, player: int, active_locations):
blue_region_locations = {}
blizzard_region_locations = {}
if False:#world.include_trade_sequence[player]:
if False:#world.options.include_trade_sequence:
bazaar_region_locations.update({
LocationName.bazaars_general_store_1: [0x615, 2, True],
LocationName.bazaars_general_store_2: [0x615, 3, True],
@@ -713,19 +714,19 @@ def create_regions(world, player: int, active_locations):
blizzard_region_locations[LocationName.blizzards_basecamp] = [0x625, 4, True]
bazaar_region = create_region(world, player, active_locations, LocationName.bazaar_region, bazaar_region_locations)
bramble_region = create_region(world, player, active_locations, LocationName.bramble_region,
bazaar_region = create_region(world, active_locations, LocationName.bazaar_region, bazaar_region_locations)
bramble_region = create_region(world, active_locations, LocationName.bramble_region,
bramble_region_locations)
flower_spot_region = create_region(world, player, active_locations, LocationName.flower_spot_region,
flower_spot_region = create_region(world, active_locations, LocationName.flower_spot_region,
flower_spot_region_locations)
barter_region = create_region(world, player, active_locations, LocationName.barter_region, barter_region_locations)
barnacle_region = create_region(world, player, active_locations, LocationName.barnacle_region,
barter_region = create_region(world, active_locations, LocationName.barter_region, barter_region_locations)
barnacle_region = create_region(world, active_locations, LocationName.barnacle_region,
barnacle_region_locations)
blue_region = create_region(world, player, active_locations, LocationName.blue_region, blue_region_locations)
blizzard_region = create_region(world, player, active_locations, LocationName.blizzard_region,
blue_region = create_region(world, active_locations, LocationName.blue_region, blue_region_locations)
blizzard_region = create_region(world, active_locations, LocationName.blizzard_region,
blizzard_region_locations)
world.regions += [
world.multiworld.regions += [
bazaar_region,
bramble_region,
flower_spot_region,
@@ -736,41 +737,41 @@ def create_regions(world, player: int, active_locations):
]
def connect_regions(world, player, level_list):
def connect_regions(world: World, level_list):
names: typing.Dict[str, int] = {}
# Overworld
connect(world, player, names, 'Menu', LocationName.overworld_1_region)
connect(world, player, names, LocationName.overworld_1_region, LocationName.overworld_2_region,
lambda state: (state.has(ItemName.progressive_boat, player, 1)))
connect(world, player, names, LocationName.overworld_2_region, LocationName.overworld_3_region,
lambda state: (state.has(ItemName.progressive_boat, player, 3)))
connect(world, player, names, LocationName.overworld_1_region, LocationName.overworld_4_region,
lambda state: (state.has(ItemName.dk_coin, player, world.dk_coins_for_gyrocopter[player].value) and
state.has(ItemName.progressive_boat, player, 3)))
connect(world, world.player, names, 'Menu', LocationName.overworld_1_region)
connect(world, world.player, names, LocationName.overworld_1_region, LocationName.overworld_2_region,
lambda state: (state.has(ItemName.progressive_boat, world.player, 1)))
connect(world, world.player, names, LocationName.overworld_2_region, LocationName.overworld_3_region,
lambda state: (state.has(ItemName.progressive_boat, world.player, 3)))
connect(world, world.player, names, LocationName.overworld_1_region, LocationName.overworld_4_region,
lambda state: (state.has(ItemName.dk_coin, world.player, world.options.dk_coins_for_gyrocopter.value) and
state.has(ItemName.progressive_boat, world.player, 3)))
# World Connections
connect(world, player, names, LocationName.overworld_1_region, LocationName.lake_orangatanga_region)
connect(world, player, names, LocationName.overworld_1_region, LocationName.kremwood_forest_region)
connect(world, player, names, LocationName.overworld_1_region, LocationName.bounty_beach_region)
connect(world, player, names, LocationName.overworld_1_region, LocationName.bazaar_region)
connect(world, world.player, names, LocationName.overworld_1_region, LocationName.lake_orangatanga_region)
connect(world, world.player, names, LocationName.overworld_1_region, LocationName.kremwood_forest_region)
connect(world, world.player, names, LocationName.overworld_1_region, LocationName.bounty_beach_region)
connect(world, world.player, names, LocationName.overworld_1_region, LocationName.bazaar_region)
connect(world, player, names, LocationName.overworld_2_region, LocationName.cotton_top_cove_region)
connect(world, player, names, LocationName.overworld_2_region, LocationName.mekanos_region)
connect(world, player, names, LocationName.overworld_2_region, LocationName.kong_cave_region)
connect(world, player, names, LocationName.overworld_2_region, LocationName.bramble_region)
connect(world, world.player, names, LocationName.overworld_2_region, LocationName.cotton_top_cove_region)
connect(world, world.player, names, LocationName.overworld_2_region, LocationName.mekanos_region)
connect(world, world.player, names, LocationName.overworld_2_region, LocationName.kong_cave_region)
connect(world, world.player, names, LocationName.overworld_2_region, LocationName.bramble_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.k3_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.razor_ridge_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.kaos_kore_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.krematoa_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.undercover_cove_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.flower_spot_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.barter_region)
connect(world, world.player, names, LocationName.overworld_3_region, LocationName.k3_region)
connect(world, world.player, names, LocationName.overworld_3_region, LocationName.razor_ridge_region)
connect(world, world.player, names, LocationName.overworld_3_region, LocationName.kaos_kore_region)
connect(world, world.player, names, LocationName.overworld_3_region, LocationName.krematoa_region)
connect(world, world.player, names, LocationName.overworld_3_region, LocationName.undercover_cove_region)
connect(world, world.player, names, LocationName.overworld_3_region, LocationName.flower_spot_region)
connect(world, world.player, names, LocationName.overworld_3_region, LocationName.barter_region)
connect(world, player, names, LocationName.overworld_4_region, LocationName.belchas_burrow_region)
connect(world, player, names, LocationName.overworld_4_region, LocationName.ks_cache_region)
connect(world, player, names, LocationName.overworld_4_region, LocationName.hill_top_hoard_region)
connect(world, world.player, names, LocationName.overworld_4_region, LocationName.belchas_burrow_region)
connect(world, world.player, names, LocationName.overworld_4_region, LocationName.ks_cache_region)
connect(world, world.player, names, LocationName.overworld_4_region, LocationName.hill_top_hoard_region)
# Lake Orangatanga Connections
@@ -786,7 +787,7 @@ def connect_regions(world, player, level_list):
]
for i in range(0, len(lake_orangatanga_levels)):
connect(world, player, names, LocationName.lake_orangatanga_region, lake_orangatanga_levels[i])
connect(world, world.player, names, LocationName.lake_orangatanga_region, lake_orangatanga_levels[i])
# Kremwood Forest Connections
kremwood_forest_levels = [
@@ -800,10 +801,10 @@ def connect_regions(world, player, level_list):
]
for i in range(0, len(kremwood_forest_levels) - 1):
connect(world, player, names, LocationName.kremwood_forest_region, kremwood_forest_levels[i])
connect(world, world.player, names, LocationName.kremwood_forest_region, kremwood_forest_levels[i])
connect(world, player, names, LocationName.kremwood_forest_region, kremwood_forest_levels[-1],
lambda state: (state.can_reach(LocationName.riverside_race_flag, "Location", player)))
connect(world, world.player, names, LocationName.kremwood_forest_region, kremwood_forest_levels[-1],
lambda state: (state.can_reach(LocationName.riverside_race_flag, "Location", world.player)))
# Cotton-Top Cove Connections
cotton_top_cove_levels = [
@@ -818,7 +819,7 @@ def connect_regions(world, player, level_list):
]
for i in range(0, len(cotton_top_cove_levels)):
connect(world, player, names, LocationName.cotton_top_cove_region, cotton_top_cove_levels[i])
connect(world, world.player, names, LocationName.cotton_top_cove_region, cotton_top_cove_levels[i])
# Mekanos Connections
mekanos_levels = [
@@ -831,14 +832,14 @@ def connect_regions(world, player, level_list):
]
for i in range(0, len(mekanos_levels)):
connect(world, player, names, LocationName.mekanos_region, mekanos_levels[i])
connect(world, world.player, names, LocationName.mekanos_region, mekanos_levels[i])
if False:#world.include_trade_sequence[player]:
connect(world, player, names, LocationName.mekanos_region, LocationName.sky_high_secret_region,
lambda state: (state.has(ItemName.bowling_ball, player, 1)))
if False:#world.options.include_trade_sequence:
connect(world, world.player, names, LocationName.mekanos_region, LocationName.sky_high_secret_region,
lambda state: (state.has(ItemName.bowling_ball, world.player, 1)))
else:
connect(world, player, names, LocationName.mekanos_region, LocationName.sky_high_secret_region,
lambda state: (state.can_reach(LocationName.bleaks_house, "Location", player)))
connect(world, world.player, names, LocationName.mekanos_region, LocationName.sky_high_secret_region,
lambda state: (state.can_reach(LocationName.bleaks_house, "Location", world.player)))
# K3 Connections
k3_levels = [
@@ -853,7 +854,7 @@ def connect_regions(world, player, level_list):
]
for i in range(0, len(k3_levels)):
connect(world, player, names, LocationName.k3_region, k3_levels[i])
connect(world, world.player, names, LocationName.k3_region, k3_levels[i])
# Razor Ridge Connections
razor_ridge_levels = [
@@ -866,13 +867,13 @@ def connect_regions(world, player, level_list):
]
for i in range(0, len(razor_ridge_levels)):
connect(world, player, names, LocationName.razor_ridge_region, razor_ridge_levels[i])
connect(world, world.player, names, LocationName.razor_ridge_region, razor_ridge_levels[i])
if False:#world.include_trade_sequence[player]:
connect(world, player, names, LocationName.razor_ridge_region, LocationName.cifftop_cache_region,
lambda state: (state.has(ItemName.wrench, player, 1)))
if False:#world.options.include_trade_sequence:
connect(world, world.player, names, LocationName.razor_ridge_region, LocationName.cifftop_cache_region,
lambda state: (state.has(ItemName.wrench, world.player, 1)))
else:
connect(world, player, names, LocationName.razor_ridge_region, LocationName.cifftop_cache_region)
connect(world, world.player, names, LocationName.razor_ridge_region, LocationName.cifftop_cache_region)
# KAOS Kore Connections
kaos_kore_levels = [
@@ -885,7 +886,7 @@ def connect_regions(world, player, level_list):
]
for i in range(0, len(kaos_kore_levels)):
connect(world, player, names, LocationName.kaos_kore_region, kaos_kore_levels[i])
connect(world, world.player, names, LocationName.kaos_kore_region, kaos_kore_levels[i])
# Krematoa Connections
krematoa_levels = [
@@ -897,22 +898,22 @@ def connect_regions(world, player, level_list):
]
for i in range(0, len(krematoa_levels)):
connect(world, player, names, LocationName.krematoa_region, krematoa_levels[i],
lambda state, i=i: (state.has(ItemName.bonus_coin, player, world.krematoa_bonus_coin_cost[player].value * (i+1))))
connect(world, world.player, names, LocationName.krematoa_region, krematoa_levels[i],
lambda state, i=i: (state.has(ItemName.bonus_coin, world.player, world.options.krematoa_bonus_coin_cost.value * (i+1))))
if world.goal[player] == "knautilus":
connect(world, player, names, LocationName.kaos_kore_region, LocationName.knautilus_region)
connect(world, player, names, LocationName.krematoa_region, LocationName.kastle_kaos_region,
lambda state: (state.has(ItemName.krematoa_cog, player, 5)))
if world.options.goal == "knautilus":
connect(world, world.player, names, LocationName.kaos_kore_region, LocationName.knautilus_region)
connect(world, world.player, names, LocationName.krematoa_region, LocationName.kastle_kaos_region,
lambda state: (state.has(ItemName.krematoa_cog, world.player, 5)))
else:
connect(world, player, names, LocationName.kaos_kore_region, LocationName.kastle_kaos_region)
connect(world, player, names, LocationName.krematoa_region, LocationName.knautilus_region,
lambda state: (state.has(ItemName.krematoa_cog, player, 5)))
connect(world, world.player, names, LocationName.kaos_kore_region, LocationName.kastle_kaos_region)
connect(world, world.player, names, LocationName.krematoa_region, LocationName.knautilus_region,
lambda state: (state.has(ItemName.krematoa_cog, world.player, 5)))
def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None):
def create_region(world: World, active_locations, name: str, locations=None):
# Shamelessly stolen from the ROR2 definition
ret = Region(name, player, world)
ret = Region(name, world.player, world.multiworld)
if locations:
for locationName, locationData in locations.items():
loc_id = active_locations.get(locationName, 0)
@@ -921,16 +922,16 @@ def create_region(world: MultiWorld, player: int, active_locations, name: str, l
loc_bit = locationData[1] if (len(locationData) > 1) else 0
loc_invert = locationData[2] if (len(locationData) > 2) else False
location = DKC3Location(player, locationName, loc_id, ret, loc_byte, loc_bit, loc_invert)
location = DKC3Location(world.player, locationName, loc_id, ret, loc_byte, loc_bit, loc_invert)
ret.locations.append(location)
return ret
def connect(world: MultiWorld, player: int, used_names: typing.Dict[str, int], source: str, target: str,
def connect(world: World, player: int, used_names: typing.Dict[str, int], source: str, target: str,
rule: typing.Optional[typing.Callable] = None):
source_region = world.get_region(source, player)
target_region = world.get_region(target, player)
source_region = world.multiworld.get_region(source, player)
target_region = world.multiworld.get_region(target, player)
if target not in used_names:
used_names[target] = 1

View File

@@ -1,5 +1,6 @@
import Utils
from Utils import read_snes_rom
from worlds.AutoWorld import World
from worlds.Files import APDeltaPatch
from .Locations import lookup_id_to_name, all_locations
from .Levels import level_list, level_dict
@@ -475,11 +476,10 @@ class LocalRom(object):
def patch_rom(world, rom, player, active_level_list):
local_random = world.per_slot_randoms[player]
def patch_rom(world: World, rom: LocalRom, active_level_list):
# Boomer Costs
bonus_coin_cost = world.krematoa_bonus_coin_cost[player]
bonus_coin_cost = world.options.krematoa_bonus_coin_cost
inverted_bonus_coin_cost = 0x100 - bonus_coin_cost
rom.write_byte(0x3498B9, inverted_bonus_coin_cost)
rom.write_byte(0x3498BA, inverted_bonus_coin_cost)
@@ -491,7 +491,7 @@ def patch_rom(world, rom, player, active_level_list):
rom.write_byte(0x349862, bonus_coin_cost)
# Gyrocopter Costs
dk_coin_cost = world.dk_coins_for_gyrocopter[player]
dk_coin_cost = world.options.dk_coins_for_gyrocopter
rom.write_byte(0x3484A6, dk_coin_cost)
rom.write_byte(0x3484D5, dk_coin_cost)
rom.write_byte(0x3484D7, 0x90)
@@ -508,8 +508,8 @@ def patch_rom(world, rom, player, active_level_list):
rom.write_bytes(0x34ACD0, bytearray([0xEA, 0xEA]))
# Banana Bird Costs
if world.goal[player] == "banana_bird_hunt":
banana_bird_cost = math.floor(world.number_of_banana_birds[player] * world.percentage_of_banana_birds[player] / 100.0)
if world.options.goal == "banana_bird_hunt":
banana_bird_cost = math.floor(world.options.number_of_banana_birds * world.options.percentage_of_banana_birds / 100.0)
rom.write_byte(0x34AB85, banana_bird_cost)
rom.write_byte(0x329FD8, banana_bird_cost)
rom.write_byte(0x32A025, banana_bird_cost)
@@ -528,65 +528,65 @@ def patch_rom(world, rom, player, active_level_list):
# Palette Swap
rom.write_byte(0x3B96A5, 0xD0)
if world.kong_palette_swap[player] == "default":
if world.options.kong_palette_swap == "default":
rom.write_byte(0x3B96A9, 0x00)
rom.write_byte(0x3B96A8, 0x00)
elif world.kong_palette_swap[player] == "purple":
elif world.options.kong_palette_swap == "purple":
rom.write_byte(0x3B96A9, 0x00)
rom.write_byte(0x3B96A8, 0x3C)
elif world.kong_palette_swap[player] == "spooky":
elif world.options.kong_palette_swap == "spooky":
rom.write_byte(0x3B96A9, 0x00)
rom.write_byte(0x3B96A8, 0xA0)
elif world.kong_palette_swap[player] == "dark":
elif world.options.kong_palette_swap == "dark":
rom.write_byte(0x3B96A9, 0x05)
rom.write_byte(0x3B96A8, 0xA0)
elif world.kong_palette_swap[player] == "chocolate":
elif world.options.kong_palette_swap == "chocolate":
rom.write_byte(0x3B96A9, 0x1D)
rom.write_byte(0x3B96A8, 0xA0)
elif world.kong_palette_swap[player] == "shadow":
elif world.options.kong_palette_swap == "shadow":
rom.write_byte(0x3B96A9, 0x45)
rom.write_byte(0x3B96A8, 0xA0)
elif world.kong_palette_swap[player] == "red_gold":
elif world.options.kong_palette_swap == "red_gold":
rom.write_byte(0x3B96A9, 0x5D)
rom.write_byte(0x3B96A8, 0xA0)
elif world.kong_palette_swap[player] == "gbc":
elif world.options.kong_palette_swap == "gbc":
rom.write_byte(0x3B96A9, 0x20)
rom.write_byte(0x3B96A8, 0x3C)
elif world.kong_palette_swap[player] == "halloween":
elif world.options.kong_palette_swap == "halloween":
rom.write_byte(0x3B96A9, 0x70)
rom.write_byte(0x3B96A8, 0x3C)
if world.music_shuffle[player]:
if world.options.music_shuffle:
for address in music_rom_data:
rand_song = local_random.choice(level_music_ids)
rand_song = world.random.choice(level_music_ids)
rom.write_byte(address, rand_song)
# Starting Lives
rom.write_byte(0x9130, world.starting_life_count[player].value)
rom.write_byte(0x913B, world.starting_life_count[player].value)
rom.write_byte(0x9130, world.options.starting_life_count.value)
rom.write_byte(0x913B, world.options.starting_life_count.value)
# Cheat options
cheat_bytes = [0x00, 0x00]
if world.merry[player]:
if world.options.merry:
cheat_bytes[0] |= 0x01
if world.autosave[player]:
if world.options.autosave:
cheat_bytes[0] |= 0x02
if world.difficulty[player] == "tufst":
if world.options.difficulty == "tufst":
cheat_bytes[0] |= 0x80
cheat_bytes[1] |= 0x80
elif world.difficulty[player] == "hardr":
elif world.options.difficulty == "hardr":
cheat_bytes[0] |= 0x00
cheat_bytes[1] |= 0x00
elif world.difficulty[player] == "norml":
elif world.options.difficulty == "norml":
cheat_bytes[1] |= 0x40
rom.write_bytes(0x8303, bytearray(cheat_bytes))
# Handle Level Shuffle Here
if world.level_shuffle[player]:
if world.options.level_shuffle:
for i in range(len(active_level_list)):
rom.write_byte(level_dict[level_list[i]].nameIDAddress, level_dict[active_level_list[i]].nameID)
rom.write_byte(level_dict[level_list[i]].levelIDAddress, level_dict[active_level_list[i]].levelID)
@@ -611,7 +611,7 @@ def patch_rom(world, rom, player, active_level_list):
rom.write_byte(0x34C213, (0x32 + level_dict[active_level_list[25]].levelID))
rom.write_byte(0x34C21B, (0x32 + level_dict[active_level_list[26]].levelID))
if world.goal[player] == "knautilus":
if world.options.goal == "knautilus":
# Swap Kastle KAOS and Knautilus
rom.write_byte(0x34D4E1, 0xC2)
rom.write_byte(0x34D4E2, 0x24)
@@ -621,7 +621,7 @@ def patch_rom(world, rom, player, active_level_list):
rom.write_byte(0x32F339, 0x55)
# Handle KONGsanity Here
if world.kongsanity[player]:
if world.options.kongsanity:
# Arich's Hoard KONGsanity fix
rom.write_bytes(0x34BA8C, bytearray([0xEA, 0xEA]))
@@ -668,7 +668,7 @@ def patch_rom(world, rom, player, active_level_list):
rom.write_bytes(0x32A5EE, bytearray([0x00, 0x03, 0x50, 0x4F, 0x52, 0x59, 0x47, 0x4F, 0x4E, 0xC5])) # "PORYGONE"
from Utils import __version__
rom.name = bytearray(f'D3{__version__.replace(".", "")[0:3]}_{player}_{world.seed:11}\0', 'utf8')[:21]
rom.name = bytearray(f'D3{__version__.replace(".", "")[0:3]}_{world.player}_{world.multiworld.seed:11}\0', 'utf8')[:21]
rom.name.extend([0] * (21 - len(rom.name)))
rom.write_bytes(0x7FC0, rom.name)

View File

@@ -1,32 +1,31 @@
import math
from BaseClasses import MultiWorld
from .Names import LocationName, ItemName
from worlds.AutoWorld import LogicMixin
from worlds.AutoWorld import LogicMixin, World
from worlds.generic.Rules import add_rule, set_rule
def set_rules(world: MultiWorld, player: int):
def set_rules(world: World):
if False:#world.include_trade_sequence[player]:
add_rule(world.get_location(LocationName.barnacles_island, player),
lambda state: state.has(ItemName.shell, player))
if False:#world.options.include_trade_sequence:
add_rule(world.multiworld.get_location(LocationName.barnacles_island, world.player),
lambda state: state.has(ItemName.shell, world.player))
add_rule(world.get_location(LocationName.blues_beach_hut, player),
lambda state: state.has(ItemName.present, player))
add_rule(world.multiworld.get_location(LocationName.blues_beach_hut, world.player),
lambda state: state.has(ItemName.present, world.player))
add_rule(world.get_location(LocationName.brambles_bungalow, player),
lambda state: state.has(ItemName.flower, player))
add_rule(world.multiworld.get_location(LocationName.brambles_bungalow, world.player),
lambda state: state.has(ItemName.flower, world.player))
add_rule(world.get_location(LocationName.barters_swap_shop, player),
lambda state: state.has(ItemName.mirror, player))
add_rule(world.multiworld.get_location(LocationName.barters_swap_shop, world.player),
lambda state: state.has(ItemName.mirror, world.player))
if world.goal[player] != "knautilus":
if world.options.goal != "knautilus":
required_banana_birds = math.floor(
world.number_of_banana_birds[player].value * (world.percentage_of_banana_birds[player].value / 100.0))
world.options.number_of_banana_birds.value * (world.options.percentage_of_banana_birds.value / 100.0))
add_rule(world.get_location(LocationName.banana_bird_mother, player),
lambda state: state.has(ItemName.banana_bird, player, required_banana_birds))
add_rule(world.multiworld.get_location(LocationName.banana_bird_mother, world.player),
lambda state: state.has(ItemName.banana_bird, world.player, required_banana_birds))
world.completion_condition[player] = lambda state: state.has(ItemName.victory, player)
world.multiworld.completion_condition[world.player] = lambda state: state.has(ItemName.victory, world.player)

View File

@@ -1,3 +1,4 @@
import dataclasses
import os
import typing
import math
@@ -5,9 +6,10 @@ import threading
import settings
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification
from Options import PerGameCommonOptions
from .Items import DKC3Item, ItemData, item_table, inventory_table, junk_table
from .Locations import DKC3Location, all_locations, setup_locations
from .Options import dkc3_options
from .Options import DKC3Options
from .Regions import create_regions, connect_regions
from .Levels import level_list
from .Rules import set_rules
@@ -50,8 +52,11 @@ class DKC3World(World):
mystery of why Donkey Kong and Diddy disappeared while on vacation.
"""
game: str = "Donkey Kong Country 3"
option_definitions = dkc3_options
settings: typing.ClassVar[DK3Settings]
options_dataclass = DKC3Options
options: DKC3Options
topology_present = False
data_version = 2
#hint_blacklist = {LocationName.rocket_rush_flag}
@@ -74,24 +79,25 @@ class DKC3World(World):
def _get_slot_data(self):
return {
#"death_link": self.world.death_link[self.player].value,
#"death_link": self.options.death_link.value,
"active_levels": self.active_level_list,
}
def fill_slot_data(self) -> dict:
slot_data = self._get_slot_data()
for option_name in dkc3_options:
option = getattr(self.multiworld, option_name)[self.player]
for option_name in (attr.name for attr in dataclasses.fields(DKC3Options)
if attr not in dataclasses.fields(PerGameCommonOptions)):
option = getattr(self.options, option_name)
slot_data[option_name] = option.value
return slot_data
def create_regions(self):
location_table = setup_locations(self.multiworld, self.player)
create_regions(self.multiworld, self.player, location_table)
location_table = setup_locations(self)
create_regions(self, location_table)
# Not generate basic
self.topology_present = self.multiworld.level_shuffle[self.player].value
self.topology_present = self.options.level_shuffle.value
itempool: typing.List[DKC3Item] = []
# Levels
@@ -103,12 +109,12 @@ class DKC3World(World):
number_of_cogs = 4
self.multiworld.get_location(LocationName.rocket_rush_flag, self.player).place_locked_item(self.create_item(ItemName.krematoa_cog))
number_of_bosses = 8
if self.multiworld.goal[self.player] == "knautilus":
if self.options.goal == "knautilus":
self.multiworld.get_location(LocationName.kastle_kaos, self.player).place_locked_item(self.create_item(ItemName.victory))
number_of_bosses = 7
else:
self.multiworld.get_location(LocationName.banana_bird_mother, self.player).place_locked_item(self.create_item(ItemName.victory))
number_of_banana_birds = self.multiworld.number_of_banana_birds[self.player]
number_of_banana_birds = self.options.number_of_banana_birds
# Bosses
total_required_locations += number_of_bosses
@@ -116,15 +122,15 @@ class DKC3World(World):
# Secret Caves
total_required_locations += 13
if self.multiworld.kongsanity[self.player]:
if self.options.kongsanity:
total_required_locations += 39
## Brothers Bear
if False:#self.world.include_trade_sequence[self.player]:
if False:#self.options.include_trade_sequence:
total_required_locations += 10
number_of_bonus_coins = (self.multiworld.krematoa_bonus_coin_cost[self.player] * 5)
number_of_bonus_coins += math.ceil((85 - number_of_bonus_coins) * self.multiworld.percentage_of_extra_bonus_coins[self.player] / 100)
number_of_bonus_coins = (self.options.krematoa_bonus_coin_cost * 5)
number_of_bonus_coins += math.ceil((85 - number_of_bonus_coins) * self.options.percentage_of_extra_bonus_coins / 100)
itempool += [self.create_item(ItemName.bonus_coin) for _ in range(number_of_bonus_coins)]
itempool += [self.create_item(ItemName.dk_coin) for _ in range(41)]
@@ -142,20 +148,17 @@ class DKC3World(World):
self.active_level_list = level_list.copy()
if self.multiworld.level_shuffle[self.player]:
self.multiworld.random.shuffle(self.active_level_list)
if self.options.level_shuffle:
self.random.shuffle(self.active_level_list)
connect_regions(self.multiworld, self.player, self.active_level_list)
connect_regions(self, self.active_level_list)
self.multiworld.itempool += itempool
def generate_output(self, output_directory: str):
try:
world = self.multiworld
player = self.player
rom = LocalRom(get_base_rom_path())
patch_rom(self.multiworld, rom, self.player, self.active_level_list)
patch_rom(self, rom, self.active_level_list)
self.active_level_list.append(LocationName.rocket_rush_region)
@@ -163,15 +166,15 @@ class DKC3World(World):
rom.write_to_file(rompath)
self.rom_name = rom.name
patch = DKC3DeltaPatch(os.path.splitext(rompath)[0]+DKC3DeltaPatch.patch_file_ending, player=player,
player_name=world.player_name[player], patched_path=rompath)
patch = DKC3DeltaPatch(os.path.splitext(rompath)[0]+DKC3DeltaPatch.patch_file_ending, player=self.player,
player_name=self.multiworld.player_name[self.player], patched_path=rompath)
patch.write()
except:
raise
finally:
self.rom_name_available_event.set() # make sure threading continues and errors are collected
if os.path.exists(rompath):
os.unlink(rompath)
self.rom_name_available_event.set() # make sure threading continues and errors are collected
def modify_multidata(self, multidata: dict):
import base64
@@ -183,6 +186,7 @@ class DKC3World(World):
new_name = base64.b64encode(bytes(self.rom_name)).decode()
multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]]
def extend_hint_information(self, hint_data: typing.Dict[int, typing.Dict[int, str]]):
if self.topology_present:
world_names = [
LocationName.lake_orangatanga_region,
@@ -200,7 +204,8 @@ class DKC3World(World):
level_region = self.multiworld.get_region(self.active_level_list[world_index * 5 + level_index], self.player)
for location in level_region.locations:
er_hint_data[location.address] = world_names[world_index]
multidata['er_hint_data'][self.player] = er_hint_data
hint_data[self.player] = er_hint_data
def create_item(self, name: str, force_non_progression=False) -> Item:
data = item_table[name]
@@ -220,4 +225,4 @@ class DKC3World(World):
return self.multiworld.random.choice(list(junk_table.keys()))
def set_rules(self):
set_rules(self.multiworld, self.player)
set_rules(self)

View File

@@ -1,253 +1,253 @@
{
"Coneria1": 257,
"Coneria2": 258,
"ConeriaMajor": 259,
"Coneria4": 260,
"Coneria5": 261,
"Coneria6": 262,
"MatoyasCave1": 299,
"MatoyasCave3": 301,
"MatoyasCave2": 300,
"NorthwestCastle1": 273,
"NorthwestCastle3": 275,
"NorthwestCastle2": 274,
"ToFTopLeft1": 263,
"ToFBottomLeft": 265,
"ToFTopLeft2": 264,
"ToFRevisited6": 509,
"ToFRevisited4": 507,
"ToFRMasmune": 504,
"ToFRevisited5": 508,
"ToFRevisited3": 506,
"ToFRevisited2": 505,
"ToFRevisited7": 510,
"ToFTopRight1": 267,
"ToFTopRight2": 268,
"ToFBottomRight": 266,
"IceCave15": 377,
"IceCave16": 378,
"IceCave9": 371,
"IceCave11": 373,
"IceCave10": 372,
"IceCave12": 374,
"IceCave13": 375,
"IceCave14": 376,
"IceCave1": 363,
"IceCave2": 364,
"IceCave3": 365,
"IceCave4": 366,
"IceCave5": 367,
"IceCaveMajor": 370,
"IceCave7": 369,
"IceCave6": 368,
"Elfland1": 269,
"Elfland2": 270,
"Elfland3": 271,
"Elfland4": 272,
"Ordeals5": 383,
"Ordeals6": 384,
"Ordeals7": 385,
"Ordeals1": 379,
"Ordeals2": 380,
"Ordeals3": 381,
"Ordeals4": 382,
"OrdealsMajor": 387,
"Ordeals8": 386,
"SeaShrine7": 411,
"SeaShrine8": 412,
"SeaShrine9": 413,
"SeaShrine10": 414,
"SeaShrine1": 405,
"SeaShrine2": 406,
"SeaShrine3": 407,
"SeaShrine4": 408,
"SeaShrine5": 409,
"SeaShrine6": 410,
"SeaShrine13": 417,
"SeaShrine14": 418,
"SeaShrine11": 415,
"SeaShrine15": 419,
"SeaShrine16": 420,
"SeaShrineLocked": 421,
"SeaShrine18": 422,
"SeaShrine19": 423,
"SeaShrine20": 424,
"SeaShrine23": 427,
"SeaShrine21": 425,
"SeaShrine22": 426,
"SeaShrine24": 428,
"SeaShrine26": 430,
"SeaShrine28": 432,
"SeaShrine25": 429,
"SeaShrine30": 434,
"SeaShrine31": 435,
"SeaShrine27": 431,
"SeaShrine29": 433,
"SeaShrineMajor": 436,
"SeaShrine12": 416,
"DwarfCave3": 291,
"DwarfCave4": 292,
"DwarfCave6": 294,
"DwarfCave7": 295,
"DwarfCave5": 293,
"DwarfCave8": 296,
"DwarfCave9": 297,
"DwarfCave10": 298,
"DwarfCave1": 289,
"DwarfCave2": 290,
"Waterfall1": 437,
"Waterfall2": 438,
"Waterfall3": 439,
"Waterfall4": 440,
"Waterfall5": 441,
"Waterfall6": 442,
"MirageTower5": 456,
"MirageTower16": 467,
"MirageTower17": 468,
"MirageTower15": 466,
"MirageTower18": 469,
"MirageTower14": 465,
"SkyPalace1": 470,
"SkyPalace2": 471,
"SkyPalace3": 472,
"SkyPalace4": 473,
"SkyPalace18": 487,
"SkyPalace19": 488,
"SkyPalace16": 485,
"SkyPalaceMajor": 489,
"SkyPalace17": 486,
"SkyPalace22": 491,
"SkyPalace21": 490,
"SkyPalace23": 492,
"SkyPalace24": 493,
"SkyPalace31": 500,
"SkyPalace32": 501,
"SkyPalace33": 502,
"SkyPalace34": 503,
"SkyPalace29": 498,
"SkyPalace26": 495,
"SkyPalace25": 494,
"SkyPalace28": 497,
"SkyPalace27": 496,
"SkyPalace30": 499,
"SkyPalace14": 483,
"SkyPalace11": 480,
"SkyPalace12": 481,
"SkyPalace13": 482,
"SkyPalace15": 484,
"SkyPalace10": 479,
"SkyPalace5": 474,
"SkyPalace6": 475,
"SkyPalace7": 476,
"SkyPalace8": 477,
"SkyPalace9": 478,
"MirageTower9": 460,
"MirageTower13": 464,
"MirageTower10": 461,
"MirageTower12": 463,
"MirageTower11": 462,
"MirageTower1": 452,
"MirageTower2": 453,
"MirageTower4": 455,
"MirageTower3": 454,
"MirageTower8": 459,
"MirageTower7": 458,
"MirageTower6": 457,
"Volcano30": 359,
"Volcano32": 361,
"Volcano31": 360,
"Volcano28": 357,
"Volcano29": 358,
"Volcano21": 350,
"Volcano20": 349,
"Volcano24": 353,
"Volcano19": 348,
"Volcano25": 354,
"VolcanoMajor": 362,
"Volcano26": 355,
"Volcano27": 356,
"Volcano22": 351,
"Volcano23": 352,
"Volcano1": 330,
"Volcano9": 338,
"Volcano2": 331,
"Volcano10": 339,
"Volcano3": 332,
"Volcano8": 337,
"Volcano4": 333,
"Volcano13": 342,
"Volcano11": 340,
"Volcano7": 336,
"Volcano6": 335,
"Volcano5": 334,
"Volcano14": 343,
"Volcano12": 341,
"Volcano15": 344,
"Volcano18": 347,
"Volcano17": 346,
"Volcano16": 345,
"MarshCave6": 281,
"MarshCave5": 280,
"MarshCave7": 282,
"MarshCave8": 283,
"MarshCave10": 285,
"MarshCave2": 277,
"MarshCave11": 286,
"MarshCave3": 278,
"MarshCaveMajor": 284,
"MarshCave12": 287,
"MarshCave4": 279,
"MarshCave1": 276,
"MarshCave13": 288,
"TitansTunnel1": 326,
"TitansTunnel2": 327,
"TitansTunnel3": 328,
"TitansTunnel4": 329,
"EarthCave1": 302,
"EarthCave2": 303,
"EarthCave5": 306,
"EarthCave3": 304,
"EarthCave4": 305,
"EarthCave9": 310,
"EarthCave10": 311,
"EarthCave11": 312,
"EarthCave6": 307,
"EarthCave7": 308,
"EarthCave12": 313,
"EarthCaveMajor": 317,
"EarthCave19": 320,
"EarthCave17": 318,
"EarthCave18": 319,
"EarthCave20": 321,
"EarthCave24": 325,
"EarthCave21": 322,
"EarthCave22": 323,
"EarthCave23": 324,
"EarthCave13": 314,
"EarthCave15": 316,
"EarthCave14": 315,
"EarthCave8": 309,
"Cardia11": 398,
"Cardia9": 396,
"Cardia10": 397,
"Cardia6": 393,
"Cardia8": 395,
"Cardia7": 394,
"Cardia13": 400,
"Cardia12": 399,
"Cardia4": 391,
"Cardia5": 392,
"Cardia3": 390,
"Cardia1": 388,
"Cardia2": 389,
"CaravanShop": 767,
"Matoya's Cave - Chest 1": 299,
"Matoya's Cave - Chest 2": 301,
"Matoya's Cave - Chest 3": 300,
"Dwarf Cave - Entrance 1": 289,
"Dwarf Cave - Entrance 2": 290,
"Dwarf Cave - Treasury 1": 291,
"Dwarf Cave - Treasury 2": 292,
"Dwarf Cave - Treasury 3": 295,
"Dwarf Cave - Treasury 4": 293,
"Dwarf Cave - Treasury 5": 294,
"Dwarf Cave - Treasury 6": 296,
"Dwarf Cave - Treasury 7": 297,
"Dwarf Cave - Treasury 8": 298,
"Coneria Castle - Treasury 1": 257,
"Coneria Castle - Treasury 2": 258,
"Coneria Castle - Treasury 3": 260,
"Coneria Castle - Treasury 4": 261,
"Coneria Castle - Treasury 5": 262,
"Coneria Castle - Treasury Major": 259,
"Elf Castle - Treasury 1": 269,
"Elf Castle - Treasury 2": 270,
"Elf Castle - Treasury 3": 271,
"Elf Castle - Treasury 4": 272,
"Northwest Castle - Treasury 1": 273,
"Northwest Castle - Treasury 2": 275,
"Northwest Castle - Treasury 3": 274,
"Titan's Tunnel - Chest 1": 327,
"Titan's Tunnel - Chest 2": 328,
"Titan's Tunnel - Chest 3": 329,
"Titan's Tunnel - Major": 326,
"Cardia Grass Island - Entrance": 398,
"Cardia Grass Island - Duo Room 1": 396,
"Cardia Grass Island - Duo Rooom 2": 397,
"Cardia Swamp Island - Chest 1": 393,
"Cardia Swamp Island - Chest 2": 395,
"Cardia Swamp Island - Chest 3": 394,
"Cardia Forest Island - Entrance 1": 389,
"Cardia Forest Island - Entrance 2": 388,
"Cardia Forest Island - Entrance 3": 390,
"Cardia Forest Island - Incentive 1": 400,
"Cardia Forest Island - Incentive 2": 399,
"Cardia Forest Island - Incentive 3": 392,
"Cardia Forest Island - Incentive Major": 391,
"Temple of Fiends - Unlocked Single": 265,
"Temple of Fiends - Unlocked Duo 1": 263,
"Temple of Fiends - Unlocked Duo 2": 264,
"Temple of Fiends - Locked Single": 266,
"Temple of Fiends - Locked Duo 1": 267,
"Temple of Fiends - Locked Duo 2": 268,
"Marsh Cave Top (B1) - Single": 283,
"Marsh Cave Top (B1) - Corner": 282,
"Marsh Cave Top (B1) - Duo 1": 281,
"Marsh Cave Top (B1) - Duo 2": 280,
"Marsh Cave Bottom (B2) - Distant": 276,
"Marsh Cave Bottom (B2) - Tetris-Z First": 277,
"Marsh Cave Bottom (B2) - Tetris-Z Middle 1": 278,
"Marsh Cave Bottom (B2) - Tetris-Z Middle 2": 285,
"Marsh Cave Bottom (B2) - Tetris-Z Incentive": 284,
"Marsh Cave Bottom (B2) - Tetris-Z Last": 279,
"Marsh Cave Bottom (B2) - Locked Corner": 286,
"Marsh Cave Bottom (B2) - Locked Middle": 287,
"Marsh Cave Bottom (B2) - Locked Incentive": 288,
"Earth Cave Giant's Floor (B1) - Single": 306,
"Earth Cave Giant's Floor (B1) - Appendix 1": 302,
"Earth Cave Giant's Floor (B1) - Appendix 2": 303,
"Earth Cave Giant's Floor (B1) - Side Path 1": 304,
"Earth Cave Giant's Floor (B1) - Side Path 2": 305,
"Earth Cave (B2) - Side Room 1": 307,
"Earth Cave (B2) - Side Room 2": 308,
"Earth Cave (B2) - Side Room 3": 309,
"Earth Cave (B2) - Guarded 1": 310,
"Earth Cave (B2) - Guarded 2": 311,
"Earth Cave (B2) - Guarded 3": 312,
"Earth Cave Vampire Floor (B3) - Side Room": 315,
"Earth Cave Vampire Floor (B3) - TFC": 316,
"Earth Cave Vampire Floor (B3) - Asher Trunk": 314,
"Earth Cave Vampire Floor (B3) - Vampire's Closet": 313,
"Earth Cave Vampire Floor (B3) - Incentive": 317,
"Earth Cave Rod Locked Floor (B4) - Armory 1": 321,
"Earth Cave Rod Locked Floor (B4) - Armory 2": 322,
"Earth Cave Rod Locked Floor (B4) - Armory 3": 325,
"Earth Cave Rod Locked Floor (B4) - Armory 4": 323,
"Earth Cave Rod Locked Floor (B4) - Armory 5": 324,
"Earth Cave Rod Locked Floor (B4) - Lich's Closet 1": 318,
"Earth Cave Rod Locked Floor (B4) - Lich's Closet 2": 319,
"Earth Cave Rod Locked Floor (B4) - Lich's Closet 3": 320,
"Gurgu Volcano Armory Floor (B2) - Guarded": 346,
"Gurgu Volcano Armory Floor (B2) - Center": 347,
"Gurgu Volcano Armory Floor (B2) - Hairpins": 344,
"Gurgu Volcano Armory Floor (B2) - Shortpins": 345,
"Gurgu Volcano Armory Floor (B2) - Vertpins 1": 342,
"Gurgu Volcano Armory Floor (B2) - Vertpins 2": 343,
"Gurgu Volcano Armory Floor (B2) - Armory 1": 338,
"Gurgu Volcano Armory Floor (B2) - Armory 2": 330,
"Gurgu Volcano Armory Floor (B2) - Armory 3": 331,
"Gurgu Volcano Armory Floor (B2) - Armory 4": 337,
"Gurgu Volcano Armory Floor (B2) - Armory 5": 335,
"Gurgu Volcano Armory Floor (B2) - Armory 6": 332,
"Gurgu Volcano Armory Floor (B2) - Armory 7": 333,
"Gurgu Volcano Armory Floor (B2) - Armory 8": 334,
"Gurgu Volcano Armory Floor (B2) - Armory 9": 341,
"Gurgu Volcano Armory Floor (B2) - Armory 10": 336,
"Gurgu Volcano Armory Floor (B2) - Armory 11": 340,
"Gurgu Volcano Armory Floor (B2) - Armory 12": 339,
"Gurgu Volcano Agama Floor (B4) - Entrance 1": 349,
"Gurgu Volcano Agama Floor (B4) - Entrance 2": 348,
"Gurgu Volcano Agama Floor (B4) - First Greed": 350,
"Gurgu Volcano Agama Floor (B4) - Worm Room 1": 361,
"Gurgu Volcano Agama Floor (B4) - Worm Room 2": 359,
"Gurgu Volcano Agama Floor (B4) - Worm Room 3": 360,
"Gurgu Volcano Agama Floor (B4) - Worm Room 4": 357,
"Gurgu Volcano Agama Floor (B4) - Worm Room 5": 358,
"Gurgu Volcano Agama Floor (B4) - Second Greed 1": 353,
"Gurgu Volcano Agama Floor (B4) - Second Greed 2": 354,
"Gurgu Volcano Agama Floor (B4) - Side Room 1": 355,
"Gurgu Volcano Agama Floor (B4) - Side Room 2": 356,
"Gurgu Volcano Agama Floor (B4) - Grind Room 1": 351,
"Gurgu Volcano Agama Floor (B4) - Grind Room 2": 352,
"Gurgu Volcano Kary Floor (B5) - Incentive": 362,
"Ice Cave Incentive Floor (B2) - Chest 1": 368,
"Ice Cave Incentive Floor (B2) - Chest 2": 369,
"Ice Cave Incentive Floor (B2) - Major": 370,
"Ice Cave Bottom (B3) - IceD Room 1": 377,
"Ice Cave Bottom (B3) - IceD Room 2": 378,
"Ice Cave Bottom (B3) - Six-Pack 1": 371,
"Ice Cave Bottom (B3) - Six-Pack 2": 372,
"Ice Cave Bottom (B3) - Six-Pack 3": 375,
"Ice Cave Bottom (B3) - Six-Pack 4": 373,
"Ice Cave Bottom (B3) - Six-Pack 5": 374,
"Ice Cave Bottom (B3) - Six-Pack 6": 376,
"Ice Cave Exit Floor (B1) - Greeds Checks 1": 363,
"Ice Cave Exit Floor (B1) - Greeds Checks 2": 364,
"Ice Cave Exit Floor (B1) - Drop Room 1": 365,
"Ice Cave Exit Floor (B1) - Drop Room 2": 366,
"Ice Cave Exit Floor (B1) - Drop Room 3": 367,
"Castle of Ordeals Top Floor (3F) - Single": 386,
"Castle of Ordeals Top Floor (3F) - Three-Pack 1": 383,
"Castle of Ordeals Top Floor (3F) - Three-Pack 2": 384,
"Castle of Ordeals Top Floor (3F) - Three-Pack 3": 385,
"Castle of Ordeals Top Floor (3F) - Four-Pack 1": 379,
"Castle of Ordeals Top Floor (3F) - Four-Pack 2": 380,
"Castle of Ordeals Top Floor (3F) - Four-Pack 3": 381,
"Castle of Ordeals Top Floor (3F) - Four-Pack 4": 382,
"Castle of Ordeals Top Floor (3F) - Incentive": 387,
"Sea Shrine Split Floor (B3) - Kraken Side": 415,
"Sea Shrine Split Floor (B3) - Mermaid Side": 416,
"Sea Shrine TFC Floor (B2) - TFC": 421,
"Sea Shrine TFC Floor (B2) - TFC North": 420,
"Sea Shrine TFC Floor (B2) - Side Corner": 419,
"Sea Shrine TFC Floor (B2) - First Greed": 422,
"Sea Shrine TFC Floor (B2) - Second Greed": 423,
"Sea Shrine Mermaids (B1) - Passby": 427,
"Sea Shrine Mermaids (B1) - Bubbles 1": 428,
"Sea Shrine Mermaids (B1) - Bubbles 2": 429,
"Sea Shrine Mermaids (B1) - Incentive 1": 434,
"Sea Shrine Mermaids (B1) - Incentive 2": 435,
"Sea Shrine Mermaids (B1) - Incentive Major": 436,
"Sea Shrine Mermaids (B1) - Entrance 1": 424,
"Sea Shrine Mermaids (B1) - Entrance 2": 425,
"Sea Shrine Mermaids (B1) - Entrance 3": 426,
"Sea Shrine Mermaids (B1) - Four-Corner First": 430,
"Sea Shrine Mermaids (B1) - Four-Corner Second": 431,
"Sea Shrine Mermaids (B1) - Four-Corner Third": 432,
"Sea Shrine Mermaids (B1) - Four-Corner Fourth": 433,
"Sea Shrine Greed Floor (B3) - Chest 1": 418,
"Sea Shrine Greed Floor (B3) - Chest 2": 417,
"Sea Shrine Sharknado Floor (B4) - Dengbait 1": 409,
"Sea Shrine Sharknado Floor (B4) - Dengbait 2": 410,
"Sea Shrine Sharknado Floor (B4) - Side Corner 1": 411,
"Sea Shrine Sharknado Floor (B4) - Side Corner 2": 412,
"Sea Shrine Sharknado Floor (B4) - Side Corner 3": 413,
"Sea Shrine Sharknado Floor (B4) - Exit": 414,
"Sea Shrine Sharknado Floor (B4) - Greed Room 1": 405,
"Sea Shrine Sharknado Floor (B4) - Greed Room 2": 406,
"Sea Shrine Sharknado Floor (B4) - Greed Room 3": 407,
"Sea Shrine Sharknado Floor (B4) - Greed Room 4": 408,
"Waterfall Cave - Chest 1": 437,
"Waterfall Cave - Chest 2": 438,
"Waterfall Cave - Chest 3": 439,
"Waterfall Cave - Chest 4": 440,
"Waterfall Cave - Chest 5": 441,
"Waterfall Cave - Chest 6": 442,
"Mirage Tower (1F) - Chest 1": 456,
"Mirage Tower (1F) - Chest 2": 452,
"Mirage Tower (1F) - Chest 3": 453,
"Mirage Tower (1F) - Chest 4": 455,
"Mirage Tower (1F) - Chest 5": 454,
"Mirage Tower (1F) - Chest 6": 459,
"Mirage Tower (1F) - Chest 7": 457,
"Mirage Tower (1F) - Chest 8": 458,
"Mirage Tower (2F) - Lesser 1": 469,
"Mirage Tower (2F) - Lesser 2": 468,
"Mirage Tower (2F) - Lesser 3": 467,
"Mirage Tower (2F) - Lesser 4": 466,
"Mirage Tower (2F) - Lesser 5": 465,
"Mirage Tower (2F) - Greater 1": 460,
"Mirage Tower (2F) - Greater 2": 461,
"Mirage Tower (2F) - Greater 3": 462,
"Mirage Tower (2F) - Greater 4": 463,
"Mirage Tower (2F) - Greater 5": 464,
"Sky Fortress Plus (1F) - Solo": 479,
"Sky Fortress Plus (1F) - Five-Pack 1": 474,
"Sky Fortress Plus (1F) - Five-Pack 2": 475,
"Sky Fortress Plus (1F) - Five-Pack 3": 476,
"Sky Fortress Plus (1F) - Five-Pack 4": 477,
"Sky Fortress Plus (1F) - Five-Pack 5": 478,
"Sky Fortress Plus (1F) - Four-Pack 1": 470,
"Sky Fortress Plus (1F) - Four-Pack 2": 471,
"Sky Fortress Plus (1F) - Four-Pack 3": 472,
"Sky Fortress Plus (1F) - Four-Pack 4": 473,
"Sky Fortress Spider (2F) - Cheap Room 1": 485,
"Sky Fortress Spider (2F) - Cheap Room 2": 486,
"Sky Fortress Spider (2F) - Vault 1": 487,
"Sky Fortress Spider (2F) - Vault 2": 488,
"Sky Fortress Spider (2F) - Incentive": 489,
"Sky Fortress Spider (2F) - Gauntlet Room": 483,
"Sky Fortress Spider (2F) - Ribbon Room 1": 482,
"Sky Fortress Spider (2F) - Ribbon Room 2": 484,
"Sky Fortress Spider (2F) - Wardrobe 1": 480,
"Sky Fortress Spider (2F) - Wardrobe 2": 481,
"Sky Fortress Provides (3F) - Six-Pack 1": 498,
"Sky Fortress Provides (3F) - Six-Pack 2": 495,
"Sky Fortress Provides (3F) - Six-Pack 3": 494,
"Sky Fortress Provides (3F) - Six-Pack 4": 497,
"Sky Fortress Provides (3F) - Six-Pack 5": 496,
"Sky Fortress Provides (3F) - Six-Pack 6": 499,
"Sky Fortress Provides (3F) - CC's Gambit 1": 500,
"Sky Fortress Provides (3F) - CC's Gambit 2": 501,
"Sky Fortress Provides (3F) - CC's Gambit 3": 502,
"Sky Fortress Provides (3F) - CC's Gambit 4": 503,
"Sky Fortress Provides (3F) - Greed 1": 491,
"Sky Fortress Provides (3F) - Greed 2": 490,
"Sky Fortress Provides (3F) - Greed 3": 492,
"Sky Fortress Provides (3F) - Greed 4": 493,
"Temple of Fiends Revisited (3F) - Validation 1": 509,
"Temple of Fiends Revisited (3F) - Validation 2": 510,
"Temple of Fiends Revisited Kary Floor (6F) - Greed Checks 1": 507,
"Temple of Fiends Revisited Kary Floor (6F) - Greed Checks 2": 508,
"Temple of Fiends Revisited Kary Floor (6F) - Katana Chest": 506,
"Temple of Fiends Revisited Kary Floor (6F) - Vault": 505,
"Temple of Fiends Revisited Tiamat Floor (8F) - Masamune Chest": 504,
"Shop Item": 767,
"King": 513,
"Princess2": 530,
"Princess": 530,
"Matoya": 522,
"Astos": 519,
"Bikke": 516,
"CanoeSage": 533,
"ElfPrince": 518,
"Canoe Sage": 533,
"Elf Prince": 518,
"Nerrick": 520,
"Smith": 521,
"CubeBot": 529,

View File

@@ -220,15 +220,12 @@ def stage_set_rules(multiworld):
for player in no_enemies_players:
for location in vendor_locations:
if multiworld.accessibility[player] == "locations":
print("exclude")
multiworld.get_location(location, player).progress_type = LocationProgressType.EXCLUDED
else:
print("unreachable")
multiworld.get_location(location, player).access_rule = lambda state: False
else:
# There are not enough junk items to fill non-minimal players' vendors. Just set an item rule not allowing
# advancement items so that useful items can be placed.
print("no advancement")
# advancement items so that useful items can be placed
for player in no_enemies_players:
for location in vendor_locations:
multiworld.get_location(location, player).item_rule = lambda item: not item.advancement

View File

@@ -41,12 +41,12 @@ def locality_rules(world: MultiWorld):
forbid_data[sender][receiver].update(items)
for receiving_player in world.player_ids:
local_items: typing.Set[str] = world.local_items[receiving_player].value
local_items: typing.Set[str] = world.worlds[receiving_player].options.local_items.value
if local_items:
for sending_player in world.player_ids:
if receiving_player != sending_player:
forbid(sending_player, receiving_player, local_items)
non_local_items: typing.Set[str] = world.non_local_items[receiving_player].value
non_local_items: typing.Set[str] = world.worlds[receiving_player].options.non_local_items.value
if non_local_items:
forbid(receiving_player, receiving_player, non_local_items)

View File

@@ -42,9 +42,9 @@ including the exclamation point.
### Collect/Release
- `!collect` Grants you all the remaining items for your world by collecting them from all games. Typically used after
goal completion.
- `!release` Releases all items contained in your world to other worlds. Typically, done automatically by the sever, but
can be configured to allow/require manual usage of this command.
goal completion.
- `!release` Releases all items contained in your world to other worlds. Typically, done automatically by the server,
but can be configured to allow/require manual usage of this command.
### Cheats
- `!getitem <item>` Cheats an item to the currently connected slot, if it is enabled in the server.

View File

@@ -161,8 +161,40 @@ into any locations within the game slots named BobsSlaytheSpire and BobsRogueLeg
## Boss Plando
As this is currently only supported by A Link to the Past, instead of finding an explanation here, please refer to the
relevant guide: [A Link to the Past Plando Guide](/tutorial/A%20Link%20to%20the%20Past/plando/en)
This is currently only supported by A Link to the Past and Kirby's Dream Land 3. Boss plando allows a player to place a
given boss within an arena. More specific information for boss plando in A Link to the Past can be found in
its [plando guide](/tutorial/A%20Link%20to%20the%20Past/plando/en).
Boss plando takes in a list of instructions for placing bosses, separated by a semicolon `;`.
There are three types of placement: direct, full, and shuffle.
* Direct placement takes both an arena and a boss, and places the boss into that arena.
* `Eastern Palace-Trinexx`
* Full placement will take a boss, and place it into as many remaining arenas as possible.
* `King Dedede`
* Shuffle will fill any remaining arenas using a given boss shuffle option, typically to be used as the last instruction.
* `full`
### Examples
```yaml
A Link to the Past:
boss_shuffle:
# Basic boss shuffle, but prevent Trinexx from being outside Turtle Rock
Turtle Rock-Trinexx;basic: 1
# Place as many Arrghus as possible, then let the rest be random
Arrghus;chaos: 1
Kirby's Dream Land 3:
boss_shuffle:
# Ensure Iceberg's boss will be King Dedede, but randomize the rest
Iceberg-King Dedede;full: 1
# Have all bosses be Whispy Woods
Whispy Woods: 1
# Ensure Ripple Field's boss is Pon & Con, but let the method others
# are placed with be random
Ripple Field-Pon & Con;random: 1
```
## Text Plando
@@ -171,20 +203,20 @@ relevant guide: [A Link to the Past Plando Guide](/tutorial/A%20Link%20to%20the%
## Connections Plando
This is currently only supported by Minecraft and A Link to the Past. As the way that these games interact with their
connections is different, I will only explain the basics here, while more specifics for A Link to the Past connection
plando can be found in its plando guide.
This is currently only supported by a few games, including A Link to the Past, Minecraft, and Ocarina of Time. As the way that these games interact with their
connections is different, only the basics are explained here. More specific information for connection plando in A Link to the Past can be found in
its [plando guide](/tutorial/A%20Link%20to%20the%20Past/plando/en#connections).
* The options for connections are `percentage`, `entrance`, `exit`, and `direction`. Each of these options supports
subweights.
* `percentage` is the percentage chance for this connection from 0 to 100 and defaults to 100.
* Every connection has an `entrance` and an `exit`. These can be unlinked like in A Link to the Past insanity entrance
shuffle.
* `direction` can be `both`, `entrance`, or `exit` and determines in which direction this connection will operate.
* `direction` can be `both`, `entrance`, or `exit` and determines in which direction this connection will operate. `direction` defaults to `both`.
[A Link to the Past connections](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/alttp/EntranceShuffle.py#L3852)
[Minecraft connections](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/minecraft/Regions.py#L62)
[Minecraft connections](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/minecraft/data/regions.json#L18****)
### Examples

434
worlds/kdl3/Aesthetics.py Normal file
View File

@@ -0,0 +1,434 @@
import struct
from .Options import KirbyFlavorPreset, GooeyFlavorPreset
kirby_flavor_presets = {
1: {
"1": "B50029",
"2": "FF91C6",
"3": "B0123B",
"4": "630F0F",
"5": "D60052",
"6": "DE4873",
"7": "D07880",
"8": "000000",
"9": "F770A5",
"10": "E01784",
"11": "CA4C74",
"12": "A7443F",
"13": "FF1784",
"14": "FFA1DE",
"15": "B03830",
},
2: {
"1": "C70057",
"2": "FF3554",
"3": "AA0040",
"4": "C02D47",
"5": "E02068",
"6": "C2183F",
"7": "D03F80",
"8": "872939",
"9": "E82B47",
"10": "E80067",
"11": "D52F40",
"12": "9F1C33",
"13": "FD187F",
"14": "F85068",
"15": "D2386F",
},
3: {
"1": "5858e2",
"2": "e6e6fa",
"3": "bcbcf2",
"4": "8484e6",
"5": "2929ec",
"6": "b5b5f0",
"7": "847bd6",
"8": "3232d6",
"9": "d6d6ef",
"10": "4a52ef",
"11": "c6c6e6",
"12": "4343ad",
"13": "6767ff",
"14": "f6f6fd",
"15": "3139b6",
},
4: {
"1": "B01810",
"2": "F0E08D",
"3": "C8A060",
"4": "A87043",
"5": "E03700",
"6": "EFC063",
"7": "D07818",
"8": "A8501C",
"9": "E8D070",
"10": "E2501E",
"11": "E8C55C",
"12": "B08833",
"13": "E8783B",
"14": "F8F8A5",
"15": "B03800",
},
5: {
"1": "9F4410",
"2": "88F27B",
"3": "57A044",
"4": "227029",
"5": "C75418",
"6": "57BA23",
"7": "1C6B00",
"8": "2D6823",
"9": "3FD744",
"10": "E06C16",
"11": "54C053",
"12": "1A541E",
"13": "F06B10",
"14": "98F89A",
"15": "B05830",
},
6: {
"1": "7C1060",
"2": "CA8AE8",
"3": "8250A5",
"4": "604B7B",
"5": "A52068",
"6": "8D64B8",
"7": "B73B80",
"8": "672D9A",
"9": "BA82D5",
"10": "B55098",
"11": "9F5CCF",
"12": "632B74",
"13": "CF78B5",
"14": "DA98F8",
"15": "8D3863",
},
7: {
"1": "6F1410",
"2": "C2735C",
"3": "5C351C",
"4": "875440",
"5": "9F2F0C",
"6": "874C3B",
"7": "88534C",
"8": "4C1E00",
"9": "B06458",
"10": "921C16",
"11": "9F5C54",
"12": "5B3125",
"13": "C01A14",
"14": "CF785B",
"15": "6B3125",
},
8: {
"1": "a6a6a6",
"2": "e6e6e6",
"3": "bcbcbc",
"4": "848484",
"5": "909090",
"6": "b5b5b5",
"7": "848484",
"8": "646464",
"9": "d6d6d6",
"10": "525252",
"11": "c6c6c6",
"12": "737373",
"13": "949494",
"14": "f6f6f6",
"15": "545454",
},
9: {
"1": "400000",
"2": "6B6B6B",
"3": "2B2B2B",
"4": "181818",
"5": "640000",
"6": "3D3D3D",
"7": "878787",
"8": "020202",
"9": "606060",
"10": "980000",
"11": "505050",
"12": "474747",
"13": "C80000",
"14": "808080",
"15": "AF0000",
},
10: {
"1": "2B4B10",
"2": "EF8A9D",
"3": "C84F6B",
"4": "B74F54",
"5": "126018",
"6": "D85F6F",
"7": "D06870",
"8": "A24858",
"9": "E77B8D",
"10": "168025",
"11": "DF5C68",
"12": "9D4353",
"13": "48953F",
"14": "F897AD",
"15": "B03830",
},
11: {
"1": "7B290C",
"2": "FF9A00",
"3": "B05C1C",
"4": "8F3F0E",
"5": "D23B0C",
"6": "E08200",
"7": "D05800",
"8": "8A2B16",
"9": "EF970A",
"10": "E24800",
"11": "E58F00",
"12": "A03700",
"13": "ED3B00",
"14": "FFAF27",
"15": "A84700",
},
12: {
"1": "AFA810",
"2": "4FF29D",
"3": "2BA04C",
"4": "007043",
"5": "C7C218",
"6": "33BA5F",
"7": "006B40",
"8": "2D6823",
"9": "1CD773",
"10": "E0CF16",
"11": "2DC06C",
"12": "00543F",
"13": "F0F010",
"14": "43F8B2",
"15": "B0A230",
},
13: {
"1": "7C73B0",
"2": "CACAE7",
"3": "7B7BA8",
"4": "5F5FA7",
"5": "B57EDC",
"6": "8585C5",
"7": "5B5B82",
"8": "474796",
"9": "B2B2D8",
"10": "B790EF",
"11": "9898C2",
"12": "6B6BB7",
"13": "CDADFA",
"14": "E6E6FA",
"15": "976FBD",
},
}
gooey_flavor_presets = {
1: {
"1": "CD539D",
"2": "D270AD",
"3": "F27CBF",
"4": "FF91C6",
"5": "FFA1DE",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
2: {
"1": "161600",
"2": "592910",
"3": "5A3118",
"4": "AB3918",
"5": "EB3918",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
3: {
"1": "001616",
"2": "102959",
"3": "18315A",
"4": "1839AB",
"5": "1839EB",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
4: {
"1": "C8A031",
"2": "C5BD38",
"3": "D2CD48",
"4": "E2E040",
"5": "EAE2A0",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
5: {
"1": "54A208",
"2": "5CB021",
"3": "6CB206",
"4": "8AC54C",
"5": "8DD554",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
6: {
"1": "3D083D",
"2": "4B024B",
"3": "4C104C",
"4": "5F0A5F",
"5": "9F1D9F",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
7: {
"1": "270C08",
"2": "481C10",
"3": "581E10",
"4": "5B2712",
"5": "743316",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
8: {
"1": "7F7F7F",
"2": "909090",
"3": "9D9D9D",
"4": "BFBFBF",
"5": "D2D2D2",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
9: {
"1": "141414",
"2": "2D2D2D",
"3": "404040",
"4": "585858",
"5": "7F7F7F",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
10: {
"1": "954353",
"2": "AF4F68",
"3": "CD6073",
"4": "E06774",
"5": "E587A2",
"6": "17AF10",
"7": "4FE748",
"8": "D6C6C6",
"9": "FFFFFF",
},
11: {
"1": "CF4700",
"2": "D85C08",
"3": "E26C04",
"4": "EA7B16",
"5": "EF8506",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
12: {
"1": "1C4708",
"2": "105B1C",
"3": "186827",
"4": "187C3B",
"5": "188831",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
13: {
"1": "501E70",
"2": "673B87",
"3": "7848A7",
"4": "9067C7",
"5": "B57EDC",
"6": "B51810",
"7": "EF524A",
"8": "D6C6C6",
"9": "FFFFFF",
},
}
kirby_target_palettes = {
0x64646: (["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], 0, 1),
0x64846: (["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], 0, 1),
0x1E007E: (["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], 0, 1),
0x1E009C: (["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], 0, 0.5),
0x1E00F6: (["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], 0, 1),
0x1E0114: (["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], 0, 0.5),
0x1E0216: (["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], 0, 1),
0x1E0234: (["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], 0, 0.5),
0x1E0486: (["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], 0, 1),
0x1E04A4: (["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"], 0, 0.5),
}
gooey_target_palettes = {
0x604C2: (["1", "2", "3", "4", "5", "6", "7", "8", "9"], 0, 1),
0x64592: (["1", "2", "3", "4", "5", "6", "7", "8", "9"], 0, 1),
0x64692: (["1", "2", "3", "4", "5", "6", "7", "8", "9"], 0, 1),
0x64892: (["1", "2", "3", "4", "5", "6", "7", "8", "9"], 0, 1),
0x1E02CA: (["1", "2", "3", "4", "5", "6", "7", "8", "9"], 0, 1),
0x1E0342: (["1", "2", "3", "4", "5", "6", "7", "8", "9"], 0, 1),
0x1E05A6: (["1", "2", "3", "4", "5", "6", "7", "8", "9"], 0, 1),
0x1E05B8: (["1", "2", "3", "4", "5", "6", "7", "8", "9"], 0, 0.5),
0x1E0636: (["1", "2", "3", "4", "5", "6", "7", "8", "9"], 0, 1),
0x1E065A: (["1", "2", "3", "4", "5", "6", "7", "8", "9"], 0, 1.5),
}
def get_kirby_palette(world):
palette = world.options.kirby_flavor_preset.value
if palette == KirbyFlavorPreset.option_custom:
return world.options.kirby_flavor.value
return kirby_flavor_presets.get(palette, None)
def get_gooey_palette(world):
palette = world.options.gooey_flavor_preset.value
if palette == GooeyFlavorPreset.option_custom:
return world.options.gooey_flavor.value
return gooey_flavor_presets.get(palette, None)
def rgb888_to_bgr555(red, green, blue) -> bytes:
red = red >> 3
green = green >> 3
blue = blue >> 3
outcol = (blue << 10) + (green << 5) + red
return struct.pack("H", outcol)
def get_palette_bytes(palette, target, offset, factor):
output_data = bytearray()
for color in target:
hexcol = palette[color]
if hexcol.startswith("#"):
hexcol = hexcol.replace("#", "")
colint = int(hexcol, 16)
col = ((colint & 0xFF0000) >> 16, (colint & 0xFF00) >> 8, colint & 0xFF)
col = tuple(int(int(factor*x) + offset) for x in col)
byte_data = rgb888_to_bgr555(col[0], col[1], col[2])
output_data.extend(bytearray(byte_data))
return output_data

417
worlds/kdl3/Client.py Normal file
View File

@@ -0,0 +1,417 @@
import logging
import struct
import time
import typing
import uuid
from struct import unpack, pack
from collections import defaultdict
import random
from MultiServer import mark_raw
from NetUtils import ClientStatus, color
from Utils import async_start
from worlds.AutoSNIClient import SNIClient
from .Locations import boss_locations
from .Gifting import kdl3_gifting_options, kdl3_trap_gifts, kdl3_gifts, update_object, pop_object, initialize_giftboxes
from .ClientAddrs import consumable_addrs, star_addrs
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from SNIClient import SNIClientCommandProcessor
snes_logger = logging.getLogger("SNES")
# FXPAK Pro protocol memory mapping used by SNI
ROM_START = 0x000000
SRAM_1_START = 0xE00000
# KDL3
KDL3_HALKEN = SRAM_1_START + 0x80F0
KDL3_NINTEN = SRAM_1_START + 0x8FF0
KDL3_ROMNAME = SRAM_1_START + 0x8100
KDL3_DEATH_LINK_ADDR = SRAM_1_START + 0x9010
KDL3_GOAL_ADDR = SRAM_1_START + 0x9012
KDL3_CONSUMABLE_FLAG = SRAM_1_START + 0x9018
KDL3_STARS_FLAG = SRAM_1_START + 0x901A
KDL3_GIFTING_FLAG = SRAM_1_START + 0x901C
KDL3_LEVEL_ADDR = SRAM_1_START + 0x9020
KDL3_IS_DEMO = SRAM_1_START + 0x5AD5
KDL3_GAME_STATE = SRAM_1_START + 0x36D0
KDL3_GAME_SAVE = SRAM_1_START + 0x3617
KDL3_LIFE_COUNT = SRAM_1_START + 0x39CF
KDL3_KIRBY_HP = SRAM_1_START + 0x39D1
KDL3_BOSS_HP = SRAM_1_START + 0x39D5
KDL3_STAR_COUNT = SRAM_1_START + 0x39D7
KDL3_LIFE_VISUAL = SRAM_1_START + 0x39E3
KDL3_HEART_STARS = SRAM_1_START + 0x53A7
KDL3_WORLD_UNLOCK = SRAM_1_START + 0x53CB
KDL3_LEVEL_UNLOCK = SRAM_1_START + 0x53CD
KDL3_CURRENT_WORLD = SRAM_1_START + 0x53CF
KDL3_CURRENT_LEVEL = SRAM_1_START + 0x53D3
KDL3_BOSS_STATUS = SRAM_1_START + 0x53D5
KDL3_INVINCIBILITY_TIMER = SRAM_1_START + 0x54B1
KDL3_MG5_STATUS = SRAM_1_START + 0x5EE4
KDL3_BOSS_BUTCH_STATUS = SRAM_1_START + 0x5EEA
KDL3_JUMPING_STATUS = SRAM_1_START + 0x5EF0
KDL3_CURRENT_BGM = SRAM_1_START + 0x733E
KDL3_SOUND_FX = SRAM_1_START + 0x7F62
KDL3_ANIMAL_FRIENDS = SRAM_1_START + 0x8000
KDL3_ABILITY_ARRAY = SRAM_1_START + 0x8020
KDL3_RECV_COUNT = SRAM_1_START + 0x8050
KDL3_HEART_STAR_COUNT = SRAM_1_START + 0x8070
KDL3_GOOEY_TRAP = SRAM_1_START + 0x8080
KDL3_SLOWNESS_TRAP = SRAM_1_START + 0x8082
KDL3_ABILITY_TRAP = SRAM_1_START + 0x8084
KDL3_GIFTING_SEND = SRAM_1_START + 0x8086
KDL3_COMPLETED_STAGES = SRAM_1_START + 0x8200
KDL3_CONSUMABLES = SRAM_1_START + 0xA000
KDL3_STARS = SRAM_1_START + 0xB000
KDL3_ITEM_QUEUE = SRAM_1_START + 0xC000
deathlink_messages = defaultdict(lambda: " was defeated.", {
0x0200: " was bonked by apples from Whispy Woods.",
0x0201: " was out-maneuvered by Acro.",
0x0202: " was out-numbered by Pon & Con.",
0x0203: " was defeated by Ado's powerful paintings.",
0x0204: " was clobbered by King Dedede.",
0x0205: " lost their battle against Dark Matter."
})
@mark_raw
def cmd_gift(self: "SNIClientCommandProcessor"):
"""Toggles gifting for the current game."""
if not getattr(self.ctx, "gifting", None):
self.ctx.gifting = True
else:
self.ctx.gifting = not self.ctx.gifting
self.output(f"Gifting set to {self.ctx.gifting}")
async_start(update_object(self.ctx, f"Giftboxes;{self.ctx.team}", {
f"{self.ctx.slot}":
{
"IsOpen": self.ctx.gifting,
**kdl3_gifting_options
}
}))
class KDL3SNIClient(SNIClient):
game = "Kirby's Dream Land 3"
levels = None
consumables = None
stars = None
item_queue: typing.List = []
initialize_gifting = False
giftbox_key: str = ""
motherbox_key: str = ""
client_random: random.Random = random.Random()
async def deathlink_kill_player(self, ctx) -> None:
from SNIClient import DeathState, snes_buffered_write, snes_flush_writes, snes_read
game_state = await snes_read(ctx, KDL3_GAME_STATE, 1)
if game_state[0] == 0xFF:
return # despite how funny it is, don't try to kill Kirby in a menu
current_stage = await snes_read(ctx, KDL3_CURRENT_LEVEL, 1)
if current_stage[0] == 0x7: # boss stage
boss_hp = await snes_read(ctx, KDL3_BOSS_HP, 1)
if boss_hp[0] == 0:
return # receiving a deathlink after defeating a boss has softlock potential
current_hp = await snes_read(ctx, KDL3_KIRBY_HP, 1)
if current_hp[0] == 0:
return # don't kill Kirby while he's already dead
snes_buffered_write(ctx, KDL3_KIRBY_HP, bytes([0x00]))
await snes_flush_writes(ctx)
ctx.death_state = DeathState.dead
ctx.last_death_link = time.time()
async def validate_rom(self, ctx) -> bool:
from SNIClient import snes_read
rom_name = await snes_read(ctx, KDL3_ROMNAME, 0x15)
if rom_name is None or rom_name == bytes([0] * 0x15) or rom_name[:4] != b"KDL3":
if "gift" in ctx.command_processor.commands:
ctx.command_processor.commands.pop("gift")
return False
ctx.game = self.game
ctx.rom = rom_name
ctx.items_handling = 0b111 # always remote items
ctx.allow_collect = True
if "gift" not in ctx.command_processor.commands:
ctx.command_processor.commands["gift"] = cmd_gift
death_link = await snes_read(ctx, KDL3_DEATH_LINK_ADDR, 1)
if death_link:
await ctx.update_death_link(bool(death_link[0] & 0b1))
return True
async def pop_item(self, ctx, in_stage):
from SNIClient import snes_buffered_write, snes_read
if len(self.item_queue) > 0:
item = self.item_queue.pop()
if not in_stage and item & 0xC0:
# can't handle this item right now, send it to the back and return to handle the rest
self.item_queue.append(item)
return
ingame_queue = list(unpack("HHHHHHHH", await snes_read(ctx, KDL3_ITEM_QUEUE, 16)))
for i in range(len(ingame_queue)):
if ingame_queue[i] == 0x00:
ingame_queue[i] = item
snes_buffered_write(ctx, KDL3_ITEM_QUEUE, pack("HHHHHHHH", *ingame_queue))
break
else:
self.item_queue.append(item) # no more slots, get it next go around
async def pop_gift(self, ctx):
if ctx.stored_data[self.giftbox_key]:
from SNIClient import snes_read, snes_buffered_write
key, gift = ctx.stored_data[self.giftbox_key].popitem()
await pop_object(ctx, self.giftbox_key, key)
# first, special cases
traits = [trait["Trait"] for trait in gift["Traits"]]
if "Candy" in traits or "Invincible" in traits:
# apply invincibility candy
self.item_queue.append(0x43)
elif "Tomato" in traits or "tomato" in gift["ItemName"].lower():
# apply maxim tomato
# only want tomatos here, no other vegetable is that good
self.item_queue.append(0x42)
elif "Life" in traits:
# Apply 1-Up
self.item_queue.append(0x41)
elif "Currency" in traits or "Star" in traits:
value = gift["ItemValue"]
if value >= 50000:
self.item_queue.append(0x46)
elif value >= 30000:
self.item_queue.append(0x45)
else:
self.item_queue.append(0x44)
elif "Trap" in traits:
# find the best trap to apply
if "Goo" in traits or "Gel" in traits:
self.item_queue.append(0x80)
elif "Slow" in traits or "Slowness" in traits:
self.item_queue.append(0x81)
elif "Eject" in traits or "Removal" in traits:
self.item_queue.append(0x82)
else:
# just deal damage to Kirby
kirby_hp = struct.unpack("H", await snes_read(ctx, KDL3_KIRBY_HP, 2))[0]
snes_buffered_write(ctx, KDL3_KIRBY_HP, struct.pack("H", max(kirby_hp - 1, 0)))
else:
# check if it's tasty
if any(x in traits for x in ["Consumable", "Food", "Drink", "Heal", "Health"]):
# it's tasty!, use quality to decide how much to heal
quality = max((trait["Quality"] for trait in gift["Traits"]
if trait["Trait"] in ["Consumable", "Food", "Drink", "Heal", "Health"]))
quality = min(10, quality * 2)
else:
# it's not really edible, but he'll eat it anyway
quality = self.client_random.choices(range(0, 2), {0: 75, 1: 25})[0]
kirby_hp = await snes_read(ctx, KDL3_KIRBY_HP, 1)
gooey_hp = await snes_read(ctx, KDL3_KIRBY_HP + 2, 1)
snes_buffered_write(ctx, KDL3_SOUND_FX, bytes([0x26]))
if gooey_hp[0] > 0x00:
snes_buffered_write(ctx, KDL3_KIRBY_HP, struct.pack("H", min(kirby_hp[0] + quality // 2, 8)))
snes_buffered_write(ctx, KDL3_KIRBY_HP + 2, struct.pack("H", min(gooey_hp[0] + quality // 2, 8)))
else:
snes_buffered_write(ctx, KDL3_KIRBY_HP, struct.pack("H", min(kirby_hp[0] + quality, 10)))
async def pick_gift_recipient(self, ctx, gift):
if gift != 4:
gift_base = kdl3_gifts[gift]
else:
gift_base = kdl3_trap_gifts[self.client_random.randint(0, 3)]
most_applicable = -1
most_applicable_slot = ctx.slot
for slot, info in ctx.stored_data[self.motherbox_key].items():
if int(slot) == ctx.slot and len(ctx.stored_data[self.motherbox_key]) > 1:
continue
desire = len(set(info["DesiredTraits"]).intersection([trait["Trait"] for trait in gift_base["Traits"]]))
if desire > most_applicable:
most_applicable = desire
most_applicable_slot = int(slot)
elif most_applicable_slot == ctx.slot and info["AcceptsAnyGift"]:
# only send to ourselves if no one else will take it
most_applicable_slot = int(slot)
# print(most_applicable, most_applicable_slot)
item_uuid = uuid.uuid4().hex
item = {
**gift_base,
"ID": item_uuid,
"Sender": ctx.player_names[ctx.slot],
"Receiver": ctx.player_names[most_applicable_slot],
"SenderTeam": ctx.team,
"ReceiverTeam": ctx.team, # for the moment
"IsRefund": False
}
# print(item)
await update_object(ctx, f"Giftbox;{ctx.team};{most_applicable_slot}", {
item_uuid: item,
})
async def game_watcher(self, ctx) -> None:
try:
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
rom = await snes_read(ctx, KDL3_ROMNAME, 0x15)
if rom != ctx.rom:
ctx.rom = None
halken = await snes_read(ctx, KDL3_HALKEN, 6)
if halken != b"halken":
return
ninten = await snes_read(ctx, KDL3_NINTEN, 6)
if ninten != b"ninten":
return
if not ctx.slot:
return
if not self.initialize_gifting:
self.giftbox_key = f"Giftbox;{ctx.team};{ctx.slot}"
self.motherbox_key = f"Giftboxes;{ctx.team}"
enable_gifting = await snes_read(ctx, KDL3_GIFTING_FLAG, 0x01)
await initialize_giftboxes(ctx, self.giftbox_key, self.motherbox_key, bool(enable_gifting[0]))
self.initialize_gifting = True
# can't check debug anymore, without going and copying the value. might be important later.
if self.levels is None:
self.levels = dict()
for i in range(5):
level_data = await snes_read(ctx, KDL3_LEVEL_ADDR + (14 * i), 14)
self.levels[i] = unpack("HHHHHHH", level_data)
if self.consumables is None:
consumables = await snes_read(ctx, KDL3_CONSUMABLE_FLAG, 1)
self.consumables = consumables[0] == 0x01
if self.stars is None:
stars = await snes_read(ctx, KDL3_STARS_FLAG, 1)
self.stars = stars[0] == 0x01
is_demo = await snes_read(ctx, KDL3_IS_DEMO, 1)
# 1 - recording a demo, 2 - playing back recorded, 3+ is a demo
if is_demo[0] > 0x00:
return
current_save = await snes_read(ctx, KDL3_GAME_SAVE, 1)
goal = await snes_read(ctx, KDL3_GOAL_ADDR, 1)
boss_butch_status = await snes_read(ctx, KDL3_BOSS_BUTCH_STATUS + (current_save[0] * 2), 1)
mg5_status = await snes_read(ctx, KDL3_MG5_STATUS + (current_save[0] * 2), 1)
jumping_status = await snes_read(ctx, KDL3_JUMPING_STATUS + (current_save[0] * 2), 1)
if boss_butch_status[0] == 0xFF:
return # save file is not created, ignore
if (goal[0] == 0x00 and boss_butch_status[0] == 0x01) \
or (goal[0] == 0x01 and boss_butch_status[0] == 0x03) \
or (goal[0] == 0x02 and mg5_status[0] == 0x03) \
or (goal[0] == 0x03 and jumping_status[0] == 0x03):
await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}])
ctx.finished_game = True
current_bgm = await snes_read(ctx, KDL3_CURRENT_BGM, 1)
if current_bgm[0] in (0x00, 0x21, 0x22, 0x23, 0x25, 0x2A, 0x2B):
return # null, title screen, opening, save select, true and false endings
game_state = await snes_read(ctx, KDL3_GAME_STATE, 1)
current_hp = await snes_read(ctx, KDL3_KIRBY_HP, 1)
if "DeathLink" in ctx.tags and game_state[0] == 0x00 and ctx.last_death_link + 1 < time.time():
currently_dead = current_hp[0] == 0x00
await ctx.handle_deathlink_state(currently_dead)
recv_count = await snes_read(ctx, KDL3_RECV_COUNT, 2)
recv_amount = unpack("H", recv_count)[0]
if recv_amount < len(ctx.items_received):
item = ctx.items_received[recv_amount]
recv_amount += 1
logging.info('Received %s from %s (%s) (%d/%d in list)' % (
color(ctx.item_names[item.item], 'red', 'bold'),
color(ctx.player_names[item.player], 'yellow'),
ctx.location_names[item.location], recv_amount, len(ctx.items_received)))
snes_buffered_write(ctx, KDL3_RECV_COUNT, pack("H", recv_amount))
item_idx = item.item & 0x00000F
if item.item & 0x000070 == 0:
self.item_queue.append(item_idx | 0x10)
elif item.item & 0x000010 > 0:
self.item_queue.append(item_idx | 0x20)
elif item.item & 0x000020 > 0:
# Positive
self.item_queue.append(item_idx | 0x40)
elif item.item & 0x000040 > 0:
self.item_queue.append(item_idx | 0x80)
# handle gifts here
gifting_status = await snes_read(ctx, KDL3_GIFTING_FLAG, 0x01)
if hasattr(ctx, "gifting") and ctx.gifting:
if gifting_status[0]:
gift = await snes_read(ctx, KDL3_GIFTING_SEND, 0x01)
if gift[0]:
# we have a gift to send
await self.pick_gift_recipient(ctx, gift[0])
snes_buffered_write(ctx, KDL3_GIFTING_SEND, bytes([0x00]))
else:
snes_buffered_write(ctx, KDL3_GIFTING_FLAG, bytes([0x01]))
else:
if gifting_status[0]:
snes_buffered_write(ctx, KDL3_GIFTING_FLAG, bytes([0x00]))
await snes_flush_writes(ctx)
new_checks = []
# level completion status
world_unlocks = await snes_read(ctx, KDL3_WORLD_UNLOCK, 1)
if world_unlocks[0] > 0x06:
return # save is not loaded, ignore
stages_raw = await snes_read(ctx, KDL3_COMPLETED_STAGES, 60)
stages = struct.unpack("HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH", stages_raw)
for i in range(30):
loc_id = 0x770000 + i + 1
if stages[i] == 1 and loc_id not in ctx.checked_locations:
new_checks.append(loc_id)
elif loc_id in ctx.checked_locations:
snes_buffered_write(ctx, KDL3_COMPLETED_STAGES + (i * 2), struct.pack("H", 1))
# heart star status
heart_stars = await snes_read(ctx, KDL3_HEART_STARS, 35)
for i in range(5):
start_ind = i * 7
for j in range(1, 7):
level_ind = start_ind + j - 1
loc_id = 0x770100 + (6 * i) + j
if heart_stars[level_ind] and loc_id not in ctx.checked_locations:
new_checks.append(loc_id)
elif loc_id in ctx.checked_locations:
snes_buffered_write(ctx, KDL3_HEART_STARS + level_ind, bytes([0x01]))
if self.consumables:
consumables = await snes_read(ctx, KDL3_CONSUMABLES, 1920)
for consumable in consumable_addrs:
# TODO: see if this can be sped up in any way
loc_id = 0x770300 + consumable
if loc_id not in ctx.checked_locations and consumables[consumable_addrs[consumable]] == 0x01:
new_checks.append(loc_id)
if self.stars:
stars = await snes_read(ctx, KDL3_STARS, 1920)
for star in star_addrs:
if star not in ctx.checked_locations and stars[star_addrs[star]] == 0x01:
new_checks.append(star)
if game_state[0] != 0xFF:
await self.pop_gift(ctx)
await self.pop_item(ctx, game_state[0] != 0xFF)
await snes_flush_writes(ctx)
# boss status
boss_flag_bytes = await snes_read(ctx, KDL3_BOSS_STATUS, 2)
boss_flag = unpack("H", boss_flag_bytes)[0]
for bitmask, boss in zip(range(1, 11, 2), boss_locations.keys()):
if boss_flag & (1 << bitmask) > 0 and boss not in ctx.checked_locations:
new_checks.append(boss)
for new_check_id in new_checks:
ctx.locations_checked.add(new_check_id)
location = ctx.location_names[new_check_id]
snes_logger.info(
f'New Check: {location} ({len(ctx.locations_checked)}/{len(ctx.missing_locations) + len(ctx.checked_locations)})')
await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": [new_check_id]}])
except Exception as ex:
# we crashed, so print log and clean up
snes_logger.error("", exc_info=ex)
if "gift" in ctx.command_processor.commands:
ctx.command_processor.commands.pop("gift")
ctx.rom = None
ctx.game = None

816
worlds/kdl3/ClientAddrs.py Normal file
View File

@@ -0,0 +1,816 @@
consumable_addrs = {
0: 14,
1: 15,
2: 84,
3: 138,
4: 139,
5: 204,
6: 214,
7: 215,
8: 224,
9: 330,
10: 353,
11: 458,
12: 459,
13: 522,
14: 525,
15: 605,
16: 606,
17: 630,
18: 671,
19: 672,
20: 693,
21: 791,
22: 851,
23: 883,
24: 971,
25: 985,
26: 986,
27: 1024,
28: 1035,
29: 1036,
30: 1038,
31: 1039,
32: 1170,
33: 1171,
34: 1377,
35: 1378,
36: 1413,
37: 1494,
38: 1666,
39: 1808,
40: 1809,
41: 1816,
42: 1856,
43: 1857,
}
star_addrs = {
0x770401: 0,
0x770402: 1,
0x770403: 2,
0x770404: 3,
0x770405: 4,
0x770406: 5,
0x770407: 7,
0x770408: 8,
0x770409: 9,
0x77040a: 10,
0x77040b: 11,
0x77040c: 12,
0x77040d: 13,
0x77040e: 16,
0x77040f: 17,
0x770410: 19,
0x770411: 20,
0x770412: 21,
0x770413: 22,
0x770414: 23,
0x770415: 24,
0x770416: 25,
0x770417: 26,
0x770418: 65,
0x770419: 66,
0x77041a: 67,
0x77041b: 68,
0x77041c: 69,
0x77041d: 70,
0x77041e: 71,
0x77041f: 72,
0x770420: 73,
0x770421: 74,
0x770422: 76,
0x770423: 77,
0x770424: 78,
0x770425: 79,
0x770426: 80,
0x770427: 81,
0x770428: 82,
0x770429: 83,
0x77042a: 85,
0x77042b: 86,
0x77042c: 87,
0x77042d: 128,
0x77042e: 129,
0x77042f: 130,
0x770430: 131,
0x770431: 132,
0x770432: 133,
0x770433: 134,
0x770434: 135,
0x770435: 136,
0x770436: 137,
0x770437: 140,
0x770438: 141,
0x770439: 142,
0x77043a: 143,
0x77043b: 144,
0x77043c: 145,
0x77043d: 146,
0x77043e: 147,
0x77043f: 148,
0x770440: 149,
0x770441: 150,
0x770442: 151,
0x770443: 152,
0x770444: 153,
0x770445: 154,
0x770446: 155,
0x770447: 156,
0x770448: 157,
0x770449: 158,
0x77044a: 159,
0x77044b: 160,
0x77044c: 192,
0x77044d: 193,
0x77044e: 194,
0x77044f: 195,
0x770450: 197,
0x770451: 198,
0x770452: 199,
0x770453: 200,
0x770454: 201,
0x770455: 203,
0x770456: 205,
0x770457: 206,
0x770458: 207,
0x770459: 208,
0x77045a: 209,
0x77045b: 210,
0x77045c: 211,
0x77045d: 212,
0x77045e: 213,
0x77045f: 216,
0x770460: 217,
0x770461: 218,
0x770462: 219,
0x770463: 220,
0x770464: 221,
0x770465: 222,
0x770466: 225,
0x770467: 227,
0x770468: 228,
0x770469: 229,
0x77046a: 230,
0x77046b: 231,
0x77046c: 232,
0x77046d: 233,
0x77046e: 234,
0x77046f: 235,
0x770470: 236,
0x770471: 257,
0x770472: 258,
0x770473: 259,
0x770474: 260,
0x770475: 261,
0x770476: 262,
0x770477: 263,
0x770478: 264,
0x770479: 265,
0x77047a: 266,
0x77047b: 267,
0x77047c: 268,
0x77047d: 270,
0x77047e: 271,
0x77047f: 272,
0x770480: 273,
0x770481: 275,
0x770482: 276,
0x770483: 277,
0x770484: 278,
0x770485: 279,
0x770486: 280,
0x770487: 281,
0x770488: 282,
0x770489: 283,
0x77048a: 284,
0x77048b: 285,
0x77048c: 286,
0x77048d: 287,
0x77048e: 321,
0x77048f: 322,
0x770490: 323,
0x770491: 324,
0x770492: 325,
0x770493: 326,
0x770494: 327,
0x770495: 328,
0x770496: 329,
0x770497: 332,
0x770498: 334,
0x770499: 335,
0x77049a: 336,
0x77049b: 337,
0x77049c: 340,
0x77049d: 341,
0x77049e: 342,
0x77049f: 343,
0x7704a0: 345,
0x7704a1: 346,
0x7704a2: 347,
0x7704a3: 348,
0x7704a4: 349,
0x7704a5: 350,
0x7704a6: 351,
0x7704a7: 354,
0x7704a8: 355,
0x7704a9: 356,
0x7704aa: 357,
0x7704ab: 384,
0x7704ac: 385,
0x7704ad: 386,
0x7704ae: 387,
0x7704af: 388,
0x7704b0: 389,
0x7704b1: 391,
0x7704b2: 392,
0x7704b3: 393,
0x7704b4: 394,
0x7704b5: 396,
0x7704b6: 397,
0x7704b7: 398,
0x7704b8: 399,
0x7704b9: 400,
0x7704ba: 401,
0x7704bb: 402,
0x7704bc: 403,
0x7704bd: 404,
0x7704be: 449,
0x7704bf: 450,
0x7704c0: 451,
0x7704c1: 453,
0x7704c2: 454,
0x7704c3: 455,
0x7704c4: 456,
0x7704c5: 457,
0x7704c6: 460,
0x7704c7: 461,
0x7704c8: 462,
0x7704c9: 463,
0x7704ca: 464,
0x7704cb: 465,
0x7704cc: 466,
0x7704cd: 467,
0x7704ce: 468,
0x7704cf: 513,
0x7704d0: 514,
0x7704d1: 515,
0x7704d2: 516,
0x7704d3: 517,
0x7704d4: 518,
0x7704d5: 519,
0x7704d6: 520,
0x7704d7: 521,
0x7704d8: 523,
0x7704d9: 524,
0x7704da: 527,
0x7704db: 528,
0x7704dc: 529,
0x7704dd: 531,
0x7704de: 532,
0x7704df: 533,
0x7704e0: 534,
0x7704e1: 535,
0x7704e2: 536,
0x7704e3: 537,
0x7704e4: 576,
0x7704e5: 577,
0x7704e6: 578,
0x7704e7: 579,
0x7704e8: 580,
0x7704e9: 582,
0x7704ea: 583,
0x7704eb: 584,
0x7704ec: 585,
0x7704ed: 586,
0x7704ee: 587,
0x7704ef: 588,
0x7704f0: 589,
0x7704f1: 590,
0x7704f2: 591,
0x7704f3: 592,
0x7704f4: 593,
0x7704f5: 594,
0x7704f6: 595,
0x7704f7: 596,
0x7704f8: 597,
0x7704f9: 598,
0x7704fa: 599,
0x7704fb: 600,
0x7704fc: 601,
0x7704fd: 602,
0x7704fe: 603,
0x7704ff: 604,
0x770500: 607,
0x770501: 608,
0x770502: 609,
0x770503: 610,
0x770504: 611,
0x770505: 612,
0x770506: 613,
0x770507: 614,
0x770508: 615,
0x770509: 616,
0x77050a: 617,
0x77050b: 618,
0x77050c: 619,
0x77050d: 620,
0x77050e: 621,
0x77050f: 622,
0x770510: 623,
0x770511: 624,
0x770512: 625,
0x770513: 626,
0x770514: 627,
0x770515: 628,
0x770516: 629,
0x770517: 640,
0x770518: 641,
0x770519: 642,
0x77051a: 643,
0x77051b: 644,
0x77051c: 645,
0x77051d: 646,
0x77051e: 647,
0x77051f: 648,
0x770520: 649,
0x770521: 650,
0x770522: 651,
0x770523: 652,
0x770524: 653,
0x770525: 654,
0x770526: 655,
0x770527: 656,
0x770528: 657,
0x770529: 658,
0x77052a: 659,
0x77052b: 660,
0x77052c: 661,
0x77052d: 662,
0x77052e: 663,
0x77052f: 664,
0x770530: 665,
0x770531: 666,
0x770532: 667,
0x770533: 668,
0x770534: 669,
0x770535: 670,
0x770536: 674,
0x770537: 675,
0x770538: 676,
0x770539: 677,
0x77053a: 678,
0x77053b: 679,
0x77053c: 680,
0x77053d: 681,
0x77053e: 682,
0x77053f: 683,
0x770540: 684,
0x770541: 686,
0x770542: 687,
0x770543: 688,
0x770544: 689,
0x770545: 690,
0x770546: 691,
0x770547: 692,
0x770548: 694,
0x770549: 695,
0x77054a: 704,
0x77054b: 705,
0x77054c: 706,
0x77054d: 707,
0x77054e: 708,
0x77054f: 709,
0x770550: 710,
0x770551: 711,
0x770552: 712,
0x770553: 713,
0x770554: 714,
0x770555: 715,
0x770556: 716,
0x770557: 717,
0x770558: 718,
0x770559: 719,
0x77055a: 720,
0x77055b: 721,
0x77055c: 722,
0x77055d: 723,
0x77055e: 724,
0x77055f: 725,
0x770560: 726,
0x770561: 769,
0x770562: 770,
0x770563: 771,
0x770564: 772,
0x770565: 773,
0x770566: 774,
0x770567: 775,
0x770568: 776,
0x770569: 777,
0x77056a: 778,
0x77056b: 779,
0x77056c: 780,
0x77056d: 781,
0x77056e: 782,
0x77056f: 783,
0x770570: 784,
0x770571: 785,
0x770572: 786,
0x770573: 787,
0x770574: 788,
0x770575: 789,
0x770576: 790,
0x770577: 832,
0x770578: 833,
0x770579: 834,
0x77057a: 835,
0x77057b: 836,
0x77057c: 837,
0x77057d: 838,
0x77057e: 839,
0x77057f: 840,
0x770580: 841,
0x770581: 842,
0x770582: 843,
0x770583: 844,
0x770584: 845,
0x770585: 846,
0x770586: 847,
0x770587: 848,
0x770588: 849,
0x770589: 850,
0x77058a: 854,
0x77058b: 855,
0x77058c: 856,
0x77058d: 857,
0x77058e: 858,
0x77058f: 859,
0x770590: 860,
0x770591: 861,
0x770592: 862,
0x770593: 863,
0x770594: 864,
0x770595: 865,
0x770596: 866,
0x770597: 867,
0x770598: 868,
0x770599: 869,
0x77059a: 870,
0x77059b: 871,
0x77059c: 872,
0x77059d: 873,
0x77059e: 874,
0x77059f: 875,
0x7705a0: 876,
0x7705a1: 877,
0x7705a2: 878,
0x7705a3: 879,
0x7705a4: 880,
0x7705a5: 881,
0x7705a6: 882,
0x7705a7: 896,
0x7705a8: 897,
0x7705a9: 898,
0x7705aa: 899,
0x7705ab: 900,
0x7705ac: 901,
0x7705ad: 902,
0x7705ae: 903,
0x7705af: 904,
0x7705b0: 905,
0x7705b1: 960,
0x7705b2: 961,
0x7705b3: 962,
0x7705b4: 963,
0x7705b5: 964,
0x7705b6: 965,
0x7705b7: 966,
0x7705b8: 967,
0x7705b9: 968,
0x7705ba: 969,
0x7705bb: 970,
0x7705bc: 972,
0x7705bd: 973,
0x7705be: 974,
0x7705bf: 975,
0x7705c0: 977,
0x7705c1: 978,
0x7705c2: 979,
0x7705c3: 980,
0x7705c4: 981,
0x7705c5: 982,
0x7705c6: 983,
0x7705c7: 984,
0x7705c8: 1025,
0x7705c9: 1026,
0x7705ca: 1027,
0x7705cb: 1028,
0x7705cc: 1029,
0x7705cd: 1030,
0x7705ce: 1031,
0x7705cf: 1032,
0x7705d0: 1033,
0x7705d1: 1034,
0x7705d2: 1037,
0x7705d3: 1040,
0x7705d4: 1041,
0x7705d5: 1042,
0x7705d6: 1043,
0x7705d7: 1044,
0x7705d8: 1045,
0x7705d9: 1046,
0x7705da: 1049,
0x7705db: 1050,
0x7705dc: 1051,
0x7705dd: 1052,
0x7705de: 1053,
0x7705df: 1054,
0x7705e0: 1055,
0x7705e1: 1056,
0x7705e2: 1057,
0x7705e3: 1058,
0x7705e4: 1059,
0x7705e5: 1060,
0x7705e6: 1061,
0x7705e7: 1062,
0x7705e8: 1063,
0x7705e9: 1064,
0x7705ea: 1065,
0x7705eb: 1066,
0x7705ec: 1067,
0x7705ed: 1068,
0x7705ee: 1069,
0x7705ef: 1070,
0x7705f0: 1152,
0x7705f1: 1154,
0x7705f2: 1155,
0x7705f3: 1156,
0x7705f4: 1157,
0x7705f5: 1158,
0x7705f6: 1159,
0x7705f7: 1160,
0x7705f8: 1161,
0x7705f9: 1162,
0x7705fa: 1163,
0x7705fb: 1164,
0x7705fc: 1165,
0x7705fd: 1166,
0x7705fe: 1167,
0x7705ff: 1168,
0x770600: 1169,
0x770601: 1173,
0x770602: 1174,
0x770603: 1175,
0x770604: 1176,
0x770605: 1177,
0x770606: 1178,
0x770607: 1216,
0x770608: 1217,
0x770609: 1218,
0x77060a: 1219,
0x77060b: 1220,
0x77060c: 1221,
0x77060d: 1222,
0x77060e: 1223,
0x77060f: 1224,
0x770610: 1225,
0x770611: 1226,
0x770612: 1227,
0x770613: 1228,
0x770614: 1229,
0x770615: 1230,
0x770616: 1231,
0x770617: 1232,
0x770618: 1233,
0x770619: 1234,
0x77061a: 1235,
0x77061b: 1236,
0x77061c: 1237,
0x77061d: 1238,
0x77061e: 1239,
0x77061f: 1240,
0x770620: 1241,
0x770621: 1242,
0x770622: 1243,
0x770623: 1244,
0x770624: 1245,
0x770625: 1246,
0x770626: 1247,
0x770627: 1248,
0x770628: 1249,
0x770629: 1250,
0x77062a: 1251,
0x77062b: 1252,
0x77062c: 1253,
0x77062d: 1254,
0x77062e: 1255,
0x77062f: 1256,
0x770630: 1257,
0x770631: 1258,
0x770632: 1259,
0x770633: 1260,
0x770634: 1261,
0x770635: 1262,
0x770636: 1263,
0x770637: 1264,
0x770638: 1265,
0x770639: 1266,
0x77063a: 1267,
0x77063b: 1268,
0x77063c: 1269,
0x77063d: 1280,
0x77063e: 1281,
0x77063f: 1282,
0x770640: 1283,
0x770641: 1284,
0x770642: 1285,
0x770643: 1286,
0x770644: 1289,
0x770645: 1290,
0x770646: 1291,
0x770647: 1292,
0x770648: 1293,
0x770649: 1294,
0x77064a: 1295,
0x77064b: 1296,
0x77064c: 1297,
0x77064d: 1298,
0x77064e: 1299,
0x77064f: 1300,
0x770650: 1301,
0x770651: 1302,
0x770652: 1303,
0x770653: 1344,
0x770654: 1345,
0x770655: 1346,
0x770656: 1347,
0x770657: 1348,
0x770658: 1349,
0x770659: 1350,
0x77065a: 1351,
0x77065b: 1352,
0x77065c: 1354,
0x77065d: 1355,
0x77065e: 1356,
0x77065f: 1357,
0x770660: 1358,
0x770661: 1359,
0x770662: 1360,
0x770663: 1361,
0x770664: 1362,
0x770665: 1363,
0x770666: 1365,
0x770667: 1366,
0x770668: 1367,
0x770669: 1368,
0x77066a: 1369,
0x77066b: 1370,
0x77066c: 1371,
0x77066d: 1372,
0x77066e: 1374,
0x77066f: 1375,
0x770670: 1376,
0x770671: 1379,
0x770672: 1380,
0x770673: 1381,
0x770674: 1382,
0x770675: 1383,
0x770676: 1384,
0x770677: 1385,
0x770678: 1386,
0x770679: 1387,
0x77067a: 1388,
0x77067b: 1389,
0x77067c: 1390,
0x77067d: 1391,
0x77067e: 1392,
0x77067f: 1393,
0x770680: 1394,
0x770681: 1395,
0x770682: 1396,
0x770683: 1397,
0x770684: 1398,
0x770685: 1408,
0x770686: 1409,
0x770687: 1410,
0x770688: 1411,
0x770689: 1412,
0x77068a: 1414,
0x77068b: 1472,
0x77068c: 1473,
0x77068d: 1474,
0x77068e: 1475,
0x77068f: 1476,
0x770690: 1477,
0x770691: 1478,
0x770692: 1479,
0x770693: 1480,
0x770694: 1481,
0x770695: 1482,
0x770696: 1483,
0x770697: 1484,
0x770698: 1486,
0x770699: 1487,
0x77069a: 1488,
0x77069b: 1489,
0x77069c: 1490,
0x77069d: 1491,
0x77069e: 1495,
0x77069f: 1496,
0x7706a0: 1497,
0x7706a1: 1498,
0x7706a2: 1499,
0x7706a3: 1500,
0x7706a4: 1501,
0x7706a5: 1502,
0x7706a6: 1503,
0x7706a7: 1504,
0x7706a8: 1505,
0x7706a9: 1506,
0x7706aa: 1507,
0x7706ab: 1508,
0x7706ac: 1536,
0x7706ad: 1537,
0x7706ae: 1538,
0x7706af: 1539,
0x7706b0: 1540,
0x7706b1: 1541,
0x7706b2: 1600,
0x7706b3: 1601,
0x7706b4: 1602,
0x7706b5: 1603,
0x7706b6: 1604,
0x7706b7: 1605,
0x7706b8: 1606,
0x7706b9: 1607,
0x7706ba: 1612,
0x7706bb: 1613,
0x7706bc: 1614,
0x7706bd: 1615,
0x7706be: 1616,
0x7706bf: 1617,
0x7706c0: 1618,
0x7706c1: 1619,
0x7706c2: 1620,
0x7706c3: 1621,
0x7706c4: 1622,
0x7706c5: 1664,
0x7706c6: 1665,
0x7706c7: 1667,
0x7706c8: 1668,
0x7706c9: 1670,
0x7706ca: 1671,
0x7706cb: 1672,
0x7706cc: 1673,
0x7706cd: 1674,
0x7706ce: 1675,
0x7706cf: 1676,
0x7706d0: 1677,
0x7706d1: 1678,
0x7706d2: 1679,
0x7706d3: 1680,
0x7706d4: 1681,
0x7706d5: 1682,
0x7706d6: 1683,
0x7706d7: 1684,
0x7706d8: 1685,
0x7706d9: 1686,
0x7706da: 1730,
0x7706db: 1732,
0x7706dc: 1734,
0x7706dd: 1792,
0x7706de: 1793,
0x7706df: 1794,
0x7706e0: 1795,
0x7706e1: 1796,
0x7706e2: 1797,
0x7706e3: 1798,
0x7706e4: 1799,
0x7706e5: 1800,
0x7706e6: 1801,
0x7706e7: 1802,
0x7706e8: 1803,
0x7706e9: 1804,
0x7706ea: 1805,
0x7706eb: 1810,
0x7706ec: 1811,
0x7706ed: 1812,
0x7706ee: 1813,
0x7706ef: 1814,
0x7706f0: 1815,
0x7706f1: 1817,
0x7706f2: 1818,
0x7706f3: 1819,
0x7706f4: 1820,
0x7706f5: 1821,
0x7706f6: 1822,
0x7706f7: 1823,
0x7706f8: 1824,
0x7706f9: 1825,
0x7706fa: 1826,
0x7706fb: 1827,
0x7706fc: 1828,
0x7706fd: 1831,
0x7706fe: 1832,
0x7706ff: 1858,
}

View File

@@ -0,0 +1,57 @@
def hal_decompress(comp: bytes) -> bytes:
"""
HAL decompression based on exhal by devinacker
https://github.com/devinacker/exhal
"""
inpos = 0
inval = 0
output = bytearray()
while inval != 0xFF:
remaining = 65536 - inpos
if remaining < 1:
return bytes()
inval = comp[inpos]
inpos += 1
if inval == 0xFF:
break
if (inval & 0xE0) == 0xE0:
command = (inval >> 2) & 0x07
length = (((inval & 0x03) << 8) | comp[inpos]) + 1
inpos += 1
else:
command = inval >> 5
length = (inval & 0x1F) + 1
if (command == 2 and ((len(output) + 2*length) > 65536)) or (len(output) + length) > 65536:
return bytes()
if command == 0:
output.extend(comp[inpos:inpos+length])
inpos += length
elif command == 1:
output.extend([comp[inpos] for _ in range(length)])
inpos += 1
elif command == 2:
output.extend([comp[x] for _ in range(length) for x in (inpos, inpos+1)])
inpos += 2
elif command == 3:
output.extend([comp[inpos] + i for i in range(length)])
inpos += 1
elif command == 4 or command == 7:
offset = (comp[inpos] << 8) | comp[inpos + 1]
if (offset + length) > 65536:
return bytes()
output.extend(output[offset:offset+length])
inpos += 2
elif command == 5:
offset = (comp[inpos] << 8) | comp[inpos + 1]
if (offset + length) > 65536:
return bytes()
output.extend([int('{:08b}'.format(x)[::-1], 2) for x in output[offset:offset+length]])
inpos += 2
elif command == 6:
offset = (comp[inpos] << 8) | comp[inpos + 1]
if offset < length - 1:
return bytes()
output.extend([output[offset - x] for x in range(length)])
inpos += 2
return bytes(output)

282
worlds/kdl3/Gifting.py Normal file
View File

@@ -0,0 +1,282 @@
# Small subfile to handle gifting info such as desired traits and giftbox management
import typing
async def update_object(ctx, key: str, value: typing.Dict):
await ctx.send_msgs([
{
"cmd": "Set",
"key": key,
"default": {},
"want_reply": False,
"operations": [
{"operation": "update", "value": value}
]
}
])
async def pop_object(ctx, key: str, value: str):
await ctx.send_msgs([
{
"cmd": "Set",
"key": key,
"default": {},
"want_reply": False,
"operations": [
{"operation": "pop", "value": value}
]
}
])
async def initialize_giftboxes(ctx, giftbox_key: str, motherbox_key: str, is_open: bool):
ctx.set_notify(motherbox_key, giftbox_key)
await update_object(ctx, f"Giftboxes;{ctx.team}", {f"{ctx.slot}":
{
"IsOpen": is_open,
**kdl3_gifting_options
}})
ctx.gifting = is_open
kdl3_gifting_options = {
"AcceptsAnyGift": True,
"DesiredTraits": [
"Consumable", "Food", "Drink", "Candy", "Tomato",
"Invincible", "Life", "Heal", "Health", "Trap",
"Goo", "Gel", "Slow", "Slowness", "Eject", "Removal"
],
"MinimumGiftVersion": 2,
}
kdl3_gifts = {
1: {
"ItemName": "1-Up",
"Amount": 1,
"ItemValue": 400000,
"Traits": [
{
"Trait": "Consumable",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Life",
"Quality": 1,
"Duration": 1
}
]
},
2: {
"ItemName": "Maxim Tomato",
"Amount": 1,
"ItemValue": 500000,
"Traits": [
{
"Trait": "Consumable",
"Quality": 5,
"Duration": 1,
},
{
"Trait": "Heal",
"Quality": 5,
"Duration": 1,
},
{
"Trait": "Food",
"Quality": 5,
"Duration": 1,
},
{
"Trait": "Tomato",
"Quality": 5,
"Duration": 1,
},
{
"Trait": "Vegetable",
"Quality": 5,
"Duration": 1,
}
]
},
3: {
"ItemName": "Energy Drink",
"Amount": 1,
"ItemValue": 100000,
"Traits": [
{
"Trait": "Consumable",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Heal",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Drink",
"Quality": 1,
"Duration": 1,
},
]
},
5: {
"ItemName": "Small Star Piece",
"Amount": 1,
"ItemValue": 10000,
"Traits": [
{
"Trait": "Currency",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Money",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Star",
"Quality": 1,
"Duration": 1
}
]
},
6: {
"ItemName": "Medium Star Piece",
"Amount": 1,
"ItemValue": 30000,
"Traits": [
{
"Trait": "Currency",
"Quality": 3,
"Duration": 1,
},
{
"Trait": "Money",
"Quality": 3,
"Duration": 1,
},
{
"Trait": "Star",
"Quality": 3,
"Duration": 1
}
]
},
7: {
"ItemName": "Large Star Piece",
"Amount": 1,
"ItemValue": 50000,
"Traits": [
{
"Trait": "Currency",
"Quality": 5,
"Duration": 1,
},
{
"Trait": "Money",
"Quality": 5,
"Duration": 1,
},
{
"Trait": "Star",
"Quality": 5,
"Duration": 1
}
]
},
}
kdl3_trap_gifts = {
0: {
"ItemName": "Gooey Bag",
"Amount": 1,
"ItemValue": 10000,
"Traits": [
{
"Trait": "Trap",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Goo",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Gel",
"Quality": 1,
"Duration": 1
}
]
},
1: {
"ItemName": "Slowness",
"Amount": 1,
"ItemValue": 10000,
"Traits": [
{
"Trait": "Trap",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Slow",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Slowness",
"Quality": 1,
"Duration": 1
}
]
},
2: {
"ItemName": "Eject Ability",
"Amount": 1,
"ItemValue": 10000,
"Traits": [
{
"Trait": "Trap",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Eject",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Removal",
"Quality": 1,
"Duration": 1
}
]
},
3: {
"ItemName": "Bad Meal",
"Amount": 1,
"ItemValue": 10000,
"Traits": [
{
"Trait": "Trap",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Damage",
"Quality": 1,
"Duration": 1,
},
{
"Trait": "Food",
"Quality": 1,
"Duration": 1
}
]
},
}

105
worlds/kdl3/Items.py Normal file
View File

@@ -0,0 +1,105 @@
from BaseClasses import Item
import typing
class ItemData(typing.NamedTuple):
code: typing.Optional[int]
progression: bool
skip_balancing: bool = False
trap: bool = False
class KDL3Item(Item):
game = "Kirby's Dream Land 3"
copy_ability_table = {
"Burning": ItemData(0x770001, True),
"Stone": ItemData(0x770002, True),
"Ice": ItemData(0x770003, True),
"Needle": ItemData(0x770004, True),
"Clean": ItemData(0x770005, True),
"Parasol": ItemData(0x770006, True),
"Spark": ItemData(0x770007, True),
"Cutter": ItemData(0x770008, True)
}
animal_friend_table = {
"Rick": ItemData(0x770010, True),
"Kine": ItemData(0x770011, True),
"Coo": ItemData(0x770012, True),
"Nago": ItemData(0x770013, True),
"ChuChu": ItemData(0x770014, True),
"Pitch": ItemData(0x770015, True)
}
animal_friend_spawn_table = {
"Rick Spawn": ItemData(None, True),
"Kine Spawn": ItemData(None, True),
"Coo Spawn": ItemData(None, True),
"Nago Spawn": ItemData(None, True),
"ChuChu Spawn": ItemData(None, True),
"Pitch Spawn": ItemData(None, True)
}
copy_ability_access_table = {
"No Ability": ItemData(None, False),
"Burning Ability": ItemData(None, True),
"Stone Ability": ItemData(None, True),
"Ice Ability": ItemData(None, True),
"Needle Ability": ItemData(None, True),
"Clean Ability": ItemData(None, True),
"Parasol Ability": ItemData(None, True),
"Spark Ability": ItemData(None, True),
"Cutter Ability": ItemData(None, True),
}
misc_item_table = {
"Heart Star": ItemData(0x770020, True, True),
"1-Up": ItemData(0x770021, False),
"Maxim Tomato": ItemData(0x770022, False),
"Invincible Candy": ItemData(0x770023, False),
"Little Star": ItemData(0x770024, False),
"Medium Star": ItemData(0x770025, False),
"Big Star": ItemData(0x770026, False),
}
trap_item_table = {
"Gooey Bag": ItemData(0x770040, False, False, True),
"Slowness": ItemData(0x770041, False, False, True),
"Eject Ability": ItemData(0x770042, False, False, True)
}
filler_item_weights = {
"1-Up": 4,
"Maxim Tomato": 2,
"Invincible Candy": 2
}
star_item_weights = {
"Little Star": 4,
"Medium Star": 2,
"Big Star": 1
}
total_filler_weights = {
**filler_item_weights,
**star_item_weights
}
item_table = {
**copy_ability_table,
**copy_ability_access_table,
**animal_friend_table,
**animal_friend_spawn_table,
**misc_item_table,
**trap_item_table
}
item_names = {
"Copy Ability": set(copy_ability_table),
"Animal Friend": set(animal_friend_table),
}
lookup_name_to_id: typing.Dict[str, int] = {item_name: data.code for item_name, data in item_table.items() if data.code}

940
worlds/kdl3/Locations.py Normal file
View File

@@ -0,0 +1,940 @@
import typing
from BaseClasses import Location, Region
from .Names import LocationName
if typing.TYPE_CHECKING:
from .Room import KDL3Room
class KDL3Location(Location):
game: str = "Kirby's Dream Land 3"
room: typing.Optional["KDL3Room"] = None
def __init__(self, player: int, name: str, address: typing.Optional[int], parent: typing.Union[Region, None]):
super().__init__(player, name, address, parent)
if not address:
self.show_in_spoiler = False
stage_locations = {
0x770001: LocationName.grass_land_1,
0x770002: LocationName.grass_land_2,
0x770003: LocationName.grass_land_3,
0x770004: LocationName.grass_land_4,
0x770005: LocationName.grass_land_5,
0x770006: LocationName.grass_land_6,
0x770007: LocationName.ripple_field_1,
0x770008: LocationName.ripple_field_2,
0x770009: LocationName.ripple_field_3,
0x77000A: LocationName.ripple_field_4,
0x77000B: LocationName.ripple_field_5,
0x77000C: LocationName.ripple_field_6,
0x77000D: LocationName.sand_canyon_1,
0x77000E: LocationName.sand_canyon_2,
0x77000F: LocationName.sand_canyon_3,
0x770010: LocationName.sand_canyon_4,
0x770011: LocationName.sand_canyon_5,
0x770012: LocationName.sand_canyon_6,
0x770013: LocationName.cloudy_park_1,
0x770014: LocationName.cloudy_park_2,
0x770015: LocationName.cloudy_park_3,
0x770016: LocationName.cloudy_park_4,
0x770017: LocationName.cloudy_park_5,
0x770018: LocationName.cloudy_park_6,
0x770019: LocationName.iceberg_1,
0x77001A: LocationName.iceberg_2,
0x77001B: LocationName.iceberg_3,
0x77001C: LocationName.iceberg_4,
0x77001D: LocationName.iceberg_5,
0x77001E: LocationName.iceberg_6,
}
heart_star_locations = {
0x770101: LocationName.grass_land_tulip,
0x770102: LocationName.grass_land_muchi,
0x770103: LocationName.grass_land_pitcherman,
0x770104: LocationName.grass_land_chao,
0x770105: LocationName.grass_land_mine,
0x770106: LocationName.grass_land_pierre,
0x770107: LocationName.ripple_field_kamuribana,
0x770108: LocationName.ripple_field_bakasa,
0x770109: LocationName.ripple_field_elieel,
0x77010A: LocationName.ripple_field_toad,
0x77010B: LocationName.ripple_field_mama_pitch,
0x77010C: LocationName.ripple_field_hb002,
0x77010D: LocationName.sand_canyon_mushrooms,
0x77010E: LocationName.sand_canyon_auntie,
0x77010F: LocationName.sand_canyon_caramello,
0x770110: LocationName.sand_canyon_hikari,
0x770111: LocationName.sand_canyon_nyupun,
0x770112: LocationName.sand_canyon_rob,
0x770113: LocationName.cloudy_park_hibanamodoki,
0x770114: LocationName.cloudy_park_piyokeko,
0x770115: LocationName.cloudy_park_mrball,
0x770116: LocationName.cloudy_park_mikarin,
0x770117: LocationName.cloudy_park_pick,
0x770118: LocationName.cloudy_park_hb007,
0x770119: LocationName.iceberg_kogoesou,
0x77011A: LocationName.iceberg_samus,
0x77011B: LocationName.iceberg_kawasaki,
0x77011C: LocationName.iceberg_name,
0x77011D: LocationName.iceberg_shiro,
0x77011E: LocationName.iceberg_angel,
}
boss_locations = {
0x770200: LocationName.grass_land_whispy,
0x770201: LocationName.ripple_field_acro,
0x770202: LocationName.sand_canyon_poncon,
0x770203: LocationName.cloudy_park_ado,
0x770204: LocationName.iceberg_dedede,
}
consumable_locations = {
0x770300: LocationName.grass_land_1_u1,
0x770301: LocationName.grass_land_1_m1,
0x770302: LocationName.grass_land_2_u1,
0x770303: LocationName.grass_land_3_u1,
0x770304: LocationName.grass_land_3_m1,
0x770305: LocationName.grass_land_4_m1,
0x770306: LocationName.grass_land_4_u1,
0x770307: LocationName.grass_land_4_m2,
0x770308: LocationName.grass_land_4_m3,
0x770309: LocationName.grass_land_6_u1,
0x77030A: LocationName.grass_land_6_u2,
0x77030B: LocationName.ripple_field_2_u1,
0x77030C: LocationName.ripple_field_2_m1,
0x77030D: LocationName.ripple_field_3_m1,
0x77030E: LocationName.ripple_field_3_u1,
0x77030F: LocationName.ripple_field_4_m2,
0x770310: LocationName.ripple_field_4_u1,
0x770311: LocationName.ripple_field_4_m1,
0x770312: LocationName.ripple_field_5_u1,
0x770313: LocationName.ripple_field_5_m2,
0x770314: LocationName.ripple_field_5_m1,
0x770315: LocationName.sand_canyon_1_u1,
0x770316: LocationName.sand_canyon_2_u1,
0x770317: LocationName.sand_canyon_2_m1,
0x770318: LocationName.sand_canyon_4_m1,
0x770319: LocationName.sand_canyon_4_u1,
0x77031A: LocationName.sand_canyon_4_m2,
0x77031B: LocationName.sand_canyon_5_u1,
0x77031C: LocationName.sand_canyon_5_u3,
0x77031D: LocationName.sand_canyon_5_m1,
0x77031E: LocationName.sand_canyon_5_u4,
0x77031F: LocationName.sand_canyon_5_u2,
0x770320: LocationName.cloudy_park_1_m1,
0x770321: LocationName.cloudy_park_1_u1,
0x770322: LocationName.cloudy_park_4_u1,
0x770323: LocationName.cloudy_park_4_m1,
0x770324: LocationName.cloudy_park_5_m1,
0x770325: LocationName.cloudy_park_6_u1,
0x770326: LocationName.iceberg_3_m1,
0x770327: LocationName.iceberg_5_u1,
0x770328: LocationName.iceberg_5_u2,
0x770329: LocationName.iceberg_5_u3,
0x77032A: LocationName.iceberg_6_m1,
0x77032B: LocationName.iceberg_6_u1,
}
level_consumables = {
1: [0, 1],
2: [2],
3: [3, 4],
4: [5, 6, 7, 8],
6: [9, 10],
8: [11, 12],
9: [13, 14],
10: [15, 16, 17],
11: [18, 19, 20],
13: [21],
14: [22, 23],
16: [24, 25, 26],
17: [27, 28, 29, 30, 31],
19: [32, 33],
22: [34, 35],
23: [36],
24: [37],
27: [38],
29: [39, 40, 41],
30: [42, 43],
}
star_locations = {
0x770401: LocationName.grass_land_1_s1,
0x770402: LocationName.grass_land_1_s2,
0x770403: LocationName.grass_land_1_s3,
0x770404: LocationName.grass_land_1_s4,
0x770405: LocationName.grass_land_1_s5,
0x770406: LocationName.grass_land_1_s6,
0x770407: LocationName.grass_land_1_s7,
0x770408: LocationName.grass_land_1_s8,
0x770409: LocationName.grass_land_1_s9,
0x77040a: LocationName.grass_land_1_s10,
0x77040b: LocationName.grass_land_1_s11,
0x77040c: LocationName.grass_land_1_s12,
0x77040d: LocationName.grass_land_1_s13,
0x77040e: LocationName.grass_land_1_s14,
0x77040f: LocationName.grass_land_1_s15,
0x770410: LocationName.grass_land_1_s16,
0x770411: LocationName.grass_land_1_s17,
0x770412: LocationName.grass_land_1_s18,
0x770413: LocationName.grass_land_1_s19,
0x770414: LocationName.grass_land_1_s20,
0x770415: LocationName.grass_land_1_s21,
0x770416: LocationName.grass_land_1_s22,
0x770417: LocationName.grass_land_1_s23,
0x770418: LocationName.grass_land_2_s1,
0x770419: LocationName.grass_land_2_s2,
0x77041a: LocationName.grass_land_2_s3,
0x77041b: LocationName.grass_land_2_s4,
0x77041c: LocationName.grass_land_2_s5,
0x77041d: LocationName.grass_land_2_s6,
0x77041e: LocationName.grass_land_2_s7,
0x77041f: LocationName.grass_land_2_s8,
0x770420: LocationName.grass_land_2_s9,
0x770421: LocationName.grass_land_2_s10,
0x770422: LocationName.grass_land_2_s11,
0x770423: LocationName.grass_land_2_s12,
0x770424: LocationName.grass_land_2_s13,
0x770425: LocationName.grass_land_2_s14,
0x770426: LocationName.grass_land_2_s15,
0x770427: LocationName.grass_land_2_s16,
0x770428: LocationName.grass_land_2_s17,
0x770429: LocationName.grass_land_2_s18,
0x77042a: LocationName.grass_land_2_s19,
0x77042b: LocationName.grass_land_2_s20,
0x77042c: LocationName.grass_land_2_s21,
0x77042d: LocationName.grass_land_3_s1,
0x77042e: LocationName.grass_land_3_s2,
0x77042f: LocationName.grass_land_3_s3,
0x770430: LocationName.grass_land_3_s4,
0x770431: LocationName.grass_land_3_s5,
0x770432: LocationName.grass_land_3_s6,
0x770433: LocationName.grass_land_3_s7,
0x770434: LocationName.grass_land_3_s8,
0x770435: LocationName.grass_land_3_s9,
0x770436: LocationName.grass_land_3_s10,
0x770437: LocationName.grass_land_3_s11,
0x770438: LocationName.grass_land_3_s12,
0x770439: LocationName.grass_land_3_s13,
0x77043a: LocationName.grass_land_3_s14,
0x77043b: LocationName.grass_land_3_s15,
0x77043c: LocationName.grass_land_3_s16,
0x77043d: LocationName.grass_land_3_s17,
0x77043e: LocationName.grass_land_3_s18,
0x77043f: LocationName.grass_land_3_s19,
0x770440: LocationName.grass_land_3_s20,
0x770441: LocationName.grass_land_3_s21,
0x770442: LocationName.grass_land_3_s22,
0x770443: LocationName.grass_land_3_s23,
0x770444: LocationName.grass_land_3_s24,
0x770445: LocationName.grass_land_3_s25,
0x770446: LocationName.grass_land_3_s26,
0x770447: LocationName.grass_land_3_s27,
0x770448: LocationName.grass_land_3_s28,
0x770449: LocationName.grass_land_3_s29,
0x77044a: LocationName.grass_land_3_s30,
0x77044b: LocationName.grass_land_3_s31,
0x77044c: LocationName.grass_land_4_s1,
0x77044d: LocationName.grass_land_4_s2,
0x77044e: LocationName.grass_land_4_s3,
0x77044f: LocationName.grass_land_4_s4,
0x770450: LocationName.grass_land_4_s5,
0x770451: LocationName.grass_land_4_s6,
0x770452: LocationName.grass_land_4_s7,
0x770453: LocationName.grass_land_4_s8,
0x770454: LocationName.grass_land_4_s9,
0x770455: LocationName.grass_land_4_s10,
0x770456: LocationName.grass_land_4_s11,
0x770457: LocationName.grass_land_4_s12,
0x770458: LocationName.grass_land_4_s13,
0x770459: LocationName.grass_land_4_s14,
0x77045a: LocationName.grass_land_4_s15,
0x77045b: LocationName.grass_land_4_s16,
0x77045c: LocationName.grass_land_4_s17,
0x77045d: LocationName.grass_land_4_s18,
0x77045e: LocationName.grass_land_4_s19,
0x77045f: LocationName.grass_land_4_s20,
0x770460: LocationName.grass_land_4_s21,
0x770461: LocationName.grass_land_4_s22,
0x770462: LocationName.grass_land_4_s23,
0x770463: LocationName.grass_land_4_s24,
0x770464: LocationName.grass_land_4_s25,
0x770465: LocationName.grass_land_4_s26,
0x770466: LocationName.grass_land_4_s27,
0x770467: LocationName.grass_land_4_s28,
0x770468: LocationName.grass_land_4_s29,
0x770469: LocationName.grass_land_4_s30,
0x77046a: LocationName.grass_land_4_s31,
0x77046b: LocationName.grass_land_4_s32,
0x77046c: LocationName.grass_land_4_s33,
0x77046d: LocationName.grass_land_4_s34,
0x77046e: LocationName.grass_land_4_s35,
0x77046f: LocationName.grass_land_4_s36,
0x770470: LocationName.grass_land_4_s37,
0x770471: LocationName.grass_land_5_s1,
0x770472: LocationName.grass_land_5_s2,
0x770473: LocationName.grass_land_5_s3,
0x770474: LocationName.grass_land_5_s4,
0x770475: LocationName.grass_land_5_s5,
0x770476: LocationName.grass_land_5_s6,
0x770477: LocationName.grass_land_5_s7,
0x770478: LocationName.grass_land_5_s8,
0x770479: LocationName.grass_land_5_s9,
0x77047a: LocationName.grass_land_5_s10,
0x77047b: LocationName.grass_land_5_s11,
0x77047c: LocationName.grass_land_5_s12,
0x77047d: LocationName.grass_land_5_s13,
0x77047e: LocationName.grass_land_5_s14,
0x77047f: LocationName.grass_land_5_s15,
0x770480: LocationName.grass_land_5_s16,
0x770481: LocationName.grass_land_5_s17,
0x770482: LocationName.grass_land_5_s18,
0x770483: LocationName.grass_land_5_s19,
0x770484: LocationName.grass_land_5_s20,
0x770485: LocationName.grass_land_5_s21,
0x770486: LocationName.grass_land_5_s22,
0x770487: LocationName.grass_land_5_s23,
0x770488: LocationName.grass_land_5_s24,
0x770489: LocationName.grass_land_5_s25,
0x77048a: LocationName.grass_land_5_s26,
0x77048b: LocationName.grass_land_5_s27,
0x77048c: LocationName.grass_land_5_s28,
0x77048d: LocationName.grass_land_5_s29,
0x77048e: LocationName.grass_land_6_s1,
0x77048f: LocationName.grass_land_6_s2,
0x770490: LocationName.grass_land_6_s3,
0x770491: LocationName.grass_land_6_s4,
0x770492: LocationName.grass_land_6_s5,
0x770493: LocationName.grass_land_6_s6,
0x770494: LocationName.grass_land_6_s7,
0x770495: LocationName.grass_land_6_s8,
0x770496: LocationName.grass_land_6_s9,
0x770497: LocationName.grass_land_6_s10,
0x770498: LocationName.grass_land_6_s11,
0x770499: LocationName.grass_land_6_s12,
0x77049a: LocationName.grass_land_6_s13,
0x77049b: LocationName.grass_land_6_s14,
0x77049c: LocationName.grass_land_6_s15,
0x77049d: LocationName.grass_land_6_s16,
0x77049e: LocationName.grass_land_6_s17,
0x77049f: LocationName.grass_land_6_s18,
0x7704a0: LocationName.grass_land_6_s19,
0x7704a1: LocationName.grass_land_6_s20,
0x7704a2: LocationName.grass_land_6_s21,
0x7704a3: LocationName.grass_land_6_s22,
0x7704a4: LocationName.grass_land_6_s23,
0x7704a5: LocationName.grass_land_6_s24,
0x7704a6: LocationName.grass_land_6_s25,
0x7704a7: LocationName.grass_land_6_s26,
0x7704a8: LocationName.grass_land_6_s27,
0x7704a9: LocationName.grass_land_6_s28,
0x7704aa: LocationName.grass_land_6_s29,
0x7704ab: LocationName.ripple_field_1_s1,
0x7704ac: LocationName.ripple_field_1_s2,
0x7704ad: LocationName.ripple_field_1_s3,
0x7704ae: LocationName.ripple_field_1_s4,
0x7704af: LocationName.ripple_field_1_s5,
0x7704b0: LocationName.ripple_field_1_s6,
0x7704b1: LocationName.ripple_field_1_s7,
0x7704b2: LocationName.ripple_field_1_s8,
0x7704b3: LocationName.ripple_field_1_s9,
0x7704b4: LocationName.ripple_field_1_s10,
0x7704b5: LocationName.ripple_field_1_s11,
0x7704b6: LocationName.ripple_field_1_s12,
0x7704b7: LocationName.ripple_field_1_s13,
0x7704b8: LocationName.ripple_field_1_s14,
0x7704b9: LocationName.ripple_field_1_s15,
0x7704ba: LocationName.ripple_field_1_s16,
0x7704bb: LocationName.ripple_field_1_s17,
0x7704bc: LocationName.ripple_field_1_s18,
0x7704bd: LocationName.ripple_field_1_s19,
0x7704be: LocationName.ripple_field_2_s1,
0x7704bf: LocationName.ripple_field_2_s2,
0x7704c0: LocationName.ripple_field_2_s3,
0x7704c1: LocationName.ripple_field_2_s4,
0x7704c2: LocationName.ripple_field_2_s5,
0x7704c3: LocationName.ripple_field_2_s6,
0x7704c4: LocationName.ripple_field_2_s7,
0x7704c5: LocationName.ripple_field_2_s8,
0x7704c6: LocationName.ripple_field_2_s9,
0x7704c7: LocationName.ripple_field_2_s10,
0x7704c8: LocationName.ripple_field_2_s11,
0x7704c9: LocationName.ripple_field_2_s12,
0x7704ca: LocationName.ripple_field_2_s13,
0x7704cb: LocationName.ripple_field_2_s14,
0x7704cc: LocationName.ripple_field_2_s15,
0x7704cd: LocationName.ripple_field_2_s16,
0x7704ce: LocationName.ripple_field_2_s17,
0x7704cf: LocationName.ripple_field_3_s1,
0x7704d0: LocationName.ripple_field_3_s2,
0x7704d1: LocationName.ripple_field_3_s3,
0x7704d2: LocationName.ripple_field_3_s4,
0x7704d3: LocationName.ripple_field_3_s5,
0x7704d4: LocationName.ripple_field_3_s6,
0x7704d5: LocationName.ripple_field_3_s7,
0x7704d6: LocationName.ripple_field_3_s8,
0x7704d7: LocationName.ripple_field_3_s9,
0x7704d8: LocationName.ripple_field_3_s10,
0x7704d9: LocationName.ripple_field_3_s11,
0x7704da: LocationName.ripple_field_3_s12,
0x7704db: LocationName.ripple_field_3_s13,
0x7704dc: LocationName.ripple_field_3_s14,
0x7704dd: LocationName.ripple_field_3_s15,
0x7704de: LocationName.ripple_field_3_s16,
0x7704df: LocationName.ripple_field_3_s17,
0x7704e0: LocationName.ripple_field_3_s18,
0x7704e1: LocationName.ripple_field_3_s19,
0x7704e2: LocationName.ripple_field_3_s20,
0x7704e3: LocationName.ripple_field_3_s21,
0x7704e4: LocationName.ripple_field_4_s1,
0x7704e5: LocationName.ripple_field_4_s2,
0x7704e6: LocationName.ripple_field_4_s3,
0x7704e7: LocationName.ripple_field_4_s4,
0x7704e8: LocationName.ripple_field_4_s5,
0x7704e9: LocationName.ripple_field_4_s6,
0x7704ea: LocationName.ripple_field_4_s7,
0x7704eb: LocationName.ripple_field_4_s8,
0x7704ec: LocationName.ripple_field_4_s9,
0x7704ed: LocationName.ripple_field_4_s10,
0x7704ee: LocationName.ripple_field_4_s11,
0x7704ef: LocationName.ripple_field_4_s12,
0x7704f0: LocationName.ripple_field_4_s13,
0x7704f1: LocationName.ripple_field_4_s14,
0x7704f2: LocationName.ripple_field_4_s15,
0x7704f3: LocationName.ripple_field_4_s16,
0x7704f4: LocationName.ripple_field_4_s17,
0x7704f5: LocationName.ripple_field_4_s18,
0x7704f6: LocationName.ripple_field_4_s19,
0x7704f7: LocationName.ripple_field_4_s20,
0x7704f8: LocationName.ripple_field_4_s21,
0x7704f9: LocationName.ripple_field_4_s22,
0x7704fa: LocationName.ripple_field_4_s23,
0x7704fb: LocationName.ripple_field_4_s24,
0x7704fc: LocationName.ripple_field_4_s25,
0x7704fd: LocationName.ripple_field_4_s26,
0x7704fe: LocationName.ripple_field_4_s27,
0x7704ff: LocationName.ripple_field_4_s28,
0x770500: LocationName.ripple_field_4_s29,
0x770501: LocationName.ripple_field_4_s30,
0x770502: LocationName.ripple_field_4_s31,
0x770503: LocationName.ripple_field_4_s32,
0x770504: LocationName.ripple_field_4_s33,
0x770505: LocationName.ripple_field_4_s34,
0x770506: LocationName.ripple_field_4_s35,
0x770507: LocationName.ripple_field_4_s36,
0x770508: LocationName.ripple_field_4_s37,
0x770509: LocationName.ripple_field_4_s38,
0x77050a: LocationName.ripple_field_4_s39,
0x77050b: LocationName.ripple_field_4_s40,
0x77050c: LocationName.ripple_field_4_s41,
0x77050d: LocationName.ripple_field_4_s42,
0x77050e: LocationName.ripple_field_4_s43,
0x77050f: LocationName.ripple_field_4_s44,
0x770510: LocationName.ripple_field_4_s45,
0x770511: LocationName.ripple_field_4_s46,
0x770512: LocationName.ripple_field_4_s47,
0x770513: LocationName.ripple_field_4_s48,
0x770514: LocationName.ripple_field_4_s49,
0x770515: LocationName.ripple_field_4_s50,
0x770516: LocationName.ripple_field_4_s51,
0x770517: LocationName.ripple_field_5_s1,
0x770518: LocationName.ripple_field_5_s2,
0x770519: LocationName.ripple_field_5_s3,
0x77051a: LocationName.ripple_field_5_s4,
0x77051b: LocationName.ripple_field_5_s5,
0x77051c: LocationName.ripple_field_5_s6,
0x77051d: LocationName.ripple_field_5_s7,
0x77051e: LocationName.ripple_field_5_s8,
0x77051f: LocationName.ripple_field_5_s9,
0x770520: LocationName.ripple_field_5_s10,
0x770521: LocationName.ripple_field_5_s11,
0x770522: LocationName.ripple_field_5_s12,
0x770523: LocationName.ripple_field_5_s13,
0x770524: LocationName.ripple_field_5_s14,
0x770525: LocationName.ripple_field_5_s15,
0x770526: LocationName.ripple_field_5_s16,
0x770527: LocationName.ripple_field_5_s17,
0x770528: LocationName.ripple_field_5_s18,
0x770529: LocationName.ripple_field_5_s19,
0x77052a: LocationName.ripple_field_5_s20,
0x77052b: LocationName.ripple_field_5_s21,
0x77052c: LocationName.ripple_field_5_s22,
0x77052d: LocationName.ripple_field_5_s23,
0x77052e: LocationName.ripple_field_5_s24,
0x77052f: LocationName.ripple_field_5_s25,
0x770530: LocationName.ripple_field_5_s26,
0x770531: LocationName.ripple_field_5_s27,
0x770532: LocationName.ripple_field_5_s28,
0x770533: LocationName.ripple_field_5_s29,
0x770534: LocationName.ripple_field_5_s30,
0x770535: LocationName.ripple_field_5_s31,
0x770536: LocationName.ripple_field_5_s32,
0x770537: LocationName.ripple_field_5_s33,
0x770538: LocationName.ripple_field_5_s34,
0x770539: LocationName.ripple_field_5_s35,
0x77053a: LocationName.ripple_field_5_s36,
0x77053b: LocationName.ripple_field_5_s37,
0x77053c: LocationName.ripple_field_5_s38,
0x77053d: LocationName.ripple_field_5_s39,
0x77053e: LocationName.ripple_field_5_s40,
0x77053f: LocationName.ripple_field_5_s41,
0x770540: LocationName.ripple_field_5_s42,
0x770541: LocationName.ripple_field_5_s43,
0x770542: LocationName.ripple_field_5_s44,
0x770543: LocationName.ripple_field_5_s45,
0x770544: LocationName.ripple_field_5_s46,
0x770545: LocationName.ripple_field_5_s47,
0x770546: LocationName.ripple_field_5_s48,
0x770547: LocationName.ripple_field_5_s49,
0x770548: LocationName.ripple_field_5_s50,
0x770549: LocationName.ripple_field_5_s51,
0x77054a: LocationName.ripple_field_6_s1,
0x77054b: LocationName.ripple_field_6_s2,
0x77054c: LocationName.ripple_field_6_s3,
0x77054d: LocationName.ripple_field_6_s4,
0x77054e: LocationName.ripple_field_6_s5,
0x77054f: LocationName.ripple_field_6_s6,
0x770550: LocationName.ripple_field_6_s7,
0x770551: LocationName.ripple_field_6_s8,
0x770552: LocationName.ripple_field_6_s9,
0x770553: LocationName.ripple_field_6_s10,
0x770554: LocationName.ripple_field_6_s11,
0x770555: LocationName.ripple_field_6_s12,
0x770556: LocationName.ripple_field_6_s13,
0x770557: LocationName.ripple_field_6_s14,
0x770558: LocationName.ripple_field_6_s15,
0x770559: LocationName.ripple_field_6_s16,
0x77055a: LocationName.ripple_field_6_s17,
0x77055b: LocationName.ripple_field_6_s18,
0x77055c: LocationName.ripple_field_6_s19,
0x77055d: LocationName.ripple_field_6_s20,
0x77055e: LocationName.ripple_field_6_s21,
0x77055f: LocationName.ripple_field_6_s22,
0x770560: LocationName.ripple_field_6_s23,
0x770561: LocationName.sand_canyon_1_s1,
0x770562: LocationName.sand_canyon_1_s2,
0x770563: LocationName.sand_canyon_1_s3,
0x770564: LocationName.sand_canyon_1_s4,
0x770565: LocationName.sand_canyon_1_s5,
0x770566: LocationName.sand_canyon_1_s6,
0x770567: LocationName.sand_canyon_1_s7,
0x770568: LocationName.sand_canyon_1_s8,
0x770569: LocationName.sand_canyon_1_s9,
0x77056a: LocationName.sand_canyon_1_s10,
0x77056b: LocationName.sand_canyon_1_s11,
0x77056c: LocationName.sand_canyon_1_s12,
0x77056d: LocationName.sand_canyon_1_s13,
0x77056e: LocationName.sand_canyon_1_s14,
0x77056f: LocationName.sand_canyon_1_s15,
0x770570: LocationName.sand_canyon_1_s16,
0x770571: LocationName.sand_canyon_1_s17,
0x770572: LocationName.sand_canyon_1_s18,
0x770573: LocationName.sand_canyon_1_s19,
0x770574: LocationName.sand_canyon_1_s20,
0x770575: LocationName.sand_canyon_1_s21,
0x770576: LocationName.sand_canyon_1_s22,
0x770577: LocationName.sand_canyon_2_s1,
0x770578: LocationName.sand_canyon_2_s2,
0x770579: LocationName.sand_canyon_2_s3,
0x77057a: LocationName.sand_canyon_2_s4,
0x77057b: LocationName.sand_canyon_2_s5,
0x77057c: LocationName.sand_canyon_2_s6,
0x77057d: LocationName.sand_canyon_2_s7,
0x77057e: LocationName.sand_canyon_2_s8,
0x77057f: LocationName.sand_canyon_2_s9,
0x770580: LocationName.sand_canyon_2_s10,
0x770581: LocationName.sand_canyon_2_s11,
0x770582: LocationName.sand_canyon_2_s12,
0x770583: LocationName.sand_canyon_2_s13,
0x770584: LocationName.sand_canyon_2_s14,
0x770585: LocationName.sand_canyon_2_s15,
0x770586: LocationName.sand_canyon_2_s16,
0x770587: LocationName.sand_canyon_2_s17,
0x770588: LocationName.sand_canyon_2_s18,
0x770589: LocationName.sand_canyon_2_s19,
0x77058a: LocationName.sand_canyon_2_s20,
0x77058b: LocationName.sand_canyon_2_s21,
0x77058c: LocationName.sand_canyon_2_s22,
0x77058d: LocationName.sand_canyon_2_s23,
0x77058e: LocationName.sand_canyon_2_s24,
0x77058f: LocationName.sand_canyon_2_s25,
0x770590: LocationName.sand_canyon_2_s26,
0x770591: LocationName.sand_canyon_2_s27,
0x770592: LocationName.sand_canyon_2_s28,
0x770593: LocationName.sand_canyon_2_s29,
0x770594: LocationName.sand_canyon_2_s30,
0x770595: LocationName.sand_canyon_2_s31,
0x770596: LocationName.sand_canyon_2_s32,
0x770597: LocationName.sand_canyon_2_s33,
0x770598: LocationName.sand_canyon_2_s34,
0x770599: LocationName.sand_canyon_2_s35,
0x77059a: LocationName.sand_canyon_2_s36,
0x77059b: LocationName.sand_canyon_2_s37,
0x77059c: LocationName.sand_canyon_2_s38,
0x77059d: LocationName.sand_canyon_2_s39,
0x77059e: LocationName.sand_canyon_2_s40,
0x77059f: LocationName.sand_canyon_2_s41,
0x7705a0: LocationName.sand_canyon_2_s42,
0x7705a1: LocationName.sand_canyon_2_s43,
0x7705a2: LocationName.sand_canyon_2_s44,
0x7705a3: LocationName.sand_canyon_2_s45,
0x7705a4: LocationName.sand_canyon_2_s46,
0x7705a5: LocationName.sand_canyon_2_s47,
0x7705a6: LocationName.sand_canyon_2_s48,
0x7705a7: LocationName.sand_canyon_3_s1,
0x7705a8: LocationName.sand_canyon_3_s2,
0x7705a9: LocationName.sand_canyon_3_s3,
0x7705aa: LocationName.sand_canyon_3_s4,
0x7705ab: LocationName.sand_canyon_3_s5,
0x7705ac: LocationName.sand_canyon_3_s6,
0x7705ad: LocationName.sand_canyon_3_s7,
0x7705ae: LocationName.sand_canyon_3_s8,
0x7705af: LocationName.sand_canyon_3_s9,
0x7705b0: LocationName.sand_canyon_3_s10,
0x7705b1: LocationName.sand_canyon_4_s1,
0x7705b2: LocationName.sand_canyon_4_s2,
0x7705b3: LocationName.sand_canyon_4_s3,
0x7705b4: LocationName.sand_canyon_4_s4,
0x7705b5: LocationName.sand_canyon_4_s5,
0x7705b6: LocationName.sand_canyon_4_s6,
0x7705b7: LocationName.sand_canyon_4_s7,
0x7705b8: LocationName.sand_canyon_4_s8,
0x7705b9: LocationName.sand_canyon_4_s9,
0x7705ba: LocationName.sand_canyon_4_s10,
0x7705bb: LocationName.sand_canyon_4_s11,
0x7705bc: LocationName.sand_canyon_4_s12,
0x7705bd: LocationName.sand_canyon_4_s13,
0x7705be: LocationName.sand_canyon_4_s14,
0x7705bf: LocationName.sand_canyon_4_s15,
0x7705c0: LocationName.sand_canyon_4_s16,
0x7705c1: LocationName.sand_canyon_4_s17,
0x7705c2: LocationName.sand_canyon_4_s18,
0x7705c3: LocationName.sand_canyon_4_s19,
0x7705c4: LocationName.sand_canyon_4_s20,
0x7705c5: LocationName.sand_canyon_4_s21,
0x7705c6: LocationName.sand_canyon_4_s22,
0x7705c7: LocationName.sand_canyon_4_s23,
0x7705c8: LocationName.sand_canyon_5_s1,
0x7705c9: LocationName.sand_canyon_5_s2,
0x7705ca: LocationName.sand_canyon_5_s3,
0x7705cb: LocationName.sand_canyon_5_s4,
0x7705cc: LocationName.sand_canyon_5_s5,
0x7705cd: LocationName.sand_canyon_5_s6,
0x7705ce: LocationName.sand_canyon_5_s7,
0x7705cf: LocationName.sand_canyon_5_s8,
0x7705d0: LocationName.sand_canyon_5_s9,
0x7705d1: LocationName.sand_canyon_5_s10,
0x7705d2: LocationName.sand_canyon_5_s11,
0x7705d3: LocationName.sand_canyon_5_s12,
0x7705d4: LocationName.sand_canyon_5_s13,
0x7705d5: LocationName.sand_canyon_5_s14,
0x7705d6: LocationName.sand_canyon_5_s15,
0x7705d7: LocationName.sand_canyon_5_s16,
0x7705d8: LocationName.sand_canyon_5_s17,
0x7705d9: LocationName.sand_canyon_5_s18,
0x7705da: LocationName.sand_canyon_5_s19,
0x7705db: LocationName.sand_canyon_5_s20,
0x7705dc: LocationName.sand_canyon_5_s21,
0x7705dd: LocationName.sand_canyon_5_s22,
0x7705de: LocationName.sand_canyon_5_s23,
0x7705df: LocationName.sand_canyon_5_s24,
0x7705e0: LocationName.sand_canyon_5_s25,
0x7705e1: LocationName.sand_canyon_5_s26,
0x7705e2: LocationName.sand_canyon_5_s27,
0x7705e3: LocationName.sand_canyon_5_s28,
0x7705e4: LocationName.sand_canyon_5_s29,
0x7705e5: LocationName.sand_canyon_5_s30,
0x7705e6: LocationName.sand_canyon_5_s31,
0x7705e7: LocationName.sand_canyon_5_s32,
0x7705e8: LocationName.sand_canyon_5_s33,
0x7705e9: LocationName.sand_canyon_5_s34,
0x7705ea: LocationName.sand_canyon_5_s35,
0x7705eb: LocationName.sand_canyon_5_s36,
0x7705ec: LocationName.sand_canyon_5_s37,
0x7705ed: LocationName.sand_canyon_5_s38,
0x7705ee: LocationName.sand_canyon_5_s39,
0x7705ef: LocationName.sand_canyon_5_s40,
0x7705f0: LocationName.cloudy_park_1_s1,
0x7705f1: LocationName.cloudy_park_1_s2,
0x7705f2: LocationName.cloudy_park_1_s3,
0x7705f3: LocationName.cloudy_park_1_s4,
0x7705f4: LocationName.cloudy_park_1_s5,
0x7705f5: LocationName.cloudy_park_1_s6,
0x7705f6: LocationName.cloudy_park_1_s7,
0x7705f7: LocationName.cloudy_park_1_s8,
0x7705f8: LocationName.cloudy_park_1_s9,
0x7705f9: LocationName.cloudy_park_1_s10,
0x7705fa: LocationName.cloudy_park_1_s11,
0x7705fb: LocationName.cloudy_park_1_s12,
0x7705fc: LocationName.cloudy_park_1_s13,
0x7705fd: LocationName.cloudy_park_1_s14,
0x7705fe: LocationName.cloudy_park_1_s15,
0x7705ff: LocationName.cloudy_park_1_s16,
0x770600: LocationName.cloudy_park_1_s17,
0x770601: LocationName.cloudy_park_1_s18,
0x770602: LocationName.cloudy_park_1_s19,
0x770603: LocationName.cloudy_park_1_s20,
0x770604: LocationName.cloudy_park_1_s21,
0x770605: LocationName.cloudy_park_1_s22,
0x770606: LocationName.cloudy_park_1_s23,
0x770607: LocationName.cloudy_park_2_s1,
0x770608: LocationName.cloudy_park_2_s2,
0x770609: LocationName.cloudy_park_2_s3,
0x77060a: LocationName.cloudy_park_2_s4,
0x77060b: LocationName.cloudy_park_2_s5,
0x77060c: LocationName.cloudy_park_2_s6,
0x77060d: LocationName.cloudy_park_2_s7,
0x77060e: LocationName.cloudy_park_2_s8,
0x77060f: LocationName.cloudy_park_2_s9,
0x770610: LocationName.cloudy_park_2_s10,
0x770611: LocationName.cloudy_park_2_s11,
0x770612: LocationName.cloudy_park_2_s12,
0x770613: LocationName.cloudy_park_2_s13,
0x770614: LocationName.cloudy_park_2_s14,
0x770615: LocationName.cloudy_park_2_s15,
0x770616: LocationName.cloudy_park_2_s16,
0x770617: LocationName.cloudy_park_2_s17,
0x770618: LocationName.cloudy_park_2_s18,
0x770619: LocationName.cloudy_park_2_s19,
0x77061a: LocationName.cloudy_park_2_s20,
0x77061b: LocationName.cloudy_park_2_s21,
0x77061c: LocationName.cloudy_park_2_s22,
0x77061d: LocationName.cloudy_park_2_s23,
0x77061e: LocationName.cloudy_park_2_s24,
0x77061f: LocationName.cloudy_park_2_s25,
0x770620: LocationName.cloudy_park_2_s26,
0x770621: LocationName.cloudy_park_2_s27,
0x770622: LocationName.cloudy_park_2_s28,
0x770623: LocationName.cloudy_park_2_s29,
0x770624: LocationName.cloudy_park_2_s30,
0x770625: LocationName.cloudy_park_2_s31,
0x770626: LocationName.cloudy_park_2_s32,
0x770627: LocationName.cloudy_park_2_s33,
0x770628: LocationName.cloudy_park_2_s34,
0x770629: LocationName.cloudy_park_2_s35,
0x77062a: LocationName.cloudy_park_2_s36,
0x77062b: LocationName.cloudy_park_2_s37,
0x77062c: LocationName.cloudy_park_2_s38,
0x77062d: LocationName.cloudy_park_2_s39,
0x77062e: LocationName.cloudy_park_2_s40,
0x77062f: LocationName.cloudy_park_2_s41,
0x770630: LocationName.cloudy_park_2_s42,
0x770631: LocationName.cloudy_park_2_s43,
0x770632: LocationName.cloudy_park_2_s44,
0x770633: LocationName.cloudy_park_2_s45,
0x770634: LocationName.cloudy_park_2_s46,
0x770635: LocationName.cloudy_park_2_s47,
0x770636: LocationName.cloudy_park_2_s48,
0x770637: LocationName.cloudy_park_2_s49,
0x770638: LocationName.cloudy_park_2_s50,
0x770639: LocationName.cloudy_park_2_s51,
0x77063a: LocationName.cloudy_park_2_s52,
0x77063b: LocationName.cloudy_park_2_s53,
0x77063c: LocationName.cloudy_park_2_s54,
0x77063d: LocationName.cloudy_park_3_s1,
0x77063e: LocationName.cloudy_park_3_s2,
0x77063f: LocationName.cloudy_park_3_s3,
0x770640: LocationName.cloudy_park_3_s4,
0x770641: LocationName.cloudy_park_3_s5,
0x770642: LocationName.cloudy_park_3_s6,
0x770643: LocationName.cloudy_park_3_s7,
0x770644: LocationName.cloudy_park_3_s8,
0x770645: LocationName.cloudy_park_3_s9,
0x770646: LocationName.cloudy_park_3_s10,
0x770647: LocationName.cloudy_park_3_s11,
0x770648: LocationName.cloudy_park_3_s12,
0x770649: LocationName.cloudy_park_3_s13,
0x77064a: LocationName.cloudy_park_3_s14,
0x77064b: LocationName.cloudy_park_3_s15,
0x77064c: LocationName.cloudy_park_3_s16,
0x77064d: LocationName.cloudy_park_3_s17,
0x77064e: LocationName.cloudy_park_3_s18,
0x77064f: LocationName.cloudy_park_3_s19,
0x770650: LocationName.cloudy_park_3_s20,
0x770651: LocationName.cloudy_park_3_s21,
0x770652: LocationName.cloudy_park_3_s22,
0x770653: LocationName.cloudy_park_4_s1,
0x770654: LocationName.cloudy_park_4_s2,
0x770655: LocationName.cloudy_park_4_s3,
0x770656: LocationName.cloudy_park_4_s4,
0x770657: LocationName.cloudy_park_4_s5,
0x770658: LocationName.cloudy_park_4_s6,
0x770659: LocationName.cloudy_park_4_s7,
0x77065a: LocationName.cloudy_park_4_s8,
0x77065b: LocationName.cloudy_park_4_s9,
0x77065c: LocationName.cloudy_park_4_s10,
0x77065d: LocationName.cloudy_park_4_s11,
0x77065e: LocationName.cloudy_park_4_s12,
0x77065f: LocationName.cloudy_park_4_s13,
0x770660: LocationName.cloudy_park_4_s14,
0x770661: LocationName.cloudy_park_4_s15,
0x770662: LocationName.cloudy_park_4_s16,
0x770663: LocationName.cloudy_park_4_s17,
0x770664: LocationName.cloudy_park_4_s18,
0x770665: LocationName.cloudy_park_4_s19,
0x770666: LocationName.cloudy_park_4_s20,
0x770667: LocationName.cloudy_park_4_s21,
0x770668: LocationName.cloudy_park_4_s22,
0x770669: LocationName.cloudy_park_4_s23,
0x77066a: LocationName.cloudy_park_4_s24,
0x77066b: LocationName.cloudy_park_4_s25,
0x77066c: LocationName.cloudy_park_4_s26,
0x77066d: LocationName.cloudy_park_4_s27,
0x77066e: LocationName.cloudy_park_4_s28,
0x77066f: LocationName.cloudy_park_4_s29,
0x770670: LocationName.cloudy_park_4_s30,
0x770671: LocationName.cloudy_park_4_s31,
0x770672: LocationName.cloudy_park_4_s32,
0x770673: LocationName.cloudy_park_4_s33,
0x770674: LocationName.cloudy_park_4_s34,
0x770675: LocationName.cloudy_park_4_s35,
0x770676: LocationName.cloudy_park_4_s36,
0x770677: LocationName.cloudy_park_4_s37,
0x770678: LocationName.cloudy_park_4_s38,
0x770679: LocationName.cloudy_park_4_s39,
0x77067a: LocationName.cloudy_park_4_s40,
0x77067b: LocationName.cloudy_park_4_s41,
0x77067c: LocationName.cloudy_park_4_s42,
0x77067d: LocationName.cloudy_park_4_s43,
0x77067e: LocationName.cloudy_park_4_s44,
0x77067f: LocationName.cloudy_park_4_s45,
0x770680: LocationName.cloudy_park_4_s46,
0x770681: LocationName.cloudy_park_4_s47,
0x770682: LocationName.cloudy_park_4_s48,
0x770683: LocationName.cloudy_park_4_s49,
0x770684: LocationName.cloudy_park_4_s50,
0x770685: LocationName.cloudy_park_5_s1,
0x770686: LocationName.cloudy_park_5_s2,
0x770687: LocationName.cloudy_park_5_s3,
0x770688: LocationName.cloudy_park_5_s4,
0x770689: LocationName.cloudy_park_5_s5,
0x77068a: LocationName.cloudy_park_5_s6,
0x77068b: LocationName.cloudy_park_6_s1,
0x77068c: LocationName.cloudy_park_6_s2,
0x77068d: LocationName.cloudy_park_6_s3,
0x77068e: LocationName.cloudy_park_6_s4,
0x77068f: LocationName.cloudy_park_6_s5,
0x770690: LocationName.cloudy_park_6_s6,
0x770691: LocationName.cloudy_park_6_s7,
0x770692: LocationName.cloudy_park_6_s8,
0x770693: LocationName.cloudy_park_6_s9,
0x770694: LocationName.cloudy_park_6_s10,
0x770695: LocationName.cloudy_park_6_s11,
0x770696: LocationName.cloudy_park_6_s12,
0x770697: LocationName.cloudy_park_6_s13,
0x770698: LocationName.cloudy_park_6_s14,
0x770699: LocationName.cloudy_park_6_s15,
0x77069a: LocationName.cloudy_park_6_s16,
0x77069b: LocationName.cloudy_park_6_s17,
0x77069c: LocationName.cloudy_park_6_s18,
0x77069d: LocationName.cloudy_park_6_s19,
0x77069e: LocationName.cloudy_park_6_s20,
0x77069f: LocationName.cloudy_park_6_s21,
0x7706a0: LocationName.cloudy_park_6_s22,
0x7706a1: LocationName.cloudy_park_6_s23,
0x7706a2: LocationName.cloudy_park_6_s24,
0x7706a3: LocationName.cloudy_park_6_s25,
0x7706a4: LocationName.cloudy_park_6_s26,
0x7706a5: LocationName.cloudy_park_6_s27,
0x7706a6: LocationName.cloudy_park_6_s28,
0x7706a7: LocationName.cloudy_park_6_s29,
0x7706a8: LocationName.cloudy_park_6_s30,
0x7706a9: LocationName.cloudy_park_6_s31,
0x7706aa: LocationName.cloudy_park_6_s32,
0x7706ab: LocationName.cloudy_park_6_s33,
0x7706ac: LocationName.iceberg_1_s1,
0x7706ad: LocationName.iceberg_1_s2,
0x7706ae: LocationName.iceberg_1_s3,
0x7706af: LocationName.iceberg_1_s4,
0x7706b0: LocationName.iceberg_1_s5,
0x7706b1: LocationName.iceberg_1_s6,
0x7706b2: LocationName.iceberg_2_s1,
0x7706b3: LocationName.iceberg_2_s2,
0x7706b4: LocationName.iceberg_2_s3,
0x7706b5: LocationName.iceberg_2_s4,
0x7706b6: LocationName.iceberg_2_s5,
0x7706b7: LocationName.iceberg_2_s6,
0x7706b8: LocationName.iceberg_2_s7,
0x7706b9: LocationName.iceberg_2_s8,
0x7706ba: LocationName.iceberg_2_s9,
0x7706bb: LocationName.iceberg_2_s10,
0x7706bc: LocationName.iceberg_2_s11,
0x7706bd: LocationName.iceberg_2_s12,
0x7706be: LocationName.iceberg_2_s13,
0x7706bf: LocationName.iceberg_2_s14,
0x7706c0: LocationName.iceberg_2_s15,
0x7706c1: LocationName.iceberg_2_s16,
0x7706c2: LocationName.iceberg_2_s17,
0x7706c3: LocationName.iceberg_2_s18,
0x7706c4: LocationName.iceberg_2_s19,
0x7706c5: LocationName.iceberg_3_s1,
0x7706c6: LocationName.iceberg_3_s2,
0x7706c7: LocationName.iceberg_3_s3,
0x7706c8: LocationName.iceberg_3_s4,
0x7706c9: LocationName.iceberg_3_s5,
0x7706ca: LocationName.iceberg_3_s6,
0x7706cb: LocationName.iceberg_3_s7,
0x7706cc: LocationName.iceberg_3_s8,
0x7706cd: LocationName.iceberg_3_s9,
0x7706ce: LocationName.iceberg_3_s10,
0x7706cf: LocationName.iceberg_3_s11,
0x7706d0: LocationName.iceberg_3_s12,
0x7706d1: LocationName.iceberg_3_s13,
0x7706d2: LocationName.iceberg_3_s14,
0x7706d3: LocationName.iceberg_3_s15,
0x7706d4: LocationName.iceberg_3_s16,
0x7706d5: LocationName.iceberg_3_s17,
0x7706d6: LocationName.iceberg_3_s18,
0x7706d7: LocationName.iceberg_3_s19,
0x7706d8: LocationName.iceberg_3_s20,
0x7706d9: LocationName.iceberg_3_s21,
0x7706da: LocationName.iceberg_4_s1,
0x7706db: LocationName.iceberg_4_s2,
0x7706dc: LocationName.iceberg_4_s3,
0x7706dd: LocationName.iceberg_5_s1,
0x7706de: LocationName.iceberg_5_s2,
0x7706df: LocationName.iceberg_5_s3,
0x7706e0: LocationName.iceberg_5_s4,
0x7706e1: LocationName.iceberg_5_s5,
0x7706e2: LocationName.iceberg_5_s6,
0x7706e3: LocationName.iceberg_5_s7,
0x7706e4: LocationName.iceberg_5_s8,
0x7706e5: LocationName.iceberg_5_s9,
0x7706e6: LocationName.iceberg_5_s10,
0x7706e7: LocationName.iceberg_5_s11,
0x7706e8: LocationName.iceberg_5_s12,
0x7706e9: LocationName.iceberg_5_s13,
0x7706ea: LocationName.iceberg_5_s14,
0x7706eb: LocationName.iceberg_5_s15,
0x7706ec: LocationName.iceberg_5_s16,
0x7706ed: LocationName.iceberg_5_s17,
0x7706ee: LocationName.iceberg_5_s18,
0x7706ef: LocationName.iceberg_5_s19,
0x7706f0: LocationName.iceberg_5_s20,
0x7706f1: LocationName.iceberg_5_s21,
0x7706f2: LocationName.iceberg_5_s22,
0x7706f3: LocationName.iceberg_5_s23,
0x7706f4: LocationName.iceberg_5_s24,
0x7706f5: LocationName.iceberg_5_s25,
0x7706f6: LocationName.iceberg_5_s26,
0x7706f7: LocationName.iceberg_5_s27,
0x7706f8: LocationName.iceberg_5_s28,
0x7706f9: LocationName.iceberg_5_s29,
0x7706fa: LocationName.iceberg_5_s30,
0x7706fb: LocationName.iceberg_5_s31,
0x7706fc: LocationName.iceberg_5_s32,
0x7706fd: LocationName.iceberg_5_s33,
0x7706fe: LocationName.iceberg_5_s34,
0x7706ff: LocationName.iceberg_6_s1,
}
location_table = {
**stage_locations,
**heart_star_locations,
**boss_locations,
**consumable_locations,
**star_locations
}

View File

@@ -0,0 +1,199 @@
grass_land_1_a1 = "Grass Land 1 - Animal 1" # Nago
grass_land_1_a2 = "Grass Land 1 - Animal 2" # Rick
grass_land_2_a1 = "Grass Land 2 - Animal 1" # ChuChu
grass_land_2_a2 = "Grass Land 2 - Animal 2" # Pitch
grass_land_3_a1 = "Grass Land 3 - Animal 1" # Kine
grass_land_3_a2 = "Grass Land 3 - Animal 2" # Coo
grass_land_4_a1 = "Grass Land 4 - Animal 1" # ChuChu
grass_land_4_a2 = "Grass Land 4 - Animal 2" # Nago
grass_land_5_a1 = "Grass Land 5 - Animal 1" # Coo
grass_land_5_a2 = "Grass Land 5 - Animal 2" # Kine
grass_land_5_a3 = "Grass Land 5 - Animal 3" # Nago
grass_land_5_a4 = "Grass Land 5 - Animal 4" # Rick
grass_land_6_a1 = "Grass Land 6 - Animal 1" # Rick
grass_land_6_a2 = "Grass Land 6 - Animal 2" # ChuChu
grass_land_6_a3 = "Grass Land 6 - Animal 3" # Nago
grass_land_6_a4 = "Grass Land 6 - Animal 4" # Coo
ripple_field_1_a1 = "Ripple Field 1 - Animal 1" # Pitch
ripple_field_1_a2 = "Ripple Field 1 - Animal 2" # Nago
ripple_field_2_a1 = "Ripple Field 2 - Animal 1" # Kine
ripple_field_2_a2 = "Ripple Field 2 - Animal 2" # ChuChu
ripple_field_2_a3 = "Ripple Field 2 - Animal 3" # Rick
ripple_field_2_a4 = "Ripple Field 2 - Animal 4" # Coo
ripple_field_3_a1 = "Ripple Field 3 - Animal 1" # Kine
ripple_field_3_a2 = "Ripple Field 3 - Animal 2" # Rick
ripple_field_4_a1 = "Ripple Field 4 - Animal 1" # ChuChu
ripple_field_4_a2 = "Ripple Field 4 - Animal 2" # Kine
ripple_field_4_a3 = "Ripple Field 4 - Animal 3" # Nago
ripple_field_5_a1 = "Ripple Field 5 - Animal 1" # Kine
ripple_field_5_a2 = "Ripple Field 5 - Animal 2" # Pitch
ripple_field_6_a1 = "Ripple Field 6 - Animal 1" # Nago
ripple_field_6_a2 = "Ripple Field 6 - Animal 2" # Pitch
ripple_field_6_a3 = "Ripple Field 6 - Animal 3" # Rick
ripple_field_6_a4 = "Ripple Field 6 - Animal 4" # Coo
sand_canyon_1_a1 = "Sand Canyon 1 - Animal 1" # Rick
sand_canyon_1_a2 = "Sand Canyon 1 - Animal 2" # Pitch
sand_canyon_2_a1 = "Sand Canyon 2 - Animal 1" # ChuChu
sand_canyon_2_a2 = "Sand Canyon 2 - Animal 2" # Coo
sand_canyon_3_a1 = "Sand Canyon 3 - Animal 1" # Pitch
sand_canyon_3_a2 = "Sand Canyon 3 - Animal 2" # Coo
sand_canyon_3_a3 = "Sand Canyon 3 - Animal 3" # ChuChu
sand_canyon_4_a1 = "Sand Canyon 4 - Animal 1" # Rick
sand_canyon_4_a2 = "Sand Canyon 4 - Animal 2" # Pitch
sand_canyon_4_a3 = "Sand Canyon 4 - Animal 3" # Nago
sand_canyon_5_a1 = "Sand Canyon 5 - Animal 1" # Rick
sand_canyon_5_a2 = "Sand Canyon 5 - Animal 2" # ChuChu
sand_canyon_6_a1 = "Sand Canyon 6 - Animal 1" # Coo
sand_canyon_6_a2 = "Sand Canyon 6 - Animal 2" # Kine
sand_canyon_6_a3 = "Sand Canyon 6 - Animal 3" # Rick
sand_canyon_6_a4 = "Sand Canyon 6 - Animal 4" # ChuChu
sand_canyon_6_a5 = "Sand Canyon 6 - Animal 5" # Nago
sand_canyon_6_a6 = "Sand Canyon 6 - Animal 6" # Pitch
cloudy_park_1_a1 = "Cloudy Park 1 - Animal 1" # Rick
cloudy_park_1_a2 = "Cloudy Park 1 - Animal 2" # Nago
cloudy_park_1_a3 = "Cloudy Park 1 - Animal 3" # Coo
cloudy_park_1_a4 = "Cloudy Park 1 - Animal 4" # Kine
cloudy_park_1_a5 = "Cloudy Park 1 - Animal 5" # ChuChu
cloudy_park_1_a6 = "Cloudy Park 1 - Animal 6" # Pitch
cloudy_park_2_a1 = "Cloudy Park 2 - Animal 1" # Nago
cloudy_park_2_a2 = "Cloudy Park 2 - Animal 2" # Pitch
cloudy_park_2_a3 = "Cloudy Park 2 - Animal 3" # ChuChu
cloudy_park_3_a1 = "Cloudy Park 3 - Animal 1" # Kine
cloudy_park_3_a2 = "Cloudy Park 3 - Animal 2" # Rick
cloudy_park_3_a3 = "Cloudy Park 3 - Animal 3" # ChuChu
cloudy_park_4_a1 = "Cloudy Park 4 - Animal 1" # Coo
cloudy_park_4_a2 = "Cloudy Park 4 - Animal 2" # ChuChu
cloudy_park_5_a1 = "Cloudy Park 5 - Animal 1" # Rick
cloudy_park_5_a2 = "Cloudy Park 5 - Animal 2" # Coo
cloudy_park_6_a1 = "Cloudy Park 6 - Animal 1" # Nago
cloudy_park_6_a2 = "Cloudy Park 6 - Animal 2" # Coo
cloudy_park_6_a3 = "Cloudy Park 6 - Animal 3" # Rick
iceberg_1_a1 = "Iceberg 1 - Animal 1" # Pitch
iceberg_1_a2 = "Iceberg 1 - Animal 2" # Rick
iceberg_2_a1 = "Iceberg 2 - Animal 1" # Nago
iceberg_2_a2 = "Iceberg 2 - Animal 2" # Pitch
iceberg_3_a1 = "Iceberg 3 - Animal 1" # Pitch
iceberg_3_a2 = "Iceberg 3 - Animal 2" # Coo
iceberg_3_a3 = "Iceberg 3 - Animal 3" # Nago
iceberg_3_a4 = "Iceberg 3 - Animal 4" # Rick
iceberg_3_a5 = "Iceberg 3 - Animal 5" # Kine
iceberg_4_a1 = "Iceberg 4 - Animal 1" # ChuChu
iceberg_4_a2 = "Iceberg 4 - Animal 2" # Coo
iceberg_4_a3 = "Iceberg 4 - Animal 3" # Pitch
iceberg_4_a4 = "Iceberg 4 - Animal 4" # Coo
iceberg_4_a5 = "Iceberg 4 - Animal 5" # Rick
iceberg_5_a1 = "Iceberg 5 - Animal 1" # Kine
iceberg_5_a2 = "Iceberg 5 - Animal 2" # Rick
iceberg_5_a3 = "Iceberg 5 - Animal 3" # Pitch
iceberg_5_a4 = "Iceberg 5 - Animal 4" # ChuChu
iceberg_5_a5 = "Iceberg 5 - Animal 5" # Kine
iceberg_5_a6 = "Iceberg 5 - Animal 6" # Coo
iceberg_5_a7 = "Iceberg 5 - Animal 7" # Rick
iceberg_5_a8 = "Iceberg 5 - Animal 8" # ChuChu
iceberg_6_a1 = "Iceberg 6 - Animal 1" # Rick
iceberg_6_a2 = "Iceberg 6 - Animal 2" # Coo
iceberg_6_a3 = "Iceberg 6 - Animal 3" # Nago
iceberg_6_a4 = "Iceberg 6 - Animal 4" # Kine
iceberg_6_a5 = "Iceberg 6 - Animal 5" # ChuChu
iceberg_6_a6 = "Iceberg 6 - Animal 6" # Nago
animal_friend_spawns = {
grass_land_1_a1: "Nago Spawn",
grass_land_1_a2: "Rick Spawn",
grass_land_2_a1: "ChuChu Spawn",
grass_land_2_a2: "Pitch Spawn",
grass_land_3_a1: "Kine Spawn",
grass_land_3_a2: "Coo Spawn",
grass_land_4_a1: "ChuChu Spawn",
grass_land_4_a2: "Nago Spawn",
grass_land_5_a1: "Coo Spawn",
grass_land_5_a2: "Kine Spawn",
grass_land_5_a3: "Nago Spawn",
grass_land_5_a4: "Rick Spawn",
grass_land_6_a1: "Rick Spawn",
grass_land_6_a2: "ChuChu Spawn",
grass_land_6_a3: "Nago Spawn",
grass_land_6_a4: "Coo Spawn",
ripple_field_1_a1: "Pitch Spawn",
ripple_field_1_a2: "Nago Spawn",
ripple_field_2_a1: "Kine Spawn",
ripple_field_2_a2: "ChuChu Spawn",
ripple_field_2_a3: "Rick Spawn",
ripple_field_2_a4: "Coo Spawn",
ripple_field_3_a1: "Kine Spawn",
ripple_field_3_a2: "Rick Spawn",
ripple_field_4_a1: "ChuChu Spawn",
ripple_field_4_a2: "Kine Spawn",
ripple_field_4_a3: "Nago Spawn",
ripple_field_5_a1: "Kine Spawn",
ripple_field_5_a2: "Pitch Spawn",
ripple_field_6_a1: "Nago Spawn",
ripple_field_6_a2: "Pitch Spawn",
ripple_field_6_a3: "Rick Spawn",
ripple_field_6_a4: "Coo Spawn",
sand_canyon_1_a1: "Rick Spawn",
sand_canyon_1_a2: "Pitch Spawn",
sand_canyon_2_a1: "ChuChu Spawn",
sand_canyon_2_a2: "Coo Spawn",
sand_canyon_3_a1: "Pitch Spawn",
sand_canyon_3_a2: "Coo Spawn",
sand_canyon_3_a3: "ChuChu Spawn",
sand_canyon_4_a1: "Rick Spawn",
sand_canyon_4_a2: "Pitch Spawn",
sand_canyon_4_a3: "Nago Spawn",
sand_canyon_5_a1: "Rick Spawn",
sand_canyon_5_a2: "ChuChu Spawn",
sand_canyon_6_a1: "Coo Spawn",
sand_canyon_6_a2: "Kine Spawn",
sand_canyon_6_a3: "Rick Spawn",
sand_canyon_6_a4: "ChuChu Spawn",
sand_canyon_6_a5: "Nago Spawn",
sand_canyon_6_a6: "Pitch Spawn",
cloudy_park_1_a1: "Rick Spawn",
cloudy_park_1_a2: "Nago Spawn",
cloudy_park_1_a3: "Coo Spawn",
cloudy_park_1_a4: "Kine Spawn",
cloudy_park_1_a5: "ChuChu Spawn",
cloudy_park_1_a6: "Pitch Spawn",
cloudy_park_2_a1: "Nago Spawn",
cloudy_park_2_a2: "Pitch Spawn",
cloudy_park_2_a3: "ChuChu Spawn",
cloudy_park_3_a1: "Kine Spawn",
cloudy_park_3_a2: "Rick Spawn",
cloudy_park_3_a3: "ChuChu Spawn",
cloudy_park_4_a1: "Coo Spawn",
cloudy_park_4_a2: "ChuChu Spawn",
cloudy_park_5_a1: "Rick Spawn",
cloudy_park_5_a2: "Coo Spawn",
cloudy_park_6_a1: "Nago Spawn",
cloudy_park_6_a2: "Coo Spawn",
cloudy_park_6_a3: "Rick Spawn",
iceberg_1_a1: "Pitch Spawn",
iceberg_1_a2: "Rick Spawn",
iceberg_2_a1: "Nago Spawn",
iceberg_2_a2: "Pitch Spawn",
iceberg_3_a1: "Pitch Spawn",
iceberg_3_a2: "Coo Spawn",
iceberg_3_a3: "Nago Spawn",
iceberg_3_a4: "Rick Spawn",
iceberg_3_a5: "Kine Spawn",
iceberg_4_a1: "ChuChu Spawn",
iceberg_4_a2: "Coo Spawn",
iceberg_4_a3: "Pitch Spawn",
iceberg_4_a4: "Coo Spawn",
iceberg_4_a5: "Rick Spawn",
iceberg_5_a1: "Kine Spawn",
iceberg_5_a2: "Rick Spawn",
iceberg_5_a3: "Pitch Spawn",
iceberg_5_a4: "ChuChu Spawn",
iceberg_5_a5: "Kine Spawn",
iceberg_5_a6: "Coo Spawn",
iceberg_5_a7: "Rick Spawn",
iceberg_5_a8: "ChuChu Spawn",
iceberg_6_a1: "Rick Spawn",
iceberg_6_a2: "Coo Spawn",
iceberg_6_a3: "Nago Spawn",
iceberg_6_a4: "Kine Spawn",
iceberg_6_a5: "ChuChu Spawn",
iceberg_6_a6: "Nago Spawn",
}

View File

@@ -0,0 +1,822 @@
from typing import List, Tuple, Set
Grass_Land_1_E1 = "Grass Land 1 - Enemy 1 (Waddle Dee)"
Grass_Land_1_E2 = "Grass Land 1 - Enemy 2 (Sir Kibble)"
Grass_Land_1_E3 = "Grass Land 1 - Enemy 3 (Cappy)"
Grass_Land_1_E4 = "Grass Land 1 - Enemy 4 (Sparky)"
Grass_Land_1_E5 = "Grass Land 1 - Enemy 5 (Bronto Burt)"
Grass_Land_1_E6 = "Grass Land 1 - Enemy 6 (Sasuke)"
Grass_Land_1_E7 = "Grass Land 1 - Enemy 7 (Poppy Bros Jr.)"
Grass_Land_2_E1 = "Grass Land 2 - Enemy 1 (Rocky)"
Grass_Land_2_E2 = "Grass Land 2 - Enemy 2 (KeKe)"
Grass_Land_2_E3 = "Grass Land 2 - Enemy 3 (Bobo)"
Grass_Land_2_E4 = "Grass Land 2 - Enemy 4 (Poppy Bros Jr.)"
Grass_Land_2_E5 = "Grass Land 2 - Enemy 5 (Waddle Dee)"
Grass_Land_2_E6 = "Grass Land 2 - Enemy 6 (Popon Ball)"
Grass_Land_2_E7 = "Grass Land 2 - Enemy 7 (Bouncy)"
Grass_Land_2_E8 = "Grass Land 2 - Enemy 8 (Tick)"
Grass_Land_2_E9 = "Grass Land 2 - Enemy 9 (Bronto Burt)"
Grass_Land_2_E10 = "Grass Land 2 - Enemy 10 (Nruff)"
Grass_Land_3_E1 = "Grass Land 3 - Enemy 1 (Sparky)"
Grass_Land_3_E2 = "Grass Land 3 - Enemy 2 (Rocky)"
Grass_Land_3_E3 = "Grass Land 3 - Enemy 3 (Nruff)"
Grass_Land_3_E4 = "Grass Land 3 - Enemy 4 (Bouncy)"
Grass_Land_4_E1 = "Grass Land 4 - Enemy 1 (Loud)"
Grass_Land_4_E2 = "Grass Land 4 - Enemy 2 (Babut)"
Grass_Land_4_E3 = "Grass Land 4 - Enemy 3 (Rocky)"
Grass_Land_4_E4 = "Grass Land 4 - Enemy 4 (Kapar)"
Grass_Land_4_E5 = "Grass Land 4 - Enemy 5 (Glunk)"
Grass_Land_4_E6 = "Grass Land 4 - Enemy 6 (Oro)"
Grass_Land_4_E7 = "Grass Land 4 - Enemy 7 (Peran)"
Grass_Land_5_E1 = "Grass Land 5 - Enemy 1 (Propeller)"
Grass_Land_5_E2 = "Grass Land 5 - Enemy 2 (Broom Hatter)"
Grass_Land_5_E3 = "Grass Land 5 - Enemy 3 (Bouncy)"
Grass_Land_5_E4 = "Grass Land 5 - Enemy 4 (Sir Kibble)"
Grass_Land_5_E5 = "Grass Land 5 - Enemy 5 (Waddle Dee)"
Grass_Land_5_E6 = "Grass Land 5 - Enemy 6 (Sasuke)"
Grass_Land_5_E7 = "Grass Land 5 - Enemy 7 (Nruff)"
Grass_Land_5_E8 = "Grass Land 5 - Enemy 8 (Tick)"
Grass_Land_6_E1 = "Grass Land 6 - Enemy 1 (Como)"
Grass_Land_6_E2 = "Grass Land 6 - Enemy 2 (Togezo)"
Grass_Land_6_E3 = "Grass Land 6 - Enemy 3 (Bronto Burt)"
Grass_Land_6_E4 = "Grass Land 6 - Enemy 4 (Cappy)"
Grass_Land_6_E5 = "Grass Land 6 - Enemy 5 (Bobo)"
Grass_Land_6_E6 = "Grass Land 6 - Enemy 6 (Mariel)"
Grass_Land_6_E7 = "Grass Land 6 - Enemy 7 (Yaban)"
Grass_Land_6_E8 = "Grass Land 6 - Enemy 8 (Broom Hatter)"
Grass_Land_6_E9 = "Grass Land 6 - Enemy 9 (Apolo)"
Grass_Land_6_E10 = "Grass Land 6 - Enemy 10 (Sasuke)"
Grass_Land_6_E11 = "Grass Land 6 - Enemy 11 (Rocky)"
Ripple_Field_1_E1 = "Ripple Field 1 - Enemy 1 (Waddle Dee)"
Ripple_Field_1_E2 = "Ripple Field 1 - Enemy 2 (Glunk)"
Ripple_Field_1_E3 = "Ripple Field 1 - Enemy 3 (Broom Hatter)"
Ripple_Field_1_E4 = "Ripple Field 1 - Enemy 4 (Cappy)"
Ripple_Field_1_E5 = "Ripple Field 1 - Enemy 5 (Bronto Burt)"
Ripple_Field_1_E6 = "Ripple Field 1 - Enemy 6 (Rocky)"
Ripple_Field_1_E7 = "Ripple Field 1 - Enemy 7 (Poppy Bros Jr.)"
Ripple_Field_1_E8 = "Ripple Field 1 - Enemy 8 (Bobin)"
Ripple_Field_2_E1 = "Ripple Field 2 - Enemy 1 (Togezo)"
Ripple_Field_2_E2 = "Ripple Field 2 - Enemy 2 (Coconut)"
Ripple_Field_2_E3 = "Ripple Field 2 - Enemy 3 (Blipper)"
Ripple_Field_2_E4 = "Ripple Field 2 - Enemy 4 (Sasuke)"
Ripple_Field_2_E5 = "Ripple Field 2 - Enemy 5 (Kany)"
Ripple_Field_2_E6 = "Ripple Field 2 - Enemy 6 (Glunk)"
Ripple_Field_3_E1 = "Ripple Field 3 - Enemy 1 (Raft Waddle Dee)"
Ripple_Field_3_E2 = "Ripple Field 3 - Enemy 2 (Kapar)"
Ripple_Field_3_E3 = "Ripple Field 3 - Enemy 3 (Blipper)"
Ripple_Field_3_E4 = "Ripple Field 3 - Enemy 4 (Sparky)"
Ripple_Field_3_E5 = "Ripple Field 3 - Enemy 5 (Glunk)"
Ripple_Field_3_E6 = "Ripple Field 3 - Enemy 6 (Joe)"
Ripple_Field_3_E7 = "Ripple Field 3 - Enemy 7 (Bobo)"
Ripple_Field_4_E1 = "Ripple Field 4 - Enemy 1 (Bukiset (Stone))"
Ripple_Field_4_E2 = "Ripple Field 4 - Enemy 2 (Bukiset (Needle))"
Ripple_Field_4_E3 = "Ripple Field 4 - Enemy 3 (Bukiset (Clean))"
Ripple_Field_4_E4 = "Ripple Field 4 - Enemy 4 (Bukiset (Parasol))"
Ripple_Field_4_E5 = "Ripple Field 4 - Enemy 5 (Mony)"
Ripple_Field_4_E6 = "Ripple Field 4 - Enemy 6 (Bukiset (Burning))"
Ripple_Field_4_E7 = "Ripple Field 4 - Enemy 7 (Bobin)"
Ripple_Field_4_E8 = "Ripple Field 4 - Enemy 8 (Blipper)"
Ripple_Field_4_E9 = "Ripple Field 4 - Enemy 9 (Como)"
Ripple_Field_4_E10 = "Ripple Field 4 - Enemy 10 (Oro)"
Ripple_Field_4_E11 = "Ripple Field 4 - Enemy 11 (Gansan)"
Ripple_Field_4_E12 = "Ripple Field 4 - Enemy 12 (Waddle Dee)"
Ripple_Field_4_E13 = "Ripple Field 4 - Enemy 13 (Kapar)"
Ripple_Field_4_E14 = "Ripple Field 4 - Enemy 14 (Squishy)"
Ripple_Field_4_E15 = "Ripple Field 4 - Enemy 15 (Nidoo)"
Ripple_Field_5_E1 = "Ripple Field 5 - Enemy 1 (Glunk)"
Ripple_Field_5_E2 = "Ripple Field 5 - Enemy 2 (Joe)"
Ripple_Field_5_E3 = "Ripple Field 5 - Enemy 3 (Bobin)"
Ripple_Field_5_E4 = "Ripple Field 5 - Enemy 4 (Mony)"
Ripple_Field_5_E5 = "Ripple Field 5 - Enemy 5 (Squishy)"
Ripple_Field_5_E6 = "Ripple Field 5 - Enemy 6 (Yaban)"
Ripple_Field_5_E7 = "Ripple Field 5 - Enemy 7 (Broom Hatter)"
Ripple_Field_5_E8 = "Ripple Field 5 - Enemy 8 (Bouncy)"
Ripple_Field_5_E9 = "Ripple Field 5 - Enemy 9 (Sparky)"
Ripple_Field_5_E10 = "Ripple Field 5 - Enemy 10 (Rocky)"
Ripple_Field_5_E11 = "Ripple Field 5 - Enemy 11 (Babut)"
Ripple_Field_5_E12 = "Ripple Field 5 - Enemy 12 (Galbo)"
Ripple_Field_6_E1 = "Ripple Field 6 - Enemy 1 (Kany)"
Ripple_Field_6_E2 = "Ripple Field 6 - Enemy 2 (KeKe)"
Ripple_Field_6_E3 = "Ripple Field 6 - Enemy 3 (Kapar)"
Ripple_Field_6_E4 = "Ripple Field 6 - Enemy 4 (Rocky)"
Ripple_Field_6_E5 = "Ripple Field 6 - Enemy 5 (Poppy Bros Jr.)"
Ripple_Field_6_E6 = "Ripple Field 6 - Enemy 6 (Propeller)"
Ripple_Field_6_E7 = "Ripple Field 6 - Enemy 7 (Coconut)"
Ripple_Field_6_E8 = "Ripple Field 6 - Enemy 8 (Sasuke)"
Ripple_Field_6_E9 = "Ripple Field 6 - Enemy 9 (Nruff)"
Sand_Canyon_1_E1 = "Sand Canyon 1 - Enemy 1 (Bronto Burt)"
Sand_Canyon_1_E2 = "Sand Canyon 1 - Enemy 2 (Galbo)"
Sand_Canyon_1_E3 = "Sand Canyon 1 - Enemy 3 (Oro)"
Sand_Canyon_1_E4 = "Sand Canyon 1 - Enemy 4 (Sparky)"
Sand_Canyon_1_E5 = "Sand Canyon 1 - Enemy 5 (Propeller)"
Sand_Canyon_1_E6 = "Sand Canyon 1 - Enemy 6 (Gansan)"
Sand_Canyon_1_E7 = "Sand Canyon 1 - Enemy 7 (Babut)"
Sand_Canyon_1_E8 = "Sand Canyon 1 - Enemy 8 (Loud)"
Sand_Canyon_1_E9 = "Sand Canyon 1 - Enemy 9 (Dogon)"
Sand_Canyon_1_E10 = "Sand Canyon 1 - Enemy 10 (Bouncy)"
Sand_Canyon_1_E11 = "Sand Canyon 1 - Enemy 11 (Pteran)"
Sand_Canyon_1_E12 = "Sand Canyon 1 - Enemy 12 (Polof)"
Sand_Canyon_2_E1 = "Sand Canyon 2 - Enemy 1 (KeKe)"
Sand_Canyon_2_E2 = "Sand Canyon 2 - Enemy 2 (Doka)"
Sand_Canyon_2_E3 = "Sand Canyon 2 - Enemy 3 (Boten)"
Sand_Canyon_2_E4 = "Sand Canyon 2 - Enemy 4 (Propeller)"
Sand_Canyon_2_E5 = "Sand Canyon 2 - Enemy 5 (Waddle Dee)"
Sand_Canyon_2_E6 = "Sand Canyon 2 - Enemy 6 (Sparky)"
Sand_Canyon_2_E7 = "Sand Canyon 2 - Enemy 7 (Sasuke)"
Sand_Canyon_2_E8 = "Sand Canyon 2 - Enemy 8 (Como)"
Sand_Canyon_2_E9 = "Sand Canyon 2 - Enemy 9 (Bukiset (Ice))"
Sand_Canyon_2_E10 = "Sand Canyon 2 - Enemy 10 (Bukiset (Needle))"
Sand_Canyon_2_E11 = "Sand Canyon 2 - Enemy 11 (Bukiset (Clean))"
Sand_Canyon_2_E12 = "Sand Canyon 2 - Enemy 12 (Bukiset (Parasol))"
Sand_Canyon_2_E13 = "Sand Canyon 2 - Enemy 13 (Bukiset (Spark))"
Sand_Canyon_2_E14 = "Sand Canyon 2 - Enemy 14 (Bukiset (Cutter))"
Sand_Canyon_2_E15 = "Sand Canyon 2 - Enemy 15 (Nidoo)"
Sand_Canyon_2_E16 = "Sand Canyon 2 - Enemy 16 (Mariel)"
Sand_Canyon_2_E17 = "Sand Canyon 2 - Enemy 17 (Yaban)"
Sand_Canyon_2_E18 = "Sand Canyon 2 - Enemy 18 (Wapod)"
Sand_Canyon_2_E19 = "Sand Canyon 2 - Enemy 19 (Squishy)"
Sand_Canyon_2_E20 = "Sand Canyon 2 - Enemy 20 (Pteran)"
Sand_Canyon_3_E1 = "Sand Canyon 3 - Enemy 1 (Sir Kibble)"
Sand_Canyon_3_E2 = "Sand Canyon 3 - Enemy 2 (Broom Hatter)"
Sand_Canyon_3_E3 = "Sand Canyon 3 - Enemy 3 (Rocky)"
Sand_Canyon_3_E4 = "Sand Canyon 3 - Enemy 4 (Gabon)"
Sand_Canyon_3_E5 = "Sand Canyon 3 - Enemy 5 (Kany)"
Sand_Canyon_3_E6 = "Sand Canyon 3 - Enemy 6 (Galbo)"
Sand_Canyon_3_E7 = "Sand Canyon 3 - Enemy 7 (Propeller)"
Sand_Canyon_3_E8 = "Sand Canyon 3 - Enemy 8 (Sasuke)"
Sand_Canyon_3_E9 = "Sand Canyon 3 - Enemy 9 (Wapod)"
Sand_Canyon_3_E10 = "Sand Canyon 3 - Enemy 10 (Bobo)"
Sand_Canyon_3_E11 = "Sand Canyon 3 - Enemy 11 (Babut)"
Sand_Canyon_3_E12 = "Sand Canyon 3 - Enemy 12 (Magoo)"
Sand_Canyon_4_E1 = "Sand Canyon 4 - Enemy 1 (Popon Ball)"
Sand_Canyon_4_E2 = "Sand Canyon 4 - Enemy 2 (Mariel)"
Sand_Canyon_4_E3 = "Sand Canyon 4 - Enemy 3 (Chilly)"
Sand_Canyon_4_E4 = "Sand Canyon 4 - Enemy 4 (Tick)"
Sand_Canyon_4_E5 = "Sand Canyon 4 - Enemy 5 (Bronto Burt)"
Sand_Canyon_4_E6 = "Sand Canyon 4 - Enemy 6 (Babut)"
Sand_Canyon_4_E7 = "Sand Canyon 4 - Enemy 7 (Bobin)"
Sand_Canyon_4_E8 = "Sand Canyon 4 - Enemy 8 (Joe)"
Sand_Canyon_4_E9 = "Sand Canyon 4 - Enemy 9 (Mony)"
Sand_Canyon_4_E10 = "Sand Canyon 4 - Enemy 10 (Blipper)"
Sand_Canyon_4_E11 = "Sand Canyon 4 - Enemy 11 (Togezo)"
Sand_Canyon_4_E12 = "Sand Canyon 4 - Enemy 12 (Rocky)"
Sand_Canyon_4_E13 = "Sand Canyon 4 - Enemy 13 (Bobo)"
Sand_Canyon_5_E1 = "Sand Canyon 5 - Enemy 1 (Wapod)"
Sand_Canyon_5_E2 = "Sand Canyon 5 - Enemy 2 (Dogon)"
Sand_Canyon_5_E3 = "Sand Canyon 5 - Enemy 3 (Tick)"
Sand_Canyon_5_E4 = "Sand Canyon 5 - Enemy 4 (Rocky)"
Sand_Canyon_5_E5 = "Sand Canyon 5 - Enemy 5 (Bobo)"
Sand_Canyon_5_E6 = "Sand Canyon 5 - Enemy 6 (Chilly)"
Sand_Canyon_5_E7 = "Sand Canyon 5 - Enemy 7 (Sparky)"
Sand_Canyon_5_E8 = "Sand Canyon 5 - Enemy 8 (Togezo)"
Sand_Canyon_5_E9 = "Sand Canyon 5 - Enemy 9 (Bronto Burt)"
Sand_Canyon_5_E10 = "Sand Canyon 5 - Enemy 10 (Sasuke)"
Sand_Canyon_5_E11 = "Sand Canyon 5 - Enemy 11 (Oro)"
Sand_Canyon_5_E12 = "Sand Canyon 5 - Enemy 12 (Galbo)"
Sand_Canyon_5_E13 = "Sand Canyon 5 - Enemy 13 (Nidoo)"
Sand_Canyon_5_E14 = "Sand Canyon 5 - Enemy 14 (Propeller)"
Sand_Canyon_5_E15 = "Sand Canyon 5 - Enemy 15 (Sir Kibble)"
Sand_Canyon_5_E16 = "Sand Canyon 5 - Enemy 16 (KeKe)"
Sand_Canyon_5_E17 = "Sand Canyon 5 - Enemy 17 (Kabu)"
Sand_Canyon_6_E1 = "Sand Canyon 6 - Enemy 1 (Sparky)"
Sand_Canyon_6_E2 = "Sand Canyon 6 - Enemy 2 (Doka)"
Sand_Canyon_6_E3 = "Sand Canyon 6 - Enemy 3 (Cappy)"
Sand_Canyon_6_E4 = "Sand Canyon 6 - Enemy 4 (Pteran)"
Sand_Canyon_6_E5 = "Sand Canyon 6 - Enemy 5 (Bukiset (Parasol))"
Sand_Canyon_6_E6 = "Sand Canyon 6 - Enemy 6 (Bukiset (Cutter))"
Sand_Canyon_6_E7 = "Sand Canyon 6 - Enemy 7 (Bukiset (Clean))"
Sand_Canyon_6_E8 = "Sand Canyon 6 - Enemy 8 (Bukiset (Spark))"
Sand_Canyon_6_E9 = "Sand Canyon 6 - Enemy 9 (Bukiset (Ice))"
Sand_Canyon_6_E10 = "Sand Canyon 6 - Enemy 10 (Bukiset (Needle))"
Sand_Canyon_6_E11 = "Sand Canyon 6 - Enemy 11 (Bukiset (Burning))"
Sand_Canyon_6_E12 = "Sand Canyon 6 - Enemy 12 (Bukiset (Stone))"
Sand_Canyon_6_E13 = "Sand Canyon 6 - Enemy 13 (Nidoo)"
Cloudy_Park_1_E1 = "Cloudy Park 1 - Enemy 1 (Waddle Dee)"
Cloudy_Park_1_E2 = "Cloudy Park 1 - Enemy 2 (KeKe)"
Cloudy_Park_1_E3 = "Cloudy Park 1 - Enemy 3 (Cappy)"
Cloudy_Park_1_E4 = "Cloudy Park 1 - Enemy 4 (Yaban)"
Cloudy_Park_1_E5 = "Cloudy Park 1 - Enemy 5 (Togezo)"
Cloudy_Park_1_E6 = "Cloudy Park 1 - Enemy 6 (Galbo)"
Cloudy_Park_1_E7 = "Cloudy Park 1 - Enemy 7 (Sparky)"
Cloudy_Park_1_E8 = "Cloudy Park 1 - Enemy 8 (Como)"
Cloudy_Park_1_E9 = "Cloudy Park 1 - Enemy 9 (Bronto Burt)"
Cloudy_Park_1_E10 = "Cloudy Park 1 - Enemy 10 (Gabon)"
Cloudy_Park_1_E11 = "Cloudy Park 1 - Enemy 11 (Sir Kibble)"
Cloudy_Park_1_E12 = "Cloudy Park 1 - Enemy 12 (Mariel)"
Cloudy_Park_1_E13 = "Cloudy Park 1 - Enemy 13 (Nruff)"
Cloudy_Park_2_E1 = "Cloudy Park 2 - Enemy 1 (Chilly)"
Cloudy_Park_2_E2 = "Cloudy Park 2 - Enemy 2 (Sasuke)"
Cloudy_Park_2_E3 = "Cloudy Park 2 - Enemy 3 (Waddle Dee)"
Cloudy_Park_2_E4 = "Cloudy Park 2 - Enemy 4 (Sparky)"
Cloudy_Park_2_E5 = "Cloudy Park 2 - Enemy 5 (Broom Hatter)"
Cloudy_Park_2_E6 = "Cloudy Park 2 - Enemy 6 (Sir Kibble)"
Cloudy_Park_2_E7 = "Cloudy Park 2 - Enemy 7 (Pteran)"
Cloudy_Park_2_E8 = "Cloudy Park 2 - Enemy 8 (Propeller)"
Cloudy_Park_2_E9 = "Cloudy Park 2 - Enemy 9 (Dogon)"
Cloudy_Park_2_E10 = "Cloudy Park 2 - Enemy 10 (Togezo)"
Cloudy_Park_2_E11 = "Cloudy Park 2 - Enemy 11 (Oro)"
Cloudy_Park_2_E12 = "Cloudy Park 2 - Enemy 12 (Bronto Burt)"
Cloudy_Park_2_E13 = "Cloudy Park 2 - Enemy 13 (Rocky)"
Cloudy_Park_2_E14 = "Cloudy Park 2 - Enemy 14 (Galbo)"
Cloudy_Park_2_E15 = "Cloudy Park 2 - Enemy 15 (Kapar)"
Cloudy_Park_3_E1 = "Cloudy Park 3 - Enemy 1 (Bronto Burt)"
Cloudy_Park_3_E2 = "Cloudy Park 3 - Enemy 2 (Mopoo)"
Cloudy_Park_3_E3 = "Cloudy Park 3 - Enemy 3 (Poppy Bros Jr.)"
Cloudy_Park_3_E4 = "Cloudy Park 3 - Enemy 4 (Como)"
Cloudy_Park_3_E5 = "Cloudy Park 3 - Enemy 5 (Glunk)"
Cloudy_Park_3_E6 = "Cloudy Park 3 - Enemy 6 (Bobin)"
Cloudy_Park_3_E7 = "Cloudy Park 3 - Enemy 7 (Loud)"
Cloudy_Park_3_E8 = "Cloudy Park 3 - Enemy 8 (Kapar)"
Cloudy_Park_3_E9 = "Cloudy Park 3 - Enemy 9 (Galbo)"
Cloudy_Park_3_E10 = "Cloudy Park 3 - Enemy 10 (Batamon)"
Cloudy_Park_3_E11 = "Cloudy Park 3 - Enemy 11 (Bouncy)"
Cloudy_Park_4_E1 = "Cloudy Park 4 - Enemy 1 (Gabon)"
Cloudy_Park_4_E2 = "Cloudy Park 4 - Enemy 2 (Como)"
Cloudy_Park_4_E3 = "Cloudy Park 4 - Enemy 3 (Wapod)"
Cloudy_Park_4_E4 = "Cloudy Park 4 - Enemy 4 (Cappy)"
Cloudy_Park_4_E5 = "Cloudy Park 4 - Enemy 5 (Sparky)"
Cloudy_Park_4_E6 = "Cloudy Park 4 - Enemy 6 (Togezo)"
Cloudy_Park_4_E7 = "Cloudy Park 4 - Enemy 7 (Bronto Burt)"
Cloudy_Park_4_E8 = "Cloudy Park 4 - Enemy 8 (KeKe)"
Cloudy_Park_4_E9 = "Cloudy Park 4 - Enemy 9 (Bouncy)"
Cloudy_Park_4_E10 = "Cloudy Park 4 - Enemy 10 (Sir Kibble)"
Cloudy_Park_4_E11 = "Cloudy Park 4 - Enemy 11 (Mariel)"
Cloudy_Park_4_E12 = "Cloudy Park 4 - Enemy 12 (Kabu)"
Cloudy_Park_4_E13 = "Cloudy Park 4 - Enemy 13 (Wappa)"
Cloudy_Park_5_E1 = "Cloudy Park 5 - Enemy 1 (Yaban)"
Cloudy_Park_5_E2 = "Cloudy Park 5 - Enemy 2 (Sir Kibble)"
Cloudy_Park_5_E3 = "Cloudy Park 5 - Enemy 3 (Cappy)"
Cloudy_Park_5_E4 = "Cloudy Park 5 - Enemy 4 (Wappa)"
Cloudy_Park_5_E5 = "Cloudy Park 5 - Enemy 5 (Galbo)"
Cloudy_Park_5_E6 = "Cloudy Park 5 - Enemy 6 (Bronto Burt)"
Cloudy_Park_5_E7 = "Cloudy Park 5 - Enemy 7 (KeKe)"
Cloudy_Park_5_E8 = "Cloudy Park 5 - Enemy 8 (Propeller)"
Cloudy_Park_5_E9 = "Cloudy Park 5 - Enemy 9 (Klinko)"
Cloudy_Park_5_E10 = "Cloudy Park 5 - Enemy 10 (Wapod)"
Cloudy_Park_5_E11 = "Cloudy Park 5 - Enemy 11 (Pteran)"
Cloudy_Park_6_E1 = "Cloudy Park 6 - Enemy 1 (Madoo)"
Cloudy_Park_6_E2 = "Cloudy Park 6 - Enemy 2 (Tick)"
Cloudy_Park_6_E3 = "Cloudy Park 6 - Enemy 3 (Como)"
Cloudy_Park_6_E4 = "Cloudy Park 6 - Enemy 4 (Waddle Dee Drawing)"
Cloudy_Park_6_E5 = "Cloudy Park 6 - Enemy 5 (Bronto Burt Drawing)"
Cloudy_Park_6_E6 = "Cloudy Park 6 - Enemy 6 (Bouncy Drawing)"
Cloudy_Park_6_E7 = "Cloudy Park 6 - Enemy 7 (Propeller)"
Cloudy_Park_6_E8 = "Cloudy Park 6 - Enemy 8 (Mopoo)"
Cloudy_Park_6_E9 = "Cloudy Park 6 - Enemy 9 (Bukiset (Burning))"
Cloudy_Park_6_E10 = "Cloudy Park 6 - Enemy 10 (Bukiset (Ice))"
Cloudy_Park_6_E11 = "Cloudy Park 6 - Enemy 11 (Bukiset (Needle))"
Cloudy_Park_6_E12 = "Cloudy Park 6 - Enemy 12 (Bukiset (Clean))"
Cloudy_Park_6_E13 = "Cloudy Park 6 - Enemy 13 (Bukiset (Cutter))"
Iceberg_1_E1 = "Iceberg 1 - Enemy 1 (Waddle Dee)"
Iceberg_1_E2 = "Iceberg 1 - Enemy 2 (Klinko)"
Iceberg_1_E3 = "Iceberg 1 - Enemy 3 (KeKe)"
Iceberg_1_E4 = "Iceberg 1 - Enemy 4 (Como)"
Iceberg_1_E5 = "Iceberg 1 - Enemy 5 (Galbo)"
Iceberg_1_E6 = "Iceberg 1 - Enemy 6 (Rocky)"
Iceberg_1_E7 = "Iceberg 1 - Enemy 7 (Kapar)"
Iceberg_1_E8 = "Iceberg 1 - Enemy 8 (Mopoo)"
Iceberg_1_E9 = "Iceberg 1 - Enemy 9 (Babut)"
Iceberg_1_E10 = "Iceberg 1 - Enemy 10 (Wappa)"
Iceberg_1_E11 = "Iceberg 1 - Enemy 11 (Bronto Burt)"
Iceberg_1_E12 = "Iceberg 1 - Enemy 12 (Chilly)"
Iceberg_1_E13 = "Iceberg 1 - Enemy 13 (Poppy Bros Jr.)"
Iceberg_2_E1 = "Iceberg 2 - Enemy 1 (Gabon)"
Iceberg_2_E2 = "Iceberg 2 - Enemy 2 (Nruff)"
Iceberg_2_E3 = "Iceberg 2 - Enemy 3 (Waddle Dee)"
Iceberg_2_E4 = "Iceberg 2 - Enemy 4 (Chilly)"
Iceberg_2_E5 = "Iceberg 2 - Enemy 5 (Pteran)"
Iceberg_2_E6 = "Iceberg 2 - Enemy 6 (Glunk)"
Iceberg_2_E7 = "Iceberg 2 - Enemy 7 (Galbo)"
Iceberg_2_E8 = "Iceberg 2 - Enemy 8 (Babut)"
Iceberg_2_E9 = "Iceberg 2 - Enemy 9 (Magoo)"
Iceberg_2_E10 = "Iceberg 2 - Enemy 10 (Propeller)"
Iceberg_2_E11 = "Iceberg 2 - Enemy 11 (Nidoo)"
Iceberg_2_E12 = "Iceberg 2 - Enemy 12 (Oro)"
Iceberg_2_E13 = "Iceberg 2 - Enemy 13 (Klinko)"
Iceberg_2_E14 = "Iceberg 2 - Enemy 14 (Bronto Burt)"
Iceberg_3_E1 = "Iceberg 3 - Enemy 1 (Corori)"
Iceberg_3_E2 = "Iceberg 3 - Enemy 2 (Bouncy)"
Iceberg_3_E3 = "Iceberg 3 - Enemy 3 (Chilly)"
Iceberg_3_E4 = "Iceberg 3 - Enemy 4 (Pteran)"
Iceberg_3_E5 = "Iceberg 3 - Enemy 5 (Raft Waddle Dee)"
Iceberg_3_E6 = "Iceberg 3 - Enemy 6 (Kapar)"
Iceberg_3_E7 = "Iceberg 3 - Enemy 7 (Blipper)"
Iceberg_3_E8 = "Iceberg 3 - Enemy 8 (Wapod)"
Iceberg_3_E9 = "Iceberg 3 - Enemy 9 (Glunk)"
Iceberg_3_E10 = "Iceberg 3 - Enemy 10 (Icicle)"
Iceberg_4_E1 = "Iceberg 4 - Enemy 1 (Bronto Burt)"
Iceberg_4_E2 = "Iceberg 4 - Enemy 2 (Galbo)"
Iceberg_4_E3 = "Iceberg 4 - Enemy 3 (Klinko)"
Iceberg_4_E4 = "Iceberg 4 - Enemy 4 (Chilly)"
Iceberg_4_E5 = "Iceberg 4 - Enemy 5 (Babut)"
Iceberg_4_E6 = "Iceberg 4 - Enemy 6 (Wappa)"
Iceberg_4_E7 = "Iceberg 4 - Enemy 7 (Icicle)"
Iceberg_4_E8 = "Iceberg 4 - Enemy 8 (Corori)"
Iceberg_4_E9 = "Iceberg 4 - Enemy 9 (Gabon)"
Iceberg_4_E10 = "Iceberg 4 - Enemy 10 (Kabu)"
Iceberg_4_E11 = "Iceberg 4 - Enemy 11 (Broom Hatter)"
Iceberg_4_E12 = "Iceberg 4 - Enemy 12 (Sasuke)"
Iceberg_4_E13 = "Iceberg 4 - Enemy 13 (Nruff)"
Iceberg_5_E1 = "Iceberg 5 - Enemy 1 (Bukiset (Burning))"
Iceberg_5_E2 = "Iceberg 5 - Enemy 2 (Bukiset (Stone))"
Iceberg_5_E3 = "Iceberg 5 - Enemy 3 (Bukiset (Ice))"
Iceberg_5_E4 = "Iceberg 5 - Enemy 4 (Bukiset (Needle))"
Iceberg_5_E5 = "Iceberg 5 - Enemy 5 (Bukiset (Clean))"
Iceberg_5_E6 = "Iceberg 5 - Enemy 6 (Bukiset (Parasol))"
Iceberg_5_E7 = "Iceberg 5 - Enemy 7 (Bukiset (Spark))"
Iceberg_5_E8 = "Iceberg 5 - Enemy 8 (Bukiset (Cutter))"
Iceberg_5_E9 = "Iceberg 5 - Enemy 9 (Glunk)"
Iceberg_5_E10 = "Iceberg 5 - Enemy 10 (Wapod)"
Iceberg_5_E11 = "Iceberg 5 - Enemy 11 (Tick)"
Iceberg_5_E12 = "Iceberg 5 - Enemy 12 (Madoo)"
Iceberg_5_E13 = "Iceberg 5 - Enemy 13 (Yaban)"
Iceberg_5_E14 = "Iceberg 5 - Enemy 14 (Propeller)"
Iceberg_5_E15 = "Iceberg 5 - Enemy 15 (Mariel)"
Iceberg_5_E16 = "Iceberg 5 - Enemy 16 (Pteran)"
Iceberg_5_E17 = "Iceberg 5 - Enemy 17 (Galbo)"
Iceberg_5_E18 = "Iceberg 5 - Enemy 18 (KeKe)"
Iceberg_5_E19 = "Iceberg 5 - Enemy 19 (Nidoo)"
Iceberg_5_E20 = "Iceberg 5 - Enemy 20 (Waddle Dee Drawing)"
Iceberg_5_E21 = "Iceberg 5 - Enemy 21 (Bronto Burt Drawing)"
Iceberg_5_E22 = "Iceberg 5 - Enemy 22 (Bouncy Drawing)"
Iceberg_5_E23 = "Iceberg 5 - Enemy 23 (Joe)"
Iceberg_5_E24 = "Iceberg 5 - Enemy 24 (Kapar)"
Iceberg_5_E25 = "Iceberg 5 - Enemy 25 (Gansan)"
Iceberg_5_E26 = "Iceberg 5 - Enemy 26 (Sasuke)"
Iceberg_5_E27 = "Iceberg 5 - Enemy 27 (Togezo)"
Iceberg_5_E28 = "Iceberg 5 - Enemy 28 (Sparky)"
Iceberg_5_E29 = "Iceberg 5 - Enemy 29 (Bobin)"
Iceberg_5_E30 = "Iceberg 5 - Enemy 30 (Chilly)"
Iceberg_5_E31 = "Iceberg 5 - Enemy 31 (Peran)"
Iceberg_6_E1 = "Iceberg 6 - Enemy 1 (Nruff)"
Iceberg_6_E2 = "Iceberg 6 - Enemy 2 (Nidoo)"
Iceberg_6_E3 = "Iceberg 6 - Enemy 3 (Sparky)"
Iceberg_6_E4 = "Iceberg 6 - Enemy 4 (Sir Kibble)"
Grass_Land_4_M1 = "Grass Land 4 - Miniboss 1 (Boboo)"
Ripple_Field_4_M1 = "Ripple Field 4 - Miniboss 1 (Captain Stitch)"
Sand_Canyon_4_M1 = "Sand Canyon 4 - Miniboss 1 (Haboki)"
Cloudy_Park_4_M1 = "Cloudy Park 4 - Miniboss 1 (Jumper Shoot)"
Iceberg_4_M1 = "Iceberg 4 - Miniboss 1 (Yuki)"
Iceberg_6_M1 = "Iceberg 6 - Miniboss 1 (Blocky)"
Iceberg_6_M2 = "Iceberg 6 - Miniboss 2 (Jumper Shoot)"
Iceberg_6_M3 = "Iceberg 6 - Miniboss 3 (Yuki)"
Iceberg_6_M4 = "Iceberg 6 - Miniboss 4 (Haboki)"
Iceberg_6_M5 = "Iceberg 6 - Miniboss 5 (Boboo)"
Iceberg_6_M6 = "Iceberg 6 - Miniboss 6 (Captain Stitch)"
enemy_mapping = {
Grass_Land_1_E1: "Waddle Dee",
Grass_Land_1_E2: "Sir Kibble",
Grass_Land_1_E3: "Cappy",
Grass_Land_1_E4: "Sparky",
Grass_Land_1_E5: "Bronto Burt",
Grass_Land_1_E6: "Sasuke",
Grass_Land_1_E7: "Poppy Bros Jr.",
Grass_Land_2_E1: "Rocky",
Grass_Land_2_E2: "KeKe",
Grass_Land_2_E3: "Bobo",
Grass_Land_2_E4: "Poppy Bros Jr.",
Grass_Land_2_E5: "Waddle Dee",
Grass_Land_2_E6: "Popon Ball",
Grass_Land_2_E7: "Bouncy",
Grass_Land_2_E8: "Tick",
Grass_Land_2_E9: "Bronto Burt",
Grass_Land_2_E10: "Nruff",
Grass_Land_3_E1: "Sparky",
Grass_Land_3_E2: "Rocky",
Grass_Land_3_E3: "Nruff",
Grass_Land_3_E4: "Bouncy",
Grass_Land_4_E1: "Loud",
Grass_Land_4_E2: "Babut",
Grass_Land_4_E3: "Rocky",
Grass_Land_4_E4: "Kapar",
Grass_Land_4_E5: "Glunk",
Grass_Land_4_E6: "Oro",
Grass_Land_4_E7: "Peran",
Grass_Land_5_E1: "Propeller",
Grass_Land_5_E2: "Broom Hatter",
Grass_Land_5_E3: "Bouncy",
Grass_Land_5_E4: "Sir Kibble",
Grass_Land_5_E5: "Waddle Dee",
Grass_Land_5_E6: "Sasuke",
Grass_Land_5_E7: "Nruff",
Grass_Land_5_E8: "Tick",
Grass_Land_6_E1: "Como",
Grass_Land_6_E2: "Togezo",
Grass_Land_6_E3: "Bronto Burt",
Grass_Land_6_E4: "Cappy",
Grass_Land_6_E5: "Bobo",
Grass_Land_6_E6: "Mariel",
Grass_Land_6_E7: "Yaban",
Grass_Land_6_E8: "Broom Hatter",
Grass_Land_6_E9: "Apolo",
Grass_Land_6_E10: "Sasuke",
Grass_Land_6_E11: "Rocky",
Ripple_Field_1_E1: "Waddle Dee",
Ripple_Field_1_E2: "Glunk",
Ripple_Field_1_E3: "Broom Hatter",
Ripple_Field_1_E4: "Cappy",
Ripple_Field_1_E5: "Bronto Burt",
Ripple_Field_1_E6: "Rocky",
Ripple_Field_1_E7: "Poppy Bros Jr.",
Ripple_Field_1_E8: "Bobin",
Ripple_Field_2_E1: "Togezo",
Ripple_Field_2_E2: "Coconut",
Ripple_Field_2_E3: "Blipper",
Ripple_Field_2_E4: "Sasuke",
Ripple_Field_2_E5: "Kany",
Ripple_Field_2_E6: "Glunk",
Ripple_Field_3_E1: "Raft Waddle Dee",
Ripple_Field_3_E2: "Kapar",
Ripple_Field_3_E3: "Blipper",
Ripple_Field_3_E4: "Sparky",
Ripple_Field_3_E5: "Glunk",
Ripple_Field_3_E6: "Joe",
Ripple_Field_3_E7: "Bobo",
Ripple_Field_4_E1: "Bukiset (Stone)",
Ripple_Field_4_E2: "Bukiset (Needle)",
Ripple_Field_4_E3: "Bukiset (Clean)",
Ripple_Field_4_E4: "Bukiset (Parasol)",
Ripple_Field_4_E5: "Mony",
Ripple_Field_4_E6: "Bukiset (Burning)",
Ripple_Field_4_E7: "Bobin",
Ripple_Field_4_E8: "Blipper",
Ripple_Field_4_E9: "Como",
Ripple_Field_4_E10: "Oro",
Ripple_Field_4_E11: "Gansan",
Ripple_Field_4_E12: "Waddle Dee",
Ripple_Field_4_E13: "Kapar",
Ripple_Field_4_E14: "Squishy",
Ripple_Field_4_E15: "Nidoo",
Ripple_Field_5_E1: "Glunk",
Ripple_Field_5_E2: "Joe",
Ripple_Field_5_E3: "Bobin",
Ripple_Field_5_E4: "Mony",
Ripple_Field_5_E5: "Squishy",
Ripple_Field_5_E6: "Yaban",
Ripple_Field_5_E7: "Broom Hatter",
Ripple_Field_5_E8: "Bouncy",
Ripple_Field_5_E9: "Sparky",
Ripple_Field_5_E10: "Rocky",
Ripple_Field_5_E11: "Babut",
Ripple_Field_5_E12: "Galbo",
Ripple_Field_6_E1: "Kany",
Ripple_Field_6_E2: "KeKe",
Ripple_Field_6_E3: "Kapar",
Ripple_Field_6_E4: "Rocky",
Ripple_Field_6_E5: "Poppy Bros Jr.",
Ripple_Field_6_E6: "Propeller",
Ripple_Field_6_E7: "Coconut",
Ripple_Field_6_E8: "Sasuke",
Ripple_Field_6_E9: "Nruff",
Sand_Canyon_1_E1: "Bronto Burt",
Sand_Canyon_1_E2: "Galbo",
Sand_Canyon_1_E3: "Oro",
Sand_Canyon_1_E4: "Sparky",
Sand_Canyon_1_E5: "Propeller",
Sand_Canyon_1_E6: "Gansan",
Sand_Canyon_1_E7: "Babut",
Sand_Canyon_1_E8: "Loud",
Sand_Canyon_1_E9: "Dogon",
Sand_Canyon_1_E10: "Bouncy",
Sand_Canyon_1_E11: "Pteran",
Sand_Canyon_1_E12: "Polof",
Sand_Canyon_2_E1: "KeKe",
Sand_Canyon_2_E2: "Doka",
Sand_Canyon_2_E3: "Boten",
Sand_Canyon_2_E4: "Propeller",
Sand_Canyon_2_E5: "Waddle Dee",
Sand_Canyon_2_E6: "Sparky",
Sand_Canyon_2_E7: "Sasuke",
Sand_Canyon_2_E8: "Como",
Sand_Canyon_2_E9: "Bukiset (Ice)",
Sand_Canyon_2_E10: "Bukiset (Needle)",
Sand_Canyon_2_E11: "Bukiset (Clean)",
Sand_Canyon_2_E12: "Bukiset (Parasol)",
Sand_Canyon_2_E13: "Bukiset (Spark)",
Sand_Canyon_2_E14: "Bukiset (Cutter)",
Sand_Canyon_2_E15: "Nidoo",
Sand_Canyon_2_E16: "Mariel",
Sand_Canyon_2_E17: "Yaban",
Sand_Canyon_2_E18: "Wapod",
Sand_Canyon_2_E19: "Squishy",
Sand_Canyon_2_E20: "Pteran",
Sand_Canyon_3_E1: "Sir Kibble",
Sand_Canyon_3_E2: "Broom Hatter",
Sand_Canyon_3_E3: "Rocky",
Sand_Canyon_3_E4: "Gabon",
Sand_Canyon_3_E5: "Kany",
Sand_Canyon_3_E6: "Galbo",
Sand_Canyon_3_E7: "Propeller",
Sand_Canyon_3_E8: "Sasuke",
Sand_Canyon_3_E9: "Wapod",
Sand_Canyon_3_E10: "Bobo",
Sand_Canyon_3_E11: "Babut",
Sand_Canyon_3_E12: "Magoo",
Sand_Canyon_4_E1: "Popon Ball",
Sand_Canyon_4_E2: "Mariel",
Sand_Canyon_4_E3: "Chilly",
Sand_Canyon_4_E4: "Tick",
Sand_Canyon_4_E5: "Bronto Burt",
Sand_Canyon_4_E6: "Babut",
Sand_Canyon_4_E7: "Bobin",
Sand_Canyon_4_E8: "Joe",
Sand_Canyon_4_E9: "Mony",
Sand_Canyon_4_E10: "Blipper",
Sand_Canyon_4_E11: "Togezo",
Sand_Canyon_4_E12: "Rocky",
Sand_Canyon_4_E13: "Bobo",
Sand_Canyon_5_E1: "Wapod",
Sand_Canyon_5_E2: "Dogon",
Sand_Canyon_5_E3: "Tick",
Sand_Canyon_5_E4: "Rocky",
Sand_Canyon_5_E5: "Bobo",
Sand_Canyon_5_E6: "Chilly",
Sand_Canyon_5_E7: "Sparky",
Sand_Canyon_5_E8: "Togezo",
Sand_Canyon_5_E9: "Bronto Burt",
Sand_Canyon_5_E10: "Sasuke",
Sand_Canyon_5_E11: "Oro",
Sand_Canyon_5_E12: "Galbo",
Sand_Canyon_5_E13: "Nidoo",
Sand_Canyon_5_E14: "Propeller",
Sand_Canyon_5_E15: "Sir Kibble",
Sand_Canyon_5_E16: "KeKe",
Sand_Canyon_5_E17: "Kabu",
Sand_Canyon_6_E1: "Sparky",
Sand_Canyon_6_E2: "Doka",
Sand_Canyon_6_E3: "Cappy",
Sand_Canyon_6_E4: "Pteran",
Sand_Canyon_6_E5: "Bukiset (Parasol)",
Sand_Canyon_6_E6: "Bukiset (Cutter)",
Sand_Canyon_6_E7: "Bukiset (Clean)",
Sand_Canyon_6_E8: "Bukiset (Spark)",
Sand_Canyon_6_E9: "Bukiset (Ice)",
Sand_Canyon_6_E10: "Bukiset (Needle)",
Sand_Canyon_6_E11: "Bukiset (Burning)",
Sand_Canyon_6_E12: "Bukiset (Stone)",
Sand_Canyon_6_E13: "Nidoo",
Cloudy_Park_1_E1: "Waddle Dee",
Cloudy_Park_1_E2: "KeKe",
Cloudy_Park_1_E3: "Cappy",
Cloudy_Park_1_E4: "Yaban",
Cloudy_Park_1_E5: "Togezo",
Cloudy_Park_1_E6: "Galbo",
Cloudy_Park_1_E7: "Sparky",
Cloudy_Park_1_E8: "Como",
Cloudy_Park_1_E9: "Bronto Burt",
Cloudy_Park_1_E10: "Gabon",
Cloudy_Park_1_E11: "Sir Kibble",
Cloudy_Park_1_E12: "Mariel",
Cloudy_Park_1_E13: "Nruff",
Cloudy_Park_2_E1: "Chilly",
Cloudy_Park_2_E2: "Sasuke",
Cloudy_Park_2_E3: "Waddle Dee",
Cloudy_Park_2_E4: "Sparky",
Cloudy_Park_2_E5: "Broom Hatter",
Cloudy_Park_2_E6: "Sir Kibble",
Cloudy_Park_2_E7: "Pteran",
Cloudy_Park_2_E8: "Propeller",
Cloudy_Park_2_E9: "Dogon",
Cloudy_Park_2_E10: "Togezo",
Cloudy_Park_2_E11: "Oro",
Cloudy_Park_2_E12: "Bronto Burt",
Cloudy_Park_2_E13: "Rocky",
Cloudy_Park_2_E14: "Galbo",
Cloudy_Park_2_E15: "Kapar",
Cloudy_Park_3_E1: "Bronto Burt",
Cloudy_Park_3_E2: "Mopoo",
Cloudy_Park_3_E3: "Poppy Bros Jr.",
Cloudy_Park_3_E4: "Como",
Cloudy_Park_3_E5: "Glunk",
Cloudy_Park_3_E6: "Bobin",
Cloudy_Park_3_E7: "Loud",
Cloudy_Park_3_E8: "Kapar",
Cloudy_Park_3_E9: "Galbo",
Cloudy_Park_3_E10: "Batamon",
Cloudy_Park_3_E11: "Bouncy",
Cloudy_Park_4_E1: "Gabon",
Cloudy_Park_4_E2: "Como",
Cloudy_Park_4_E3: "Wapod",
Cloudy_Park_4_E4: "Cappy",
Cloudy_Park_4_E5: "Sparky",
Cloudy_Park_4_E6: "Togezo",
Cloudy_Park_4_E7: "Bronto Burt",
Cloudy_Park_4_E8: "KeKe",
Cloudy_Park_4_E9: "Bouncy",
Cloudy_Park_4_E10: "Sir Kibble",
Cloudy_Park_4_E11: "Mariel",
Cloudy_Park_4_E12: "Kabu",
Cloudy_Park_4_E13: "Wappa",
Cloudy_Park_5_E1: "Yaban",
Cloudy_Park_5_E2: "Sir Kibble",
Cloudy_Park_5_E3: "Cappy",
Cloudy_Park_5_E4: "Wappa",
Cloudy_Park_5_E5: "Galbo",
Cloudy_Park_5_E6: "Bronto Burt",
Cloudy_Park_5_E7: "KeKe",
Cloudy_Park_5_E8: "Propeller",
Cloudy_Park_5_E9: "Klinko",
Cloudy_Park_5_E10: "Wapod",
Cloudy_Park_5_E11: "Pteran",
Cloudy_Park_6_E1: "Madoo",
Cloudy_Park_6_E2: "Tick",
Cloudy_Park_6_E3: "Como",
Cloudy_Park_6_E4: "Waddle Dee Drawing",
Cloudy_Park_6_E5: "Bronto Burt Drawing",
Cloudy_Park_6_E6: "Bouncy Drawing",
Cloudy_Park_6_E7: "Propeller",
Cloudy_Park_6_E8: "Mopoo",
Cloudy_Park_6_E9: "Bukiset (Burning)",
Cloudy_Park_6_E10: "Bukiset (Ice)",
Cloudy_Park_6_E11: "Bukiset (Needle)",
Cloudy_Park_6_E12: "Bukiset (Clean)",
Cloudy_Park_6_E13: "Bukiset (Cutter)",
Iceberg_1_E1: "Waddle Dee",
Iceberg_1_E2: "Klinko",
Iceberg_1_E3: "KeKe",
Iceberg_1_E4: "Como",
Iceberg_1_E5: "Galbo",
Iceberg_1_E6: "Rocky",
Iceberg_1_E7: "Kapar",
Iceberg_1_E8: "Mopoo",
Iceberg_1_E9: "Babut",
Iceberg_1_E10: "Wappa",
Iceberg_1_E11: "Bronto Burt",
Iceberg_1_E12: "Chilly",
Iceberg_1_E13: "Poppy Bros Jr.",
Iceberg_2_E1: "Gabon",
Iceberg_2_E2: "Nruff",
Iceberg_2_E3: "Waddle Dee",
Iceberg_2_E4: "Chilly",
Iceberg_2_E5: "Pteran",
Iceberg_2_E6: "Glunk",
Iceberg_2_E7: "Galbo",
Iceberg_2_E8: "Babut",
Iceberg_2_E9: "Magoo",
Iceberg_2_E10: "Propeller",
Iceberg_2_E11: "Nidoo",
Iceberg_2_E12: "Oro",
Iceberg_2_E13: "Klinko",
Iceberg_2_E14: "Bronto Burt",
Iceberg_3_E1: "Corori",
Iceberg_3_E2: "Bouncy",
Iceberg_3_E3: "Chilly",
Iceberg_3_E4: "Pteran",
Iceberg_3_E5: "Raft Waddle Dee",
Iceberg_3_E6: "Kapar",
Iceberg_3_E7: "Blipper",
Iceberg_3_E8: "Wapod",
Iceberg_3_E9: "Glunk",
Iceberg_3_E10: "Icicle",
Iceberg_4_E1: "Bronto Burt",
Iceberg_4_E2: "Galbo",
Iceberg_4_E3: "Klinko",
Iceberg_4_E4: "Chilly",
Iceberg_4_E5: "Babut",
Iceberg_4_E6: "Wappa",
Iceberg_4_E7: "Icicle",
Iceberg_4_E8: "Corori",
Iceberg_4_E9: "Gabon",
Iceberg_4_E10: "Kabu",
Iceberg_4_E11: "Broom Hatter",
Iceberg_4_E12: "Sasuke",
Iceberg_4_E13: "Nruff",
Iceberg_5_E1: "Bukiset (Burning)",
Iceberg_5_E2: "Bukiset (Stone)",
Iceberg_5_E3: "Bukiset (Ice)",
Iceberg_5_E4: "Bukiset (Needle)",
Iceberg_5_E5: "Bukiset (Clean)",
Iceberg_5_E6: "Bukiset (Parasol)",
Iceberg_5_E7: "Bukiset (Spark)",
Iceberg_5_E8: "Bukiset (Cutter)",
Iceberg_5_E9: "Glunk",
Iceberg_5_E10: "Wapod",
Iceberg_5_E11: "Tick",
Iceberg_5_E12: "Madoo",
Iceberg_5_E13: "Yaban",
Iceberg_5_E14: "Propeller",
Iceberg_5_E15: "Mariel",
Iceberg_5_E16: "Pteran",
Iceberg_5_E17: "Galbo",
Iceberg_5_E18: "KeKe",
Iceberg_5_E19: "Nidoo",
Iceberg_5_E20: "Waddle Dee Drawing",
Iceberg_5_E21: "Bronto Burt Drawing",
Iceberg_5_E22: "Bouncy Drawing",
Iceberg_5_E23: "Joe",
Iceberg_5_E24: "Kapar",
Iceberg_5_E25: "Gansan",
Iceberg_5_E26: "Sasuke",
Iceberg_5_E27: "Togezo",
Iceberg_5_E28: "Sparky",
Iceberg_5_E29: "Bobin",
Iceberg_5_E30: "Chilly",
Iceberg_5_E31: "Peran",
Iceberg_6_E1: "Nruff",
Iceberg_6_E2: "Nidoo",
Iceberg_6_E3: "Sparky",
Iceberg_6_E4: "Sir Kibble",
Grass_Land_4_M1: "Boboo",
Ripple_Field_4_M1: "Captain Stitch",
Sand_Canyon_4_M1: "Haboki",
Cloudy_Park_4_M1: "Jumper Shoot",
Iceberg_4_M1: "Yuki",
Iceberg_6_M1: "Blocky",
Iceberg_6_M2: "Jumper Shoot",
Iceberg_6_M3: "Yuki",
Iceberg_6_M4: "Haboki",
Iceberg_6_M5: "Boboo",
Iceberg_6_M6: "Captain Stitch",
}
vanilla_enemies = {'Waddle Dee': 'No Ability',
'Bronto Burt': 'No Ability',
'Rocky': 'Stone Ability',
'Bobo': 'Burning Ability',
'Chilly': 'Ice Ability',
'Poppy Bros Jr.': 'No Ability',
'Sparky': 'Spark Ability',
'Polof': 'No Ability',
'Broom Hatter': 'Clean Ability',
'Cappy': 'No Ability',
'Bouncy': 'No Ability',
'Nruff': 'No Ability',
'Glunk': 'No Ability',
'Togezo': 'Needle Ability',
'Kabu': 'No Ability',
'Mony': 'No Ability',
'Blipper': 'No Ability',
'Squishy': 'No Ability',
'Gabon': 'No Ability',
'Oro': 'No Ability',
'Galbo': 'Burning Ability',
'Sir Kibble': 'Cutter Ability',
'Nidoo': 'No Ability',
'Kany': 'No Ability',
'Sasuke': 'Parasol Ability',
'Yaban': 'No Ability',
'Boten': 'Needle Ability',
'Coconut': 'No Ability',
'Doka': 'No Ability',
'Icicle': 'No Ability',
'Pteran': 'No Ability',
'Loud': 'No Ability',
'Como': 'No Ability',
'Klinko': 'Parasol Ability',
'Babut': 'No Ability',
'Wappa': 'Ice Ability',
'Mariel': 'No Ability',
'Tick': 'Needle Ability',
'Apolo': 'No Ability',
'Popon Ball': 'No Ability',
'KeKe': 'Clean Ability',
'Magoo': 'Burning Ability',
'Raft Waddle Dee': 'No Ability',
'Madoo': 'No Ability',
'Corori': 'No Ability',
'Kapar': 'Cutter Ability',
'Batamon': 'No Ability',
'Peran': 'No Ability',
'Bobin': 'Spark Ability',
'Mopoo': 'No Ability',
'Gansan': 'Stone Ability',
'Bukiset (Burning)': 'Burning Ability',
'Bukiset (Stone)': 'Stone Ability',
'Bukiset (Ice)': 'Ice Ability',
'Bukiset (Needle)': 'Needle Ability',
'Bukiset (Clean)': 'Clean Ability',
'Bukiset (Parasol)': 'Parasol Ability',
'Bukiset (Spark)': 'Spark Ability',
'Bukiset (Cutter)': 'Cutter Ability',
'Waddle Dee Drawing': 'No Ability',
'Bronto Burt Drawing': 'No Ability',
'Bouncy Drawing': 'No Ability',
'Kabu (Dekabu)': 'No Ability',
'Wapod': 'No Ability',
'Propeller': 'No Ability',
'Dogon': 'No Ability',
'Joe': 'No Ability',
'Captain Stitch': 'Needle Ability',
'Yuki': 'Ice Ability',
'Blocky': 'Stone Ability',
'Jumper Shoot': 'Parasol Ability',
'Boboo': 'Burning Ability',
'Haboki': 'Clean Ability',
}
enemy_restrictive: List[Tuple[List[str], List[str]]] = [
# abilities, enemies, set_all (False to set any)
(["Burning Ability", "Stone Ability"], ["Rocky", "Sparky", "Babut", "Squishy", ]), # Ribbon Field 5 - 7
# Sand Canyon 6
(["Parasol Ability", "Cutter Ability"], ['Bukiset (Parasol)', 'Bukiset (Cutter)']),
(["Spark Ability", "Clean Ability"], ['Bukiset (Spark)', 'Bukiset (Clean)']),
(["Ice Ability", "Needle Ability"], ['Bukiset (Ice)', 'Bukiset (Needle)']),
(["Stone Ability", "Burning Ability"], ['Bukiset (Stone)', 'Bukiset (Burning)']),
(["Stone Ability"], ['Bukiset (Burning)', 'Bukiset (Stone)', 'Bukiset (Ice)', 'Bukiset (Needle)',
'Bukiset (Clean)', 'Bukiset (Spark)', 'Bukiset (Parasol)', 'Bukiset (Cutter)']),
(["Parasol Ability"], ['Bukiset (Burning)', 'Bukiset (Stone)', 'Bukiset (Ice)', 'Bukiset (Needle)',
'Bukiset (Clean)', 'Bukiset (Spark)', 'Bukiset (Parasol)', 'Bukiset (Cutter)']),
]

View File

@@ -0,0 +1,928 @@
# Level 1
grass_land_1 = "Grass Land 1 - Complete"
grass_land_2 = "Grass Land 2 - Complete"
grass_land_3 = "Grass Land 3 - Complete"
grass_land_4 = "Grass Land 4 - Complete"
grass_land_5 = "Grass Land 5 - Complete"
grass_land_6 = "Grass Land 6 - Complete"
grass_land_tulip = "Grass Land 1 - Tulip"
grass_land_muchi = "Grass Land 2 - Muchimuchi"
grass_land_pitcherman = "Grass Land 3 - Pitcherman"
grass_land_chao = "Grass Land 4 - Chao & Goku"
grass_land_mine = "Grass Land 5 - Mine"
grass_land_pierre = "Grass Land 6 - Pierre"
grass_land_whispy = "Grass Land - Boss (Whispy Woods) Purified"
# Level 2
ripple_field_1 = "Ripple Field 1 - Complete"
ripple_field_2 = "Ripple Field 2 - Complete"
ripple_field_3 = "Ripple Field 3 - Complete"
ripple_field_4 = "Ripple Field 4 - Complete"
ripple_field_5 = "Ripple Field 5 - Complete"
ripple_field_6 = "Ripple Field 6 - Complete"
ripple_field_kamuribana = "Ripple Field 1 - Kamuribana"
ripple_field_bakasa = "Ripple Field 2 - Bakasa"
ripple_field_elieel = "Ripple Field 3 - Elieel"
ripple_field_toad = "Ripple Field 4 - Toad & Little Toad"
ripple_field_mama_pitch = "Ripple Field 5 - Mama Pitch"
ripple_field_hb002 = "Ripple Field 6 - HB-002"
ripple_field_acro = "Ripple Field - Boss (Acro) Purified"
# Level 3
sand_canyon_1 = "Sand Canyon 1 - Complete"
sand_canyon_2 = "Sand Canyon 2 - Complete"
sand_canyon_3 = "Sand Canyon 3 - Complete"
sand_canyon_4 = "Sand Canyon 4 - Complete"
sand_canyon_5 = "Sand Canyon 5 - Complete"
sand_canyon_6 = "Sand Canyon 6 - Complete"
sand_canyon_mushrooms = "Sand Canyon 1 - Geromuzudake"
sand_canyon_auntie = "Sand Canyon 2 - Auntie"
sand_canyon_caramello = "Sand Canyon 3 - Caramello"
sand_canyon_hikari = "Sand Canyon 4 - Donbe & Hikari"
sand_canyon_nyupun = "Sand Canyon 5 - Nyupun"
sand_canyon_rob = "Sand Canyon 6 - Professor Hector & R.O.B"
sand_canyon_poncon = "Sand Canyon - Boss (Pon & Con) Purified"
# Level 4
cloudy_park_1 = "Cloudy Park 1 - Complete"
cloudy_park_2 = "Cloudy Park 2 - Complete"
cloudy_park_3 = "Cloudy Park 3 - Complete"
cloudy_park_4 = "Cloudy Park 4 - Complete"
cloudy_park_5 = "Cloudy Park 5 - Complete"
cloudy_park_6 = "Cloudy Park 6 - Complete"
cloudy_park_hibanamodoki = "Cloudy Park 1 - Hibanamodoki"
cloudy_park_piyokeko = "Cloudy Park 2 - Piyo & Keko"
cloudy_park_mrball = "Cloudy Park 3 - Mr. Ball"
cloudy_park_mikarin = "Cloudy Park 4 - Mikarin & Kagami Mocchi"
cloudy_park_pick = "Cloudy Park 5 - Pick"
cloudy_park_hb007 = "Cloudy Park 6 - HB-007"
cloudy_park_ado = "Cloudy Park - Boss (Ado) Purified"
# Level 5
iceberg_1 = "Iceberg 1 - Complete"
iceberg_2 = "Iceberg 2 - Complete"
iceberg_3 = "Iceberg 3 - Complete"
iceberg_4 = "Iceberg 4 - Complete"
iceberg_5 = "Iceberg 5 - Complete"
iceberg_6 = "Iceberg 6 - Complete"
iceberg_kogoesou = "Iceberg 1 - Kogoesou"
iceberg_samus = "Iceberg 2 - Samus"
iceberg_kawasaki = "Iceberg 3 - Chef Kawasaki"
iceberg_name = "Iceberg 4 - Name"
iceberg_shiro = "Iceberg 5 - Shiro"
iceberg_angel = "Iceberg 6 - Angel"
iceberg_dedede = "Iceberg - Boss (Dedede) Purified"
# Level 6
hyper_zone = "Hyper Zone - Zero"
# Extras
boss_butch = "Boss Butch"
mg5_p = "Minigame 5 - Perfect"
jumping_clear = "Jumping - Target Score Reached"
# 1-Ups
grass_land_1_u1 = "Grass Land 1 - 1-Up (Parasol)" # Parasol
grass_land_2_u1 = "Grass Land 2 - 1-Up (Needle)" # Needle
grass_land_3_u1 = "Grass Land 3 - 1-Up (Climb)" # None
grass_land_4_u1 = "Grass Land 4 - 1-Up (Gordo)" # None
grass_land_6_u1 = "Grass Land 6 - 1-Up (Tower)" # None
grass_land_6_u2 = "Grass Land 6 - 1-Up (Falling)" # None
ripple_field_2_u1 = "Ripple Field 2 - 1-Up (Currents)" # Kine
ripple_field_3_u1 = "Ripple Field 3 - 1-Up (Cutter/Spark)" # Cutter or Spark
ripple_field_4_u1 = "Ripple Field 4 - 1-Up (Stone)" # Stone
ripple_field_5_u1 = "Ripple Field 5 - 1-Up (Currents)" # Kine, Burning, Stone
sand_canyon_1_u1 = "Sand Canyon 1 - 1-Up (Polof)" # None
sand_canyon_2_u1 = "Sand Canyon 2 - 1-Up (Enclave)" # None
sand_canyon_4_u1 = "Sand Canyon 4 - 1-Up (Clean)" # Clean
sand_canyon_5_u1 = "Sand Canyon 5 - 1-Up (Falling Block)" # None
sand_canyon_5_u2 = "Sand Canyon 5 - 1-Up (Ice 1)" # Ice
sand_canyon_5_u3 = "Sand Canyon 5 - 1-Up (Ice 2)" # Ice
sand_canyon_5_u4 = "Sand Canyon 5 - 1-Up (Ice 3)" # Ice
cloudy_park_1_u1 = "Cloudy Park 1 - 1-Up (Shotzo)" # None
cloudy_park_4_u1 = "Cloudy Park 4 - 1-Up (Windy)" # Coo
cloudy_park_6_u1 = "Cloudy Park 6 - 1-Up (Cutter)" # Cutter
iceberg_5_u1 = "Iceberg 5 - 1-Up (Boulder)" # None
iceberg_5_u2 = "Iceberg 5 - 1-Up (Floor)" # None
iceberg_5_u3 = "Iceberg 5 - 1-Up (Peloo)" # None, just let yourself get eaten by the Peloo
iceberg_6_u1 = "Iceberg 6 - 1-Up (Middle)" # None
# Maxim Tomatoes
grass_land_1_m1 = "Grass Land 1 - Maxim Tomato (Spark)" # Spark
grass_land_3_m1 = "Grass Land 3 - Maxim Tomato (Climb)" # None
grass_land_4_m1 = "Grass Land 4 - Maxim Tomato (Zebon Right)" # None
grass_land_4_m2 = "Grass Land 4 - Maxim Tomato (Gordo)" # None
grass_land_4_m3 = "Grass Land 4 - Maxim Tomato (Cliff)" # None
ripple_field_2_m1 = "Ripple Field 2 - Maxim Tomato (Currents)" # Kine
ripple_field_3_m1 = "Ripple Field 3 - Maxim Tomato (Cove)" # None
ripple_field_4_m1 = "Ripple Field 4 - Maxim Tomato (Dark)" # None (maybe Spark?)
ripple_field_4_m2 = "Ripple Field 4 - Maxim Tomato (Stone)" # Stone
ripple_field_5_m1 = "Ripple Field 5 - Maxim Tomato (Exit)" # Kine
ripple_field_5_m2 = "Ripple Field 5 - Maxim Tomato (Currents)" # Kine, Burning, Stone
sand_canyon_2_m1 = "Sand Canyon 2 - Maxim Tomato (Underwater)" # None
sand_canyon_4_m1 = "Sand Canyon 4 - Maxim Tomato (Pacto)" # None
sand_canyon_4_m2 = "Sand Canyon 4 - Maxim Tomato (Needle)" # Needle
sand_canyon_5_m1 = "Sand Canyon 5 - Maxim Tomato (Pit)" # None
cloudy_park_1_m1 = "Cloudy Park 1 - Maxim Tomato (Mariel)" # None
cloudy_park_4_m1 = "Cloudy Park 4 - Maxim Tomato (Windy)" # Coo
cloudy_park_5_m1 = "Cloudy Park 5 - Maxim Tomato (Pillars)" # None
iceberg_3_m1 = "Iceberg 3 - Maxim Tomato (Ceiling)" # None
iceberg_6_m1 = "Iceberg 6 - Maxim Tomato (Left)" # None
# Level Names
level_names = {
"Grass Land": 1,
"Ripple Field": 2,
"Sand Canyon": 3,
"Cloudy Park": 4,
"Iceberg": 5,
}
level_names_inverse = {
level_names[level]: level for level in level_names
}
# Boss Names
boss_names = {
"Whispy Woods": 0x770200,
"Acro": 0x770201,
"Pon & Con": 0x770202,
"Ado": 0x770203,
"King Dedede": 0x770204
}
# Goal Mapping
goals = {
0: hyper_zone,
1: boss_butch,
2: mg5_p,
3: jumping_clear
}
grass_land_1_s1 = "Grass Land 1 - Star 1"
grass_land_1_s2 = "Grass Land 1 - Star 2"
grass_land_1_s3 = "Grass Land 1 - Star 3"
grass_land_1_s4 = "Grass Land 1 - Star 4"
grass_land_1_s5 = "Grass Land 1 - Star 5"
grass_land_1_s6 = "Grass Land 1 - Star 6"
grass_land_1_s7 = "Grass Land 1 - Star 7"
grass_land_1_s8 = "Grass Land 1 - Star 8"
grass_land_1_s9 = "Grass Land 1 - Star 9"
grass_land_1_s10 = "Grass Land 1 - Star 10"
grass_land_1_s11 = "Grass Land 1 - Star 11"
grass_land_1_s12 = "Grass Land 1 - Star 12"
grass_land_1_s13 = "Grass Land 1 - Star 13"
grass_land_1_s14 = "Grass Land 1 - Star 14"
grass_land_1_s15 = "Grass Land 1 - Star 15"
grass_land_1_s16 = "Grass Land 1 - Star 16"
grass_land_1_s17 = "Grass Land 1 - Star 17"
grass_land_1_s18 = "Grass Land 1 - Star 18"
grass_land_1_s19 = "Grass Land 1 - Star 19"
grass_land_1_s20 = "Grass Land 1 - Star 20"
grass_land_1_s21 = "Grass Land 1 - Star 21"
grass_land_1_s22 = "Grass Land 1 - Star 22"
grass_land_1_s23 = "Grass Land 1 - Star 23"
grass_land_2_s1 = "Grass Land 2 - Star 1"
grass_land_2_s2 = "Grass Land 2 - Star 2"
grass_land_2_s3 = "Grass Land 2 - Star 3"
grass_land_2_s4 = "Grass Land 2 - Star 4"
grass_land_2_s5 = "Grass Land 2 - Star 5"
grass_land_2_s6 = "Grass Land 2 - Star 6"
grass_land_2_s7 = "Grass Land 2 - Star 7"
grass_land_2_s8 = "Grass Land 2 - Star 8"
grass_land_2_s9 = "Grass Land 2 - Star 9"
grass_land_2_s10 = "Grass Land 2 - Star 10"
grass_land_2_s11 = "Grass Land 2 - Star 11"
grass_land_2_s12 = "Grass Land 2 - Star 12"
grass_land_2_s13 = "Grass Land 2 - Star 13"
grass_land_2_s14 = "Grass Land 2 - Star 14"
grass_land_2_s15 = "Grass Land 2 - Star 15"
grass_land_2_s16 = "Grass Land 2 - Star 16"
grass_land_2_s17 = "Grass Land 2 - Star 17"
grass_land_2_s18 = "Grass Land 2 - Star 18"
grass_land_2_s19 = "Grass Land 2 - Star 19"
grass_land_2_s20 = "Grass Land 2 - Star 20"
grass_land_2_s21 = "Grass Land 2 - Star 21"
grass_land_3_s1 = "Grass Land 3 - Star 1"
grass_land_3_s2 = "Grass Land 3 - Star 2"
grass_land_3_s3 = "Grass Land 3 - Star 3"
grass_land_3_s4 = "Grass Land 3 - Star 4"
grass_land_3_s5 = "Grass Land 3 - Star 5"
grass_land_3_s6 = "Grass Land 3 - Star 6"
grass_land_3_s7 = "Grass Land 3 - Star 7"
grass_land_3_s8 = "Grass Land 3 - Star 8"
grass_land_3_s9 = "Grass Land 3 - Star 9"
grass_land_3_s10 = "Grass Land 3 - Star 10"
grass_land_3_s11 = "Grass Land 3 - Star 11"
grass_land_3_s12 = "Grass Land 3 - Star 12"
grass_land_3_s13 = "Grass Land 3 - Star 13"
grass_land_3_s14 = "Grass Land 3 - Star 14"
grass_land_3_s15 = "Grass Land 3 - Star 15"
grass_land_3_s16 = "Grass Land 3 - Star 16"
grass_land_3_s17 = "Grass Land 3 - Star 17"
grass_land_3_s18 = "Grass Land 3 - Star 18"
grass_land_3_s19 = "Grass Land 3 - Star 19"
grass_land_3_s20 = "Grass Land 3 - Star 20"
grass_land_3_s21 = "Grass Land 3 - Star 21"
grass_land_3_s22 = "Grass Land 3 - Star 22"
grass_land_3_s23 = "Grass Land 3 - Star 23"
grass_land_3_s24 = "Grass Land 3 - Star 24"
grass_land_3_s25 = "Grass Land 3 - Star 25"
grass_land_3_s26 = "Grass Land 3 - Star 26"
grass_land_3_s27 = "Grass Land 3 - Star 27"
grass_land_3_s28 = "Grass Land 3 - Star 28"
grass_land_3_s29 = "Grass Land 3 - Star 29"
grass_land_3_s30 = "Grass Land 3 - Star 30"
grass_land_3_s31 = "Grass Land 3 - Star 31"
grass_land_4_s1 = "Grass Land 4 - Star 1"
grass_land_4_s2 = "Grass Land 4 - Star 2"
grass_land_4_s3 = "Grass Land 4 - Star 3"
grass_land_4_s4 = "Grass Land 4 - Star 4"
grass_land_4_s5 = "Grass Land 4 - Star 5"
grass_land_4_s6 = "Grass Land 4 - Star 6"
grass_land_4_s7 = "Grass Land 4 - Star 7"
grass_land_4_s8 = "Grass Land 4 - Star 8"
grass_land_4_s9 = "Grass Land 4 - Star 9"
grass_land_4_s10 = "Grass Land 4 - Star 10"
grass_land_4_s11 = "Grass Land 4 - Star 11"
grass_land_4_s12 = "Grass Land 4 - Star 12"
grass_land_4_s13 = "Grass Land 4 - Star 13"
grass_land_4_s14 = "Grass Land 4 - Star 14"
grass_land_4_s15 = "Grass Land 4 - Star 15"
grass_land_4_s16 = "Grass Land 4 - Star 16"
grass_land_4_s17 = "Grass Land 4 - Star 17"
grass_land_4_s18 = "Grass Land 4 - Star 18"
grass_land_4_s19 = "Grass Land 4 - Star 19"
grass_land_4_s20 = "Grass Land 4 - Star 20"
grass_land_4_s21 = "Grass Land 4 - Star 21"
grass_land_4_s22 = "Grass Land 4 - Star 22"
grass_land_4_s23 = "Grass Land 4 - Star 23"
grass_land_4_s24 = "Grass Land 4 - Star 24"
grass_land_4_s25 = "Grass Land 4 - Star 25"
grass_land_4_s26 = "Grass Land 4 - Star 26"
grass_land_4_s27 = "Grass Land 4 - Star 27"
grass_land_4_s28 = "Grass Land 4 - Star 28"
grass_land_4_s29 = "Grass Land 4 - Star 29"
grass_land_4_s30 = "Grass Land 4 - Star 30"
grass_land_4_s31 = "Grass Land 4 - Star 31"
grass_land_4_s32 = "Grass Land 4 - Star 32"
grass_land_4_s33 = "Grass Land 4 - Star 33"
grass_land_4_s34 = "Grass Land 4 - Star 34"
grass_land_4_s35 = "Grass Land 4 - Star 35"
grass_land_4_s36 = "Grass Land 4 - Star 36"
grass_land_4_s37 = "Grass Land 4 - Star 37"
grass_land_5_s1 = "Grass Land 5 - Star 1"
grass_land_5_s2 = "Grass Land 5 - Star 2"
grass_land_5_s3 = "Grass Land 5 - Star 3"
grass_land_5_s4 = "Grass Land 5 - Star 4"
grass_land_5_s5 = "Grass Land 5 - Star 5"
grass_land_5_s6 = "Grass Land 5 - Star 6"
grass_land_5_s7 = "Grass Land 5 - Star 7"
grass_land_5_s8 = "Grass Land 5 - Star 8"
grass_land_5_s9 = "Grass Land 5 - Star 9"
grass_land_5_s10 = "Grass Land 5 - Star 10"
grass_land_5_s11 = "Grass Land 5 - Star 11"
grass_land_5_s12 = "Grass Land 5 - Star 12"
grass_land_5_s13 = "Grass Land 5 - Star 13"
grass_land_5_s14 = "Grass Land 5 - Star 14"
grass_land_5_s15 = "Grass Land 5 - Star 15"
grass_land_5_s16 = "Grass Land 5 - Star 16"
grass_land_5_s17 = "Grass Land 5 - Star 17"
grass_land_5_s18 = "Grass Land 5 - Star 18"
grass_land_5_s19 = "Grass Land 5 - Star 19"
grass_land_5_s20 = "Grass Land 5 - Star 20"
grass_land_5_s21 = "Grass Land 5 - Star 21"
grass_land_5_s22 = "Grass Land 5 - Star 22"
grass_land_5_s23 = "Grass Land 5 - Star 23"
grass_land_5_s24 = "Grass Land 5 - Star 24"
grass_land_5_s25 = "Grass Land 5 - Star 25"
grass_land_5_s26 = "Grass Land 5 - Star 26"
grass_land_5_s27 = "Grass Land 5 - Star 27"
grass_land_5_s28 = "Grass Land 5 - Star 28"
grass_land_5_s29 = "Grass Land 5 - Star 29"
grass_land_6_s1 = "Grass Land 6 - Star 1"
grass_land_6_s2 = "Grass Land 6 - Star 2"
grass_land_6_s3 = "Grass Land 6 - Star 3"
grass_land_6_s4 = "Grass Land 6 - Star 4"
grass_land_6_s5 = "Grass Land 6 - Star 5"
grass_land_6_s6 = "Grass Land 6 - Star 6"
grass_land_6_s7 = "Grass Land 6 - Star 7"
grass_land_6_s8 = "Grass Land 6 - Star 8"
grass_land_6_s9 = "Grass Land 6 - Star 9"
grass_land_6_s10 = "Grass Land 6 - Star 10"
grass_land_6_s11 = "Grass Land 6 - Star 11"
grass_land_6_s12 = "Grass Land 6 - Star 12"
grass_land_6_s13 = "Grass Land 6 - Star 13"
grass_land_6_s14 = "Grass Land 6 - Star 14"
grass_land_6_s15 = "Grass Land 6 - Star 15"
grass_land_6_s16 = "Grass Land 6 - Star 16"
grass_land_6_s17 = "Grass Land 6 - Star 17"
grass_land_6_s18 = "Grass Land 6 - Star 18"
grass_land_6_s19 = "Grass Land 6 - Star 19"
grass_land_6_s20 = "Grass Land 6 - Star 20"
grass_land_6_s21 = "Grass Land 6 - Star 21"
grass_land_6_s22 = "Grass Land 6 - Star 22"
grass_land_6_s23 = "Grass Land 6 - Star 23"
grass_land_6_s24 = "Grass Land 6 - Star 24"
grass_land_6_s25 = "Grass Land 6 - Star 25"
grass_land_6_s26 = "Grass Land 6 - Star 26"
grass_land_6_s27 = "Grass Land 6 - Star 27"
grass_land_6_s28 = "Grass Land 6 - Star 28"
grass_land_6_s29 = "Grass Land 6 - Star 29"
ripple_field_1_s1 = "Ripple Field 1 - Star 1"
ripple_field_1_s2 = "Ripple Field 1 - Star 2"
ripple_field_1_s3 = "Ripple Field 1 - Star 3"
ripple_field_1_s4 = "Ripple Field 1 - Star 4"
ripple_field_1_s5 = "Ripple Field 1 - Star 5"
ripple_field_1_s6 = "Ripple Field 1 - Star 6"
ripple_field_1_s7 = "Ripple Field 1 - Star 7"
ripple_field_1_s8 = "Ripple Field 1 - Star 8"
ripple_field_1_s9 = "Ripple Field 1 - Star 9"
ripple_field_1_s10 = "Ripple Field 1 - Star 10"
ripple_field_1_s11 = "Ripple Field 1 - Star 11"
ripple_field_1_s12 = "Ripple Field 1 - Star 12"
ripple_field_1_s13 = "Ripple Field 1 - Star 13"
ripple_field_1_s14 = "Ripple Field 1 - Star 14"
ripple_field_1_s15 = "Ripple Field 1 - Star 15"
ripple_field_1_s16 = "Ripple Field 1 - Star 16"
ripple_field_1_s17 = "Ripple Field 1 - Star 17"
ripple_field_1_s18 = "Ripple Field 1 - Star 18"
ripple_field_1_s19 = "Ripple Field 1 - Star 19"
ripple_field_2_s1 = "Ripple Field 2 - Star 1"
ripple_field_2_s2 = "Ripple Field 2 - Star 2"
ripple_field_2_s3 = "Ripple Field 2 - Star 3"
ripple_field_2_s4 = "Ripple Field 2 - Star 4"
ripple_field_2_s5 = "Ripple Field 2 - Star 5"
ripple_field_2_s6 = "Ripple Field 2 - Star 6"
ripple_field_2_s7 = "Ripple Field 2 - Star 7"
ripple_field_2_s8 = "Ripple Field 2 - Star 8"
ripple_field_2_s9 = "Ripple Field 2 - Star 9"
ripple_field_2_s10 = "Ripple Field 2 - Star 10"
ripple_field_2_s11 = "Ripple Field 2 - Star 11"
ripple_field_2_s12 = "Ripple Field 2 - Star 12"
ripple_field_2_s13 = "Ripple Field 2 - Star 13"
ripple_field_2_s14 = "Ripple Field 2 - Star 14"
ripple_field_2_s15 = "Ripple Field 2 - Star 15"
ripple_field_2_s16 = "Ripple Field 2 - Star 16"
ripple_field_2_s17 = "Ripple Field 2 - Star 17"
ripple_field_3_s1 = "Ripple Field 3 - Star 1"
ripple_field_3_s2 = "Ripple Field 3 - Star 2"
ripple_field_3_s3 = "Ripple Field 3 - Star 3"
ripple_field_3_s4 = "Ripple Field 3 - Star 4"
ripple_field_3_s5 = "Ripple Field 3 - Star 5"
ripple_field_3_s6 = "Ripple Field 3 - Star 6"
ripple_field_3_s7 = "Ripple Field 3 - Star 7"
ripple_field_3_s8 = "Ripple Field 3 - Star 8"
ripple_field_3_s9 = "Ripple Field 3 - Star 9"
ripple_field_3_s10 = "Ripple Field 3 - Star 10"
ripple_field_3_s11 = "Ripple Field 3 - Star 11"
ripple_field_3_s12 = "Ripple Field 3 - Star 12"
ripple_field_3_s13 = "Ripple Field 3 - Star 13"
ripple_field_3_s14 = "Ripple Field 3 - Star 14"
ripple_field_3_s15 = "Ripple Field 3 - Star 15"
ripple_field_3_s16 = "Ripple Field 3 - Star 16"
ripple_field_3_s17 = "Ripple Field 3 - Star 17"
ripple_field_3_s18 = "Ripple Field 3 - Star 18"
ripple_field_3_s19 = "Ripple Field 3 - Star 19"
ripple_field_3_s20 = "Ripple Field 3 - Star 20"
ripple_field_3_s21 = "Ripple Field 3 - Star 21"
ripple_field_4_s1 = "Ripple Field 4 - Star 1"
ripple_field_4_s2 = "Ripple Field 4 - Star 2"
ripple_field_4_s3 = "Ripple Field 4 - Star 3"
ripple_field_4_s4 = "Ripple Field 4 - Star 4"
ripple_field_4_s5 = "Ripple Field 4 - Star 5"
ripple_field_4_s6 = "Ripple Field 4 - Star 6"
ripple_field_4_s7 = "Ripple Field 4 - Star 7"
ripple_field_4_s8 = "Ripple Field 4 - Star 8"
ripple_field_4_s9 = "Ripple Field 4 - Star 9"
ripple_field_4_s10 = "Ripple Field 4 - Star 10"
ripple_field_4_s11 = "Ripple Field 4 - Star 11"
ripple_field_4_s12 = "Ripple Field 4 - Star 12"
ripple_field_4_s13 = "Ripple Field 4 - Star 13"
ripple_field_4_s14 = "Ripple Field 4 - Star 14"
ripple_field_4_s15 = "Ripple Field 4 - Star 15"
ripple_field_4_s16 = "Ripple Field 4 - Star 16"
ripple_field_4_s17 = "Ripple Field 4 - Star 17"
ripple_field_4_s18 = "Ripple Field 4 - Star 18"
ripple_field_4_s19 = "Ripple Field 4 - Star 19"
ripple_field_4_s20 = "Ripple Field 4 - Star 20"
ripple_field_4_s21 = "Ripple Field 4 - Star 21"
ripple_field_4_s22 = "Ripple Field 4 - Star 22"
ripple_field_4_s23 = "Ripple Field 4 - Star 23"
ripple_field_4_s24 = "Ripple Field 4 - Star 24"
ripple_field_4_s25 = "Ripple Field 4 - Star 25"
ripple_field_4_s26 = "Ripple Field 4 - Star 26"
ripple_field_4_s27 = "Ripple Field 4 - Star 27"
ripple_field_4_s28 = "Ripple Field 4 - Star 28"
ripple_field_4_s29 = "Ripple Field 4 - Star 29"
ripple_field_4_s30 = "Ripple Field 4 - Star 30"
ripple_field_4_s31 = "Ripple Field 4 - Star 31"
ripple_field_4_s32 = "Ripple Field 4 - Star 32"
ripple_field_4_s33 = "Ripple Field 4 - Star 33"
ripple_field_4_s34 = "Ripple Field 4 - Star 34"
ripple_field_4_s35 = "Ripple Field 4 - Star 35"
ripple_field_4_s36 = "Ripple Field 4 - Star 36"
ripple_field_4_s37 = "Ripple Field 4 - Star 37"
ripple_field_4_s38 = "Ripple Field 4 - Star 38"
ripple_field_4_s39 = "Ripple Field 4 - Star 39"
ripple_field_4_s40 = "Ripple Field 4 - Star 40"
ripple_field_4_s41 = "Ripple Field 4 - Star 41"
ripple_field_4_s42 = "Ripple Field 4 - Star 42"
ripple_field_4_s43 = "Ripple Field 4 - Star 43"
ripple_field_4_s44 = "Ripple Field 4 - Star 44"
ripple_field_4_s45 = "Ripple Field 4 - Star 45"
ripple_field_4_s46 = "Ripple Field 4 - Star 46"
ripple_field_4_s47 = "Ripple Field 4 - Star 47"
ripple_field_4_s48 = "Ripple Field 4 - Star 48"
ripple_field_4_s49 = "Ripple Field 4 - Star 49"
ripple_field_4_s50 = "Ripple Field 4 - Star 50"
ripple_field_4_s51 = "Ripple Field 4 - Star 51"
ripple_field_5_s1 = "Ripple Field 5 - Star 1"
ripple_field_5_s2 = "Ripple Field 5 - Star 2"
ripple_field_5_s3 = "Ripple Field 5 - Star 3"
ripple_field_5_s4 = "Ripple Field 5 - Star 4"
ripple_field_5_s5 = "Ripple Field 5 - Star 5"
ripple_field_5_s6 = "Ripple Field 5 - Star 6"
ripple_field_5_s7 = "Ripple Field 5 - Star 7"
ripple_field_5_s8 = "Ripple Field 5 - Star 8"
ripple_field_5_s9 = "Ripple Field 5 - Star 9"
ripple_field_5_s10 = "Ripple Field 5 - Star 10"
ripple_field_5_s11 = "Ripple Field 5 - Star 11"
ripple_field_5_s12 = "Ripple Field 5 - Star 12"
ripple_field_5_s13 = "Ripple Field 5 - Star 13"
ripple_field_5_s14 = "Ripple Field 5 - Star 14"
ripple_field_5_s15 = "Ripple Field 5 - Star 15"
ripple_field_5_s16 = "Ripple Field 5 - Star 16"
ripple_field_5_s17 = "Ripple Field 5 - Star 17"
ripple_field_5_s18 = "Ripple Field 5 - Star 18"
ripple_field_5_s19 = "Ripple Field 5 - Star 19"
ripple_field_5_s20 = "Ripple Field 5 - Star 20"
ripple_field_5_s21 = "Ripple Field 5 - Star 21"
ripple_field_5_s22 = "Ripple Field 5 - Star 22"
ripple_field_5_s23 = "Ripple Field 5 - Star 23"
ripple_field_5_s24 = "Ripple Field 5 - Star 24"
ripple_field_5_s25 = "Ripple Field 5 - Star 25"
ripple_field_5_s26 = "Ripple Field 5 - Star 26"
ripple_field_5_s27 = "Ripple Field 5 - Star 27"
ripple_field_5_s28 = "Ripple Field 5 - Star 28"
ripple_field_5_s29 = "Ripple Field 5 - Star 29"
ripple_field_5_s30 = "Ripple Field 5 - Star 30"
ripple_field_5_s31 = "Ripple Field 5 - Star 31"
ripple_field_5_s32 = "Ripple Field 5 - Star 32"
ripple_field_5_s33 = "Ripple Field 5 - Star 33"
ripple_field_5_s34 = "Ripple Field 5 - Star 34"
ripple_field_5_s35 = "Ripple Field 5 - Star 35"
ripple_field_5_s36 = "Ripple Field 5 - Star 36"
ripple_field_5_s37 = "Ripple Field 5 - Star 37"
ripple_field_5_s38 = "Ripple Field 5 - Star 38"
ripple_field_5_s39 = "Ripple Field 5 - Star 39"
ripple_field_5_s40 = "Ripple Field 5 - Star 40"
ripple_field_5_s41 = "Ripple Field 5 - Star 41"
ripple_field_5_s42 = "Ripple Field 5 - Star 42"
ripple_field_5_s43 = "Ripple Field 5 - Star 43"
ripple_field_5_s44 = "Ripple Field 5 - Star 44"
ripple_field_5_s45 = "Ripple Field 5 - Star 45"
ripple_field_5_s46 = "Ripple Field 5 - Star 46"
ripple_field_5_s47 = "Ripple Field 5 - Star 47"
ripple_field_5_s48 = "Ripple Field 5 - Star 48"
ripple_field_5_s49 = "Ripple Field 5 - Star 49"
ripple_field_5_s50 = "Ripple Field 5 - Star 50"
ripple_field_5_s51 = "Ripple Field 5 - Star 51"
ripple_field_6_s1 = "Ripple Field 6 - Star 1"
ripple_field_6_s2 = "Ripple Field 6 - Star 2"
ripple_field_6_s3 = "Ripple Field 6 - Star 3"
ripple_field_6_s4 = "Ripple Field 6 - Star 4"
ripple_field_6_s5 = "Ripple Field 6 - Star 5"
ripple_field_6_s6 = "Ripple Field 6 - Star 6"
ripple_field_6_s7 = "Ripple Field 6 - Star 7"
ripple_field_6_s8 = "Ripple Field 6 - Star 8"
ripple_field_6_s9 = "Ripple Field 6 - Star 9"
ripple_field_6_s10 = "Ripple Field 6 - Star 10"
ripple_field_6_s11 = "Ripple Field 6 - Star 11"
ripple_field_6_s12 = "Ripple Field 6 - Star 12"
ripple_field_6_s13 = "Ripple Field 6 - Star 13"
ripple_field_6_s14 = "Ripple Field 6 - Star 14"
ripple_field_6_s15 = "Ripple Field 6 - Star 15"
ripple_field_6_s16 = "Ripple Field 6 - Star 16"
ripple_field_6_s17 = "Ripple Field 6 - Star 17"
ripple_field_6_s18 = "Ripple Field 6 - Star 18"
ripple_field_6_s19 = "Ripple Field 6 - Star 19"
ripple_field_6_s20 = "Ripple Field 6 - Star 20"
ripple_field_6_s21 = "Ripple Field 6 - Star 21"
ripple_field_6_s22 = "Ripple Field 6 - Star 22"
ripple_field_6_s23 = "Ripple Field 6 - Star 23"
sand_canyon_1_s1 = "Sand Canyon 1 - Star 1"
sand_canyon_1_s2 = "Sand Canyon 1 - Star 2"
sand_canyon_1_s3 = "Sand Canyon 1 - Star 3"
sand_canyon_1_s4 = "Sand Canyon 1 - Star 4"
sand_canyon_1_s5 = "Sand Canyon 1 - Star 5"
sand_canyon_1_s6 = "Sand Canyon 1 - Star 6"
sand_canyon_1_s7 = "Sand Canyon 1 - Star 7"
sand_canyon_1_s8 = "Sand Canyon 1 - Star 8"
sand_canyon_1_s9 = "Sand Canyon 1 - Star 9"
sand_canyon_1_s10 = "Sand Canyon 1 - Star 10"
sand_canyon_1_s11 = "Sand Canyon 1 - Star 11"
sand_canyon_1_s12 = "Sand Canyon 1 - Star 12"
sand_canyon_1_s13 = "Sand Canyon 1 - Star 13"
sand_canyon_1_s14 = "Sand Canyon 1 - Star 14"
sand_canyon_1_s15 = "Sand Canyon 1 - Star 15"
sand_canyon_1_s16 = "Sand Canyon 1 - Star 16"
sand_canyon_1_s17 = "Sand Canyon 1 - Star 17"
sand_canyon_1_s18 = "Sand Canyon 1 - Star 18"
sand_canyon_1_s19 = "Sand Canyon 1 - Star 19"
sand_canyon_1_s20 = "Sand Canyon 1 - Star 20"
sand_canyon_1_s21 = "Sand Canyon 1 - Star 21"
sand_canyon_1_s22 = "Sand Canyon 1 - Star 22"
sand_canyon_2_s1 = "Sand Canyon 2 - Star 1"
sand_canyon_2_s2 = "Sand Canyon 2 - Star 2"
sand_canyon_2_s3 = "Sand Canyon 2 - Star 3"
sand_canyon_2_s4 = "Sand Canyon 2 - Star 4"
sand_canyon_2_s5 = "Sand Canyon 2 - Star 5"
sand_canyon_2_s6 = "Sand Canyon 2 - Star 6"
sand_canyon_2_s7 = "Sand Canyon 2 - Star 7"
sand_canyon_2_s8 = "Sand Canyon 2 - Star 8"
sand_canyon_2_s9 = "Sand Canyon 2 - Star 9"
sand_canyon_2_s10 = "Sand Canyon 2 - Star 10"
sand_canyon_2_s11 = "Sand Canyon 2 - Star 11"
sand_canyon_2_s12 = "Sand Canyon 2 - Star 12"
sand_canyon_2_s13 = "Sand Canyon 2 - Star 13"
sand_canyon_2_s14 = "Sand Canyon 2 - Star 14"
sand_canyon_2_s15 = "Sand Canyon 2 - Star 15"
sand_canyon_2_s16 = "Sand Canyon 2 - Star 16"
sand_canyon_2_s17 = "Sand Canyon 2 - Star 17"
sand_canyon_2_s18 = "Sand Canyon 2 - Star 18"
sand_canyon_2_s19 = "Sand Canyon 2 - Star 19"
sand_canyon_2_s20 = "Sand Canyon 2 - Star 20"
sand_canyon_2_s21 = "Sand Canyon 2 - Star 21"
sand_canyon_2_s22 = "Sand Canyon 2 - Star 22"
sand_canyon_2_s23 = "Sand Canyon 2 - Star 23"
sand_canyon_2_s24 = "Sand Canyon 2 - Star 24"
sand_canyon_2_s25 = "Sand Canyon 2 - Star 25"
sand_canyon_2_s26 = "Sand Canyon 2 - Star 26"
sand_canyon_2_s27 = "Sand Canyon 2 - Star 27"
sand_canyon_2_s28 = "Sand Canyon 2 - Star 28"
sand_canyon_2_s29 = "Sand Canyon 2 - Star 29"
sand_canyon_2_s30 = "Sand Canyon 2 - Star 30"
sand_canyon_2_s31 = "Sand Canyon 2 - Star 31"
sand_canyon_2_s32 = "Sand Canyon 2 - Star 32"
sand_canyon_2_s33 = "Sand Canyon 2 - Star 33"
sand_canyon_2_s34 = "Sand Canyon 2 - Star 34"
sand_canyon_2_s35 = "Sand Canyon 2 - Star 35"
sand_canyon_2_s36 = "Sand Canyon 2 - Star 36"
sand_canyon_2_s37 = "Sand Canyon 2 - Star 37"
sand_canyon_2_s38 = "Sand Canyon 2 - Star 38"
sand_canyon_2_s39 = "Sand Canyon 2 - Star 39"
sand_canyon_2_s40 = "Sand Canyon 2 - Star 40"
sand_canyon_2_s41 = "Sand Canyon 2 - Star 41"
sand_canyon_2_s42 = "Sand Canyon 2 - Star 42"
sand_canyon_2_s43 = "Sand Canyon 2 - Star 43"
sand_canyon_2_s44 = "Sand Canyon 2 - Star 44"
sand_canyon_2_s45 = "Sand Canyon 2 - Star 45"
sand_canyon_2_s46 = "Sand Canyon 2 - Star 46"
sand_canyon_2_s47 = "Sand Canyon 2 - Star 47"
sand_canyon_2_s48 = "Sand Canyon 2 - Star 48"
sand_canyon_3_s1 = "Sand Canyon 3 - Star 1"
sand_canyon_3_s2 = "Sand Canyon 3 - Star 2"
sand_canyon_3_s3 = "Sand Canyon 3 - Star 3"
sand_canyon_3_s4 = "Sand Canyon 3 - Star 4"
sand_canyon_3_s5 = "Sand Canyon 3 - Star 5"
sand_canyon_3_s6 = "Sand Canyon 3 - Star 6"
sand_canyon_3_s7 = "Sand Canyon 3 - Star 7"
sand_canyon_3_s8 = "Sand Canyon 3 - Star 8"
sand_canyon_3_s9 = "Sand Canyon 3 - Star 9"
sand_canyon_3_s10 = "Sand Canyon 3 - Star 10"
sand_canyon_4_s1 = "Sand Canyon 4 - Star 1"
sand_canyon_4_s2 = "Sand Canyon 4 - Star 2"
sand_canyon_4_s3 = "Sand Canyon 4 - Star 3"
sand_canyon_4_s4 = "Sand Canyon 4 - Star 4"
sand_canyon_4_s5 = "Sand Canyon 4 - Star 5"
sand_canyon_4_s6 = "Sand Canyon 4 - Star 6"
sand_canyon_4_s7 = "Sand Canyon 4 - Star 7"
sand_canyon_4_s8 = "Sand Canyon 4 - Star 8"
sand_canyon_4_s9 = "Sand Canyon 4 - Star 9"
sand_canyon_4_s10 = "Sand Canyon 4 - Star 10"
sand_canyon_4_s11 = "Sand Canyon 4 - Star 11"
sand_canyon_4_s12 = "Sand Canyon 4 - Star 12"
sand_canyon_4_s13 = "Sand Canyon 4 - Star 13"
sand_canyon_4_s14 = "Sand Canyon 4 - Star 14"
sand_canyon_4_s15 = "Sand Canyon 4 - Star 15"
sand_canyon_4_s16 = "Sand Canyon 4 - Star 16"
sand_canyon_4_s17 = "Sand Canyon 4 - Star 17"
sand_canyon_4_s18 = "Sand Canyon 4 - Star 18"
sand_canyon_4_s19 = "Sand Canyon 4 - Star 19"
sand_canyon_4_s20 = "Sand Canyon 4 - Star 20"
sand_canyon_4_s21 = "Sand Canyon 4 - Star 21"
sand_canyon_4_s22 = "Sand Canyon 4 - Star 22"
sand_canyon_4_s23 = "Sand Canyon 4 - Star 23"
sand_canyon_5_s1 = "Sand Canyon 5 - Star 1"
sand_canyon_5_s2 = "Sand Canyon 5 - Star 2"
sand_canyon_5_s3 = "Sand Canyon 5 - Star 3"
sand_canyon_5_s4 = "Sand Canyon 5 - Star 4"
sand_canyon_5_s5 = "Sand Canyon 5 - Star 5"
sand_canyon_5_s6 = "Sand Canyon 5 - Star 6"
sand_canyon_5_s7 = "Sand Canyon 5 - Star 7"
sand_canyon_5_s8 = "Sand Canyon 5 - Star 8"
sand_canyon_5_s9 = "Sand Canyon 5 - Star 9"
sand_canyon_5_s10 = "Sand Canyon 5 - Star 10"
sand_canyon_5_s11 = "Sand Canyon 5 - Star 11"
sand_canyon_5_s12 = "Sand Canyon 5 - Star 12"
sand_canyon_5_s13 = "Sand Canyon 5 - Star 13"
sand_canyon_5_s14 = "Sand Canyon 5 - Star 14"
sand_canyon_5_s15 = "Sand Canyon 5 - Star 15"
sand_canyon_5_s16 = "Sand Canyon 5 - Star 16"
sand_canyon_5_s17 = "Sand Canyon 5 - Star 17"
sand_canyon_5_s18 = "Sand Canyon 5 - Star 18"
sand_canyon_5_s19 = "Sand Canyon 5 - Star 19"
sand_canyon_5_s20 = "Sand Canyon 5 - Star 20"
sand_canyon_5_s21 = "Sand Canyon 5 - Star 21"
sand_canyon_5_s22 = "Sand Canyon 5 - Star 22"
sand_canyon_5_s23 = "Sand Canyon 5 - Star 23"
sand_canyon_5_s24 = "Sand Canyon 5 - Star 24"
sand_canyon_5_s25 = "Sand Canyon 5 - Star 25"
sand_canyon_5_s26 = "Sand Canyon 5 - Star 26"
sand_canyon_5_s27 = "Sand Canyon 5 - Star 27"
sand_canyon_5_s28 = "Sand Canyon 5 - Star 28"
sand_canyon_5_s29 = "Sand Canyon 5 - Star 29"
sand_canyon_5_s30 = "Sand Canyon 5 - Star 30"
sand_canyon_5_s31 = "Sand Canyon 5 - Star 31"
sand_canyon_5_s32 = "Sand Canyon 5 - Star 32"
sand_canyon_5_s33 = "Sand Canyon 5 - Star 33"
sand_canyon_5_s34 = "Sand Canyon 5 - Star 34"
sand_canyon_5_s35 = "Sand Canyon 5 - Star 35"
sand_canyon_5_s36 = "Sand Canyon 5 - Star 36"
sand_canyon_5_s37 = "Sand Canyon 5 - Star 37"
sand_canyon_5_s38 = "Sand Canyon 5 - Star 38"
sand_canyon_5_s39 = "Sand Canyon 5 - Star 39"
sand_canyon_5_s40 = "Sand Canyon 5 - Star 40"
cloudy_park_1_s1 = "Cloudy Park 1 - Star 1"
cloudy_park_1_s2 = "Cloudy Park 1 - Star 2"
cloudy_park_1_s3 = "Cloudy Park 1 - Star 3"
cloudy_park_1_s4 = "Cloudy Park 1 - Star 4"
cloudy_park_1_s5 = "Cloudy Park 1 - Star 5"
cloudy_park_1_s6 = "Cloudy Park 1 - Star 6"
cloudy_park_1_s7 = "Cloudy Park 1 - Star 7"
cloudy_park_1_s8 = "Cloudy Park 1 - Star 8"
cloudy_park_1_s9 = "Cloudy Park 1 - Star 9"
cloudy_park_1_s10 = "Cloudy Park 1 - Star 10"
cloudy_park_1_s11 = "Cloudy Park 1 - Star 11"
cloudy_park_1_s12 = "Cloudy Park 1 - Star 12"
cloudy_park_1_s13 = "Cloudy Park 1 - Star 13"
cloudy_park_1_s14 = "Cloudy Park 1 - Star 14"
cloudy_park_1_s15 = "Cloudy Park 1 - Star 15"
cloudy_park_1_s16 = "Cloudy Park 1 - Star 16"
cloudy_park_1_s17 = "Cloudy Park 1 - Star 17"
cloudy_park_1_s18 = "Cloudy Park 1 - Star 18"
cloudy_park_1_s19 = "Cloudy Park 1 - Star 19"
cloudy_park_1_s20 = "Cloudy Park 1 - Star 20"
cloudy_park_1_s21 = "Cloudy Park 1 - Star 21"
cloudy_park_1_s22 = "Cloudy Park 1 - Star 22"
cloudy_park_1_s23 = "Cloudy Park 1 - Star 23"
cloudy_park_2_s1 = "Cloudy Park 2 - Star 1"
cloudy_park_2_s2 = "Cloudy Park 2 - Star 2"
cloudy_park_2_s3 = "Cloudy Park 2 - Star 3"
cloudy_park_2_s4 = "Cloudy Park 2 - Star 4"
cloudy_park_2_s5 = "Cloudy Park 2 - Star 5"
cloudy_park_2_s6 = "Cloudy Park 2 - Star 6"
cloudy_park_2_s7 = "Cloudy Park 2 - Star 7"
cloudy_park_2_s8 = "Cloudy Park 2 - Star 8"
cloudy_park_2_s9 = "Cloudy Park 2 - Star 9"
cloudy_park_2_s10 = "Cloudy Park 2 - Star 10"
cloudy_park_2_s11 = "Cloudy Park 2 - Star 11"
cloudy_park_2_s12 = "Cloudy Park 2 - Star 12"
cloudy_park_2_s13 = "Cloudy Park 2 - Star 13"
cloudy_park_2_s14 = "Cloudy Park 2 - Star 14"
cloudy_park_2_s15 = "Cloudy Park 2 - Star 15"
cloudy_park_2_s16 = "Cloudy Park 2 - Star 16"
cloudy_park_2_s17 = "Cloudy Park 2 - Star 17"
cloudy_park_2_s18 = "Cloudy Park 2 - Star 18"
cloudy_park_2_s19 = "Cloudy Park 2 - Star 19"
cloudy_park_2_s20 = "Cloudy Park 2 - Star 20"
cloudy_park_2_s21 = "Cloudy Park 2 - Star 21"
cloudy_park_2_s22 = "Cloudy Park 2 - Star 22"
cloudy_park_2_s23 = "Cloudy Park 2 - Star 23"
cloudy_park_2_s24 = "Cloudy Park 2 - Star 24"
cloudy_park_2_s25 = "Cloudy Park 2 - Star 25"
cloudy_park_2_s26 = "Cloudy Park 2 - Star 26"
cloudy_park_2_s27 = "Cloudy Park 2 - Star 27"
cloudy_park_2_s28 = "Cloudy Park 2 - Star 28"
cloudy_park_2_s29 = "Cloudy Park 2 - Star 29"
cloudy_park_2_s30 = "Cloudy Park 2 - Star 30"
cloudy_park_2_s31 = "Cloudy Park 2 - Star 31"
cloudy_park_2_s32 = "Cloudy Park 2 - Star 32"
cloudy_park_2_s33 = "Cloudy Park 2 - Star 33"
cloudy_park_2_s34 = "Cloudy Park 2 - Star 34"
cloudy_park_2_s35 = "Cloudy Park 2 - Star 35"
cloudy_park_2_s36 = "Cloudy Park 2 - Star 36"
cloudy_park_2_s37 = "Cloudy Park 2 - Star 37"
cloudy_park_2_s38 = "Cloudy Park 2 - Star 38"
cloudy_park_2_s39 = "Cloudy Park 2 - Star 39"
cloudy_park_2_s40 = "Cloudy Park 2 - Star 40"
cloudy_park_2_s41 = "Cloudy Park 2 - Star 41"
cloudy_park_2_s42 = "Cloudy Park 2 - Star 42"
cloudy_park_2_s43 = "Cloudy Park 2 - Star 43"
cloudy_park_2_s44 = "Cloudy Park 2 - Star 44"
cloudy_park_2_s45 = "Cloudy Park 2 - Star 45"
cloudy_park_2_s46 = "Cloudy Park 2 - Star 46"
cloudy_park_2_s47 = "Cloudy Park 2 - Star 47"
cloudy_park_2_s48 = "Cloudy Park 2 - Star 48"
cloudy_park_2_s49 = "Cloudy Park 2 - Star 49"
cloudy_park_2_s50 = "Cloudy Park 2 - Star 50"
cloudy_park_2_s51 = "Cloudy Park 2 - Star 51"
cloudy_park_2_s52 = "Cloudy Park 2 - Star 52"
cloudy_park_2_s53 = "Cloudy Park 2 - Star 53"
cloudy_park_2_s54 = "Cloudy Park 2 - Star 54"
cloudy_park_3_s1 = "Cloudy Park 3 - Star 1"
cloudy_park_3_s2 = "Cloudy Park 3 - Star 2"
cloudy_park_3_s3 = "Cloudy Park 3 - Star 3"
cloudy_park_3_s4 = "Cloudy Park 3 - Star 4"
cloudy_park_3_s5 = "Cloudy Park 3 - Star 5"
cloudy_park_3_s6 = "Cloudy Park 3 - Star 6"
cloudy_park_3_s7 = "Cloudy Park 3 - Star 7"
cloudy_park_3_s8 = "Cloudy Park 3 - Star 8"
cloudy_park_3_s9 = "Cloudy Park 3 - Star 9"
cloudy_park_3_s10 = "Cloudy Park 3 - Star 10"
cloudy_park_3_s11 = "Cloudy Park 3 - Star 11"
cloudy_park_3_s12 = "Cloudy Park 3 - Star 12"
cloudy_park_3_s13 = "Cloudy Park 3 - Star 13"
cloudy_park_3_s14 = "Cloudy Park 3 - Star 14"
cloudy_park_3_s15 = "Cloudy Park 3 - Star 15"
cloudy_park_3_s16 = "Cloudy Park 3 - Star 16"
cloudy_park_3_s17 = "Cloudy Park 3 - Star 17"
cloudy_park_3_s18 = "Cloudy Park 3 - Star 18"
cloudy_park_3_s19 = "Cloudy Park 3 - Star 19"
cloudy_park_3_s20 = "Cloudy Park 3 - Star 20"
cloudy_park_3_s21 = "Cloudy Park 3 - Star 21"
cloudy_park_3_s22 = "Cloudy Park 3 - Star 22"
cloudy_park_4_s1 = "Cloudy Park 4 - Star 1"
cloudy_park_4_s2 = "Cloudy Park 4 - Star 2"
cloudy_park_4_s3 = "Cloudy Park 4 - Star 3"
cloudy_park_4_s4 = "Cloudy Park 4 - Star 4"
cloudy_park_4_s5 = "Cloudy Park 4 - Star 5"
cloudy_park_4_s6 = "Cloudy Park 4 - Star 6"
cloudy_park_4_s7 = "Cloudy Park 4 - Star 7"
cloudy_park_4_s8 = "Cloudy Park 4 - Star 8"
cloudy_park_4_s9 = "Cloudy Park 4 - Star 9"
cloudy_park_4_s10 = "Cloudy Park 4 - Star 10"
cloudy_park_4_s11 = "Cloudy Park 4 - Star 11"
cloudy_park_4_s12 = "Cloudy Park 4 - Star 12"
cloudy_park_4_s13 = "Cloudy Park 4 - Star 13"
cloudy_park_4_s14 = "Cloudy Park 4 - Star 14"
cloudy_park_4_s15 = "Cloudy Park 4 - Star 15"
cloudy_park_4_s16 = "Cloudy Park 4 - Star 16"
cloudy_park_4_s17 = "Cloudy Park 4 - Star 17"
cloudy_park_4_s18 = "Cloudy Park 4 - Star 18"
cloudy_park_4_s19 = "Cloudy Park 4 - Star 19"
cloudy_park_4_s20 = "Cloudy Park 4 - Star 20"
cloudy_park_4_s21 = "Cloudy Park 4 - Star 21"
cloudy_park_4_s22 = "Cloudy Park 4 - Star 22"
cloudy_park_4_s23 = "Cloudy Park 4 - Star 23"
cloudy_park_4_s24 = "Cloudy Park 4 - Star 24"
cloudy_park_4_s25 = "Cloudy Park 4 - Star 25"
cloudy_park_4_s26 = "Cloudy Park 4 - Star 26"
cloudy_park_4_s27 = "Cloudy Park 4 - Star 27"
cloudy_park_4_s28 = "Cloudy Park 4 - Star 28"
cloudy_park_4_s29 = "Cloudy Park 4 - Star 29"
cloudy_park_4_s30 = "Cloudy Park 4 - Star 30"
cloudy_park_4_s31 = "Cloudy Park 4 - Star 31"
cloudy_park_4_s32 = "Cloudy Park 4 - Star 32"
cloudy_park_4_s33 = "Cloudy Park 4 - Star 33"
cloudy_park_4_s34 = "Cloudy Park 4 - Star 34"
cloudy_park_4_s35 = "Cloudy Park 4 - Star 35"
cloudy_park_4_s36 = "Cloudy Park 4 - Star 36"
cloudy_park_4_s37 = "Cloudy Park 4 - Star 37"
cloudy_park_4_s38 = "Cloudy Park 4 - Star 38"
cloudy_park_4_s39 = "Cloudy Park 4 - Star 39"
cloudy_park_4_s40 = "Cloudy Park 4 - Star 40"
cloudy_park_4_s41 = "Cloudy Park 4 - Star 41"
cloudy_park_4_s42 = "Cloudy Park 4 - Star 42"
cloudy_park_4_s43 = "Cloudy Park 4 - Star 43"
cloudy_park_4_s44 = "Cloudy Park 4 - Star 44"
cloudy_park_4_s45 = "Cloudy Park 4 - Star 45"
cloudy_park_4_s46 = "Cloudy Park 4 - Star 46"
cloudy_park_4_s47 = "Cloudy Park 4 - Star 47"
cloudy_park_4_s48 = "Cloudy Park 4 - Star 48"
cloudy_park_4_s49 = "Cloudy Park 4 - Star 49"
cloudy_park_4_s50 = "Cloudy Park 4 - Star 50"
cloudy_park_5_s1 = "Cloudy Park 5 - Star 1"
cloudy_park_5_s2 = "Cloudy Park 5 - Star 2"
cloudy_park_5_s3 = "Cloudy Park 5 - Star 3"
cloudy_park_5_s4 = "Cloudy Park 5 - Star 4"
cloudy_park_5_s5 = "Cloudy Park 5 - Star 5"
cloudy_park_5_s6 = "Cloudy Park 5 - Star 6"
cloudy_park_6_s1 = "Cloudy Park 6 - Star 1"
cloudy_park_6_s2 = "Cloudy Park 6 - Star 2"
cloudy_park_6_s3 = "Cloudy Park 6 - Star 3"
cloudy_park_6_s4 = "Cloudy Park 6 - Star 4"
cloudy_park_6_s5 = "Cloudy Park 6 - Star 5"
cloudy_park_6_s6 = "Cloudy Park 6 - Star 6"
cloudy_park_6_s7 = "Cloudy Park 6 - Star 7"
cloudy_park_6_s8 = "Cloudy Park 6 - Star 8"
cloudy_park_6_s9 = "Cloudy Park 6 - Star 9"
cloudy_park_6_s10 = "Cloudy Park 6 - Star 10"
cloudy_park_6_s11 = "Cloudy Park 6 - Star 11"
cloudy_park_6_s12 = "Cloudy Park 6 - Star 12"
cloudy_park_6_s13 = "Cloudy Park 6 - Star 13"
cloudy_park_6_s14 = "Cloudy Park 6 - Star 14"
cloudy_park_6_s15 = "Cloudy Park 6 - Star 15"
cloudy_park_6_s16 = "Cloudy Park 6 - Star 16"
cloudy_park_6_s17 = "Cloudy Park 6 - Star 17"
cloudy_park_6_s18 = "Cloudy Park 6 - Star 18"
cloudy_park_6_s19 = "Cloudy Park 6 - Star 19"
cloudy_park_6_s20 = "Cloudy Park 6 - Star 20"
cloudy_park_6_s21 = "Cloudy Park 6 - Star 21"
cloudy_park_6_s22 = "Cloudy Park 6 - Star 22"
cloudy_park_6_s23 = "Cloudy Park 6 - Star 23"
cloudy_park_6_s24 = "Cloudy Park 6 - Star 24"
cloudy_park_6_s25 = "Cloudy Park 6 - Star 25"
cloudy_park_6_s26 = "Cloudy Park 6 - Star 26"
cloudy_park_6_s27 = "Cloudy Park 6 - Star 27"
cloudy_park_6_s28 = "Cloudy Park 6 - Star 28"
cloudy_park_6_s29 = "Cloudy Park 6 - Star 29"
cloudy_park_6_s30 = "Cloudy Park 6 - Star 30"
cloudy_park_6_s31 = "Cloudy Park 6 - Star 31"
cloudy_park_6_s32 = "Cloudy Park 6 - Star 32"
cloudy_park_6_s33 = "Cloudy Park 6 - Star 33"
iceberg_1_s1 = "Iceberg 1 - Star 1"
iceberg_1_s2 = "Iceberg 1 - Star 2"
iceberg_1_s3 = "Iceberg 1 - Star 3"
iceberg_1_s4 = "Iceberg 1 - Star 4"
iceberg_1_s5 = "Iceberg 1 - Star 5"
iceberg_1_s6 = "Iceberg 1 - Star 6"
iceberg_2_s1 = "Iceberg 2 - Star 1"
iceberg_2_s2 = "Iceberg 2 - Star 2"
iceberg_2_s3 = "Iceberg 2 - Star 3"
iceberg_2_s4 = "Iceberg 2 - Star 4"
iceberg_2_s5 = "Iceberg 2 - Star 5"
iceberg_2_s6 = "Iceberg 2 - Star 6"
iceberg_2_s7 = "Iceberg 2 - Star 7"
iceberg_2_s8 = "Iceberg 2 - Star 8"
iceberg_2_s9 = "Iceberg 2 - Star 9"
iceberg_2_s10 = "Iceberg 2 - Star 10"
iceberg_2_s11 = "Iceberg 2 - Star 11"
iceberg_2_s12 = "Iceberg 2 - Star 12"
iceberg_2_s13 = "Iceberg 2 - Star 13"
iceberg_2_s14 = "Iceberg 2 - Star 14"
iceberg_2_s15 = "Iceberg 2 - Star 15"
iceberg_2_s16 = "Iceberg 2 - Star 16"
iceberg_2_s17 = "Iceberg 2 - Star 17"
iceberg_2_s18 = "Iceberg 2 - Star 18"
iceberg_2_s19 = "Iceberg 2 - Star 19"
iceberg_3_s1 = "Iceberg 3 - Star 1"
iceberg_3_s2 = "Iceberg 3 - Star 2"
iceberg_3_s3 = "Iceberg 3 - Star 3"
iceberg_3_s4 = "Iceberg 3 - Star 4"
iceberg_3_s5 = "Iceberg 3 - Star 5"
iceberg_3_s6 = "Iceberg 3 - Star 6"
iceberg_3_s7 = "Iceberg 3 - Star 7"
iceberg_3_s8 = "Iceberg 3 - Star 8"
iceberg_3_s9 = "Iceberg 3 - Star 9"
iceberg_3_s10 = "Iceberg 3 - Star 10"
iceberg_3_s11 = "Iceberg 3 - Star 11"
iceberg_3_s12 = "Iceberg 3 - Star 12"
iceberg_3_s13 = "Iceberg 3 - Star 13"
iceberg_3_s14 = "Iceberg 3 - Star 14"
iceberg_3_s15 = "Iceberg 3 - Star 15"
iceberg_3_s16 = "Iceberg 3 - Star 16"
iceberg_3_s17 = "Iceberg 3 - Star 17"
iceberg_3_s18 = "Iceberg 3 - Star 18"
iceberg_3_s19 = "Iceberg 3 - Star 19"
iceberg_3_s20 = "Iceberg 3 - Star 20"
iceberg_3_s21 = "Iceberg 3 - Star 21"
iceberg_4_s1 = "Iceberg 4 - Star 1"
iceberg_4_s2 = "Iceberg 4 - Star 2"
iceberg_4_s3 = "Iceberg 4 - Star 3"
iceberg_5_s1 = "Iceberg 5 - Star 1"
iceberg_5_s2 = "Iceberg 5 - Star 2"
iceberg_5_s3 = "Iceberg 5 - Star 3"
iceberg_5_s4 = "Iceberg 5 - Star 4"
iceberg_5_s5 = "Iceberg 5 - Star 5"
iceberg_5_s6 = "Iceberg 5 - Star 6"
iceberg_5_s7 = "Iceberg 5 - Star 7"
iceberg_5_s8 = "Iceberg 5 - Star 8"
iceberg_5_s9 = "Iceberg 5 - Star 9"
iceberg_5_s10 = "Iceberg 5 - Star 10"
iceberg_5_s11 = "Iceberg 5 - Star 11"
iceberg_5_s12 = "Iceberg 5 - Star 12"
iceberg_5_s13 = "Iceberg 5 - Star 13"
iceberg_5_s14 = "Iceberg 5 - Star 14"
iceberg_5_s15 = "Iceberg 5 - Star 15"
iceberg_5_s16 = "Iceberg 5 - Star 16"
iceberg_5_s17 = "Iceberg 5 - Star 17"
iceberg_5_s18 = "Iceberg 5 - Star 18"
iceberg_5_s19 = "Iceberg 5 - Star 19"
iceberg_5_s20 = "Iceberg 5 - Star 20"
iceberg_5_s21 = "Iceberg 5 - Star 21"
iceberg_5_s22 = "Iceberg 5 - Star 22"
iceberg_5_s23 = "Iceberg 5 - Star 23"
iceberg_5_s24 = "Iceberg 5 - Star 24"
iceberg_5_s25 = "Iceberg 5 - Star 25"
iceberg_5_s26 = "Iceberg 5 - Star 26"
iceberg_5_s27 = "Iceberg 5 - Star 27"
iceberg_5_s28 = "Iceberg 5 - Star 28"
iceberg_5_s29 = "Iceberg 5 - Star 29"
iceberg_5_s30 = "Iceberg 5 - Star 30"
iceberg_5_s31 = "Iceberg 5 - Star 31"
iceberg_5_s32 = "Iceberg 5 - Star 32"
iceberg_5_s33 = "Iceberg 5 - Star 33"
iceberg_5_s34 = "Iceberg 5 - Star 34"
iceberg_6_s1 = "Iceberg 6 - Star 1"

View File

432
worlds/kdl3/Options.py Normal file
View File

@@ -0,0 +1,432 @@
import random
from dataclasses import dataclass
from Options import DeathLink, Choice, Toggle, OptionDict, Range, PlandoBosses, DefaultOnToggle, \
PerGameCommonOptions
from .Names import LocationName
class Goal(Choice):
"""
Zero: collect the Heart Stars, and defeat Zero in the Hyper Zone.
Boss Butch: collect the Heart Stars, and then complete the boss rematches in the Boss Butch mode.
MG5: collect the Heart Stars, and then complete a perfect run through the minigame gauntlet within the Super MG5
Jumping: collect the Heart Stars, and then reach a designated score within the Jumping sub-game
"""
display_name = "Goal"
option_zero = 0
option_boss_butch = 1
option_MG5 = 2
option_jumping = 3
default = 0
@classmethod
def get_option_name(cls, value: int) -> str:
if value == 2:
return cls.name_lookup[value].upper()
return super().get_option_name(value)
class GoalSpeed(Choice):
"""
Normal: the goal is unlocked after purifying the five bosses
Fast: the goal is unlocked after acquiring the target number of Heart Stars
"""
display_name = "Goal Speed"
option_normal = 0
option_fast = 1
class TotalHeartStars(Range):
"""
Maximum number of heart stars to include in the pool of items.
"""
display_name = "Max Heart Stars"
range_start = 5 # set to 5 so strict bosses does not degrade
range_end = 50 # 30 default locations + 30 stage clears + 5 bosses - 14 progression items = 51, so round down
default = 30
class HeartStarsRequired(Range):
"""
Percentage of heart stars required to purify the five bosses and reach Zero.
Each boss will require a differing amount of heart stars to purify.
"""
display_name = "Required Heart Stars"
range_start = 1
range_end = 100
default = 50
class LevelShuffle(Choice):
"""
None: No stage shuffling.
Same World: shuffles stages around their world.
Pattern: shuffles stages according to the stage pattern (stage 3 will always be a minigame stage, etc.)
Shuffled: shuffles stages across all worlds.
"""
display_name = "Stage Shuffle"
option_none = 0
option_same_world = 1
option_pattern = 2
option_shuffled = 3
default = 0
class BossShuffle(PlandoBosses):
"""
None: Bosses will remain in their vanilla locations
Shuffled: Bosses will be shuffled amongst each other
Full: Bosses will be randomized
Singularity: All (non-Zero) bosses will be replaced with a single boss
Supports plando placement.
"""
bosses = frozenset(LocationName.boss_names.keys())
locations = frozenset(LocationName.level_names.keys())
duplicate_bosses = True
@classmethod
def can_place_boss(cls, boss: str, location: str) -> bool:
# Kirby has no logic about requiring bosses in specific locations (since we load in their stage)
return True
display_name = "Boss Shuffle"
option_none = 0
option_shuffled = 1
option_full = 2
option_singularity = 3
class BossShuffleAllowBB(Choice):
"""
Allow Boss Butch variants of bosses in Boss Shuffle.
Enabled: any boss placed will have a 50% chance of being the Boss Butch variant, including bosses not present
Enforced: all bosses will be their Boss Butch variant.
Boss Butch boss changes are only visual.
"""
display_name = "Allow Boss Butch Bosses"
option_disabled = 0
option_enabled = 1
option_enforced = 2
default = 0
class AnimalRandomization(Choice):
"""
Disabled: all animal positions will be vanilla.
Shuffled: all animal positions will be shuffled amongst each other.
Full: random animals will be placed across the levels. At least one of each animal is guaranteed.
"""
display_name = "Animal Randomization"
option_disabled = 0
option_shuffled = 1
option_full = 2
default = 0
class CopyAbilityRandomization(Choice):
"""
Disabled: enemies give regular copy abilities and health.
Enabled: all enemies will have the copy ability received from them randomized.
Enabled Plus Minus: enemies (except minibosses) can additionally give you anywhere from +2 health to -1 health when eaten.
"""
display_name = "Copy Ability Randomization"
option_disabled = 0
option_enabled = 1
option_enabled_plus_minus = 2
class StrictBosses(DefaultOnToggle):
"""
If enabled, one will not be able to move onto the next world until the previous world's boss has been purified.
"""
display_name = "Strict Bosses"
class OpenWorld(DefaultOnToggle):
"""
If enabled, all 6 stages will be unlocked upon entering a world for the first time. A certain amount of stages
will need to be completed in order to unlock the bosses
"""
display_name = "Open World"
class OpenWorldBossRequirement(Range):
"""
The amount of stages completed needed to unlock the boss of a world when Open World is turned on.
"""
display_name = "Open World Boss Requirement"
range_start = 1
range_end = 6
default = 3
class BossRequirementRandom(Toggle):
"""
If enabled, boss purification will require a random amount of Heart Stars. Depending on options, this may have
boss purification unlock in a random order.
"""
display_name = "Randomize Purification Requirement"
class JumpingTarget(Range):
"""
The required score needed to complete the Jumping minigame.
"""
display_name = "Jumping Target Score"
range_start = 1
range_end = 25
default = 10
class GameLanguage(Choice):
"""
The language that the game should display. This does not have to match the given rom.
"""
display_name = "Game Language"
option_japanese = 0
option_english = 1
default = 1
class FillerPercentage(Range):
"""
Percentage of non-required Heart Stars to be converted to filler items (1-Ups, Maxim Tomatoes, Invincibility Candy).
"""
display_name = "Filler Percentage"
range_start = 0
range_end = 100
default = 50
class TrapPercentage(Range):
"""
Percentage of filler items to be converted to trap items (Gooey Bags, Slowness, Eject Ability).
"""
display_name = "Trap Percentage"
range_start = 0
range_end = 100
default = 50
class GooeyTrapPercentage(Range):
"""
Chance that any given trap is a Gooey Bag (spawns Gooey when you receive it).
"""
display_name = "Gooey Trap Percentage"
range_start = 0
range_end = 100
default = 50
class SlowTrapPercentage(Range):
"""
Chance that any given trap is Slowness (halves your max speed for 15 seconds when you receive it).
"""
display_name = "Slowness Trap Percentage"
range_start = 0
range_end = 100
default = 50
class AbilityTrapPercentage(Range):
"""
Chance that any given trap is an Eject Ability (ejects your ability when you receive it).
"""
display_name = "Ability Trap Percentage"
range_start = 0
range_end = 100
default = 50
class ConsumableChecks(Toggle):
"""
When enabled, adds all 1-Ups and Maxim Tomatoes as possible locations.
"""
display_name = "Consumable-sanity"
class StarChecks(Toggle):
"""
When enabled, every star in a given stage will become a check.
Will increase the possible filler pool to include 1/3/5 stars.
"""
display_name = "Starsanity"
class KirbyFlavorPreset(Choice):
"""
The color of Kirby, from a list of presets.
"""
display_name = "Kirby Flavor"
option_default = 0
option_bubblegum = 1
option_cherry = 2
option_blueberry = 3
option_lemon = 4
option_kiwi = 5
option_grape = 6
option_chocolate = 7
option_marshmallow = 8
option_licorice = 9
option_watermelon = 10
option_orange = 11
option_lime = 12
option_lavender = 13
option_custom = 14
default = 0
@classmethod
def from_text(cls, text: str) -> Choice:
text = text.lower()
if text == "random":
choice_list = list(cls.name_lookup)
choice_list.remove(14)
return cls(random.choice(choice_list))
return super().from_text(text)
class KirbyFlavor(OptionDict):
"""
A custom color for Kirby. To use a custom color, set the preset to Custom and then define a dict of keys from "1" to
"15", with their values being an HTML hex color.
"""
default = {
"1": "B01810",
"2": "F0E0E8",
"3": "C8A0A8",
"4": "A87070",
"5": "E02018",
"6": "F0A0B8",
"7": "D07880",
"8": "A85048",
"9": "E8D0D0",
"10": "E85048",
"11": "D0C0C0",
"12": "B08888",
"13": "E87880",
"14": "F8F8F8",
"15": "B03830",
}
class GooeyFlavorPreset(Choice):
"""
The color of Gooey, from a list of presets.
"""
display_name = "Gooey Flavor"
option_default = 0
option_bubblegum = 1
option_cherry = 2
option_blueberry = 3
option_lemon = 4
option_kiwi = 5
option_grape = 6
option_chocolate = 7
option_marshmallow = 8
option_licorice = 9
option_watermelon = 10
option_orange = 11
option_lime = 12
option_lavender = 13
option_custom = 14
default = 0
@classmethod
def from_text(cls, text: str) -> Choice:
text = text.lower()
if text == "random":
choice_list = list(cls.name_lookup)
choice_list.remove(14)
return cls(random.choice(choice_list))
return super().from_text(text)
class GooeyFlavor(OptionDict):
"""
A custom color for Gooey. To use a custom color, set the preset to Custom and then define a dict of keys from "1" to
"15", with their values being an HTML hex color.
"""
default = {
"1": "000808",
"2": "102838",
"3": "183048",
"4": "183878",
"5": "1838A0",
"6": "B01810",
"7": "E85048",
"8": "D0C0C0",
"9": "F8F8F8",
}
class MusicShuffle(Choice):
"""
None: default music will play
Shuffled: music will be shuffled amongst each other
Full: random music will play in each room
Note that certain songs will not be chosen in shuffled or full
"""
display_name = "Music Randomization"
option_none = 0
option_shuffled = 1
option_full = 2
default = 0
class VirtualConsoleChanges(Choice):
"""
Adds the ability to enable 2 of the Virtual Console changes.
Flash Reduction: reduces the flashing during the Zero battle.
Color Changes: changes the color of the background within the Zero Boss Butch rematch.
"""
display_name = "Virtual Console Changes"
option_none = 0
option_flash_reduction = 1
option_color_changes = 2
option_both = 3
default = 1
class Gifting(Toggle):
"""
When enabled, the goal game item will be sent to other compatible games as a gift,
and you can receive gifts from other players. This can be enabled during gameplay
using the client.
"""
display_name = "Gifting"
@dataclass
class KDL3Options(PerGameCommonOptions):
death_link: DeathLink
game_language: GameLanguage
goal: Goal
goal_speed: GoalSpeed
total_heart_stars: TotalHeartStars
heart_stars_required: HeartStarsRequired
filler_percentage: FillerPercentage
trap_percentage: TrapPercentage
gooey_trap_weight: GooeyTrapPercentage
slow_trap_weight: SlowTrapPercentage
ability_trap_weight: AbilityTrapPercentage
jumping_target: JumpingTarget
stage_shuffle: LevelShuffle
boss_shuffle: BossShuffle
allow_bb: BossShuffleAllowBB
animal_randomization: AnimalRandomization
copy_ability_randomization: CopyAbilityRandomization
strict_bosses: StrictBosses
open_world: OpenWorld
ow_boss_requirement: OpenWorldBossRequirement
boss_requirement_random: BossRequirementRandom
consumables: ConsumableChecks
starsanity: StarChecks
gifting: Gifting
kirby_flavor_preset: KirbyFlavorPreset
kirby_flavor: KirbyFlavor
gooey_flavor_preset: GooeyFlavorPreset
gooey_flavor: GooeyFlavor
music_shuffle: MusicShuffle
virtual_console: VirtualConsoleChanges

56
worlds/kdl3/Presets.py Normal file
View File

@@ -0,0 +1,56 @@
from typing import Dict, Any
all_random = {
"progression_balancing": "random",
"accessibility": "random",
"death_link": "random",
"game_language": "random",
"goal": "random",
"goal_speed": "random",
"total_heart_stars": "random",
"heart_stars_required": "random",
"filler_percentage": "random",
"trap_percentage": "random",
"gooey_trap_weight": "random",
"slow_trap_weight": "random",
"ability_trap_weight": "random",
"jumping_target": "random",
"stage_shuffle": "random",
"boss_shuffle": "random",
"allow_bb": "random",
"animal_randomization": "random",
"copy_ability_randomization": "random",
"strict_bosses": "random",
"open_world": "random",
"ow_boss_requirement": "random",
"boss_requirement_random": "random",
"consumables": "random",
"kirby_flavor_preset": "random",
"gooey_flavor_preset": "random",
"music_shuffle": "random",
}
beginner = {
"goal": "zero",
"goal_speed": "normal",
"total_heart_stars": 50,
"heart_stars_required": 30,
"filler_percentage": 25,
"trap_percentage": 0,
"gooey_trap_weight": "random",
"slow_trap_weight": "random",
"ability_trap_weight": "random",
"jumping_target": 5,
"stage_shuffle": "pattern",
"boss_shuffle": "shuffled",
"allow_bb": "random",
"strict_bosses": True,
"open_world": True,
"ow_boss_requirement": 3,
}
kdl3_options_presets: Dict[str, Dict[str, Any]] = {
"All Random": all_random,
"Beginner": beginner,
}

231
worlds/kdl3/Regions.py Normal file
View File

@@ -0,0 +1,231 @@
import orjson
import os
import typing
from pkgutil import get_data
from BaseClasses import Region
from worlds.generic.Rules import add_item_rule
from .Locations import KDL3Location
from .Names import LocationName
from .Options import BossShuffle
from .Room import KDL3Room
if typing.TYPE_CHECKING:
from . import KDL3World
default_levels = {
1: [0x770001, 0x770002, 0x770003, 0x770004, 0x770005, 0x770006, 0x770200],
2: [0x770007, 0x770008, 0x770009, 0x77000A, 0x77000B, 0x77000C, 0x770201],
3: [0x77000D, 0x77000E, 0x77000F, 0x770010, 0x770011, 0x770012, 0x770202],
4: [0x770013, 0x770014, 0x770015, 0x770016, 0x770017, 0x770018, 0x770203],
5: [0x770019, 0x77001A, 0x77001B, 0x77001C, 0x77001D, 0x77001E, 0x770204],
}
first_stage_blacklist = {
# We want to confirm that the first stage can be completed without any items
0x77000B, # 2-5 needs Kine
0x770011, # 3-5 needs Cutter
0x77001C, # 5-4 needs Burning
}
def generate_valid_level(level, stage, possible_stages, slot_random):
new_stage = slot_random.choice(possible_stages)
if level == 1 and stage == 0 and new_stage in first_stage_blacklist:
return generate_valid_level(level, stage, possible_stages, slot_random)
else:
return new_stage
def generate_rooms(world: "KDL3World", door_shuffle: bool, level_regions: typing.Dict[int, Region]):
level_names = {LocationName.level_names[level]: level for level in LocationName.level_names}
room_data = orjson.loads(get_data(__name__, os.path.join("data", "Rooms.json")))
rooms: typing.Dict[str, KDL3Room] = dict()
for room_entry in room_data:
room = KDL3Room(room_entry["name"], world.player, world.multiworld, None, room_entry["level"],
room_entry["stage"], room_entry["room"], room_entry["pointer"], room_entry["music"],
room_entry["default_exits"], room_entry["animal_pointers"], room_entry["enemies"],
room_entry["entity_load"], room_entry["consumables"], room_entry["consumables_pointer"])
room.add_locations({location: world.location_name_to_id[location] if location in world.location_name_to_id else
None for location in room_entry["locations"]
if (not any(x in location for x in ["1-Up", "Maxim"]) or
world.options.consumables.value) and ("Star" not in location
or world.options.starsanity.value)},
KDL3Location)
rooms[room.name] = room
for location in room.locations:
if "Animal" in location.name:
add_item_rule(location, lambda item: item.name in {
"Rick Spawn", "Kine Spawn", "Coo Spawn", "Nago Spawn", "ChuChu Spawn", "Pitch Spawn"
})
world.rooms = list(rooms.values())
world.multiworld.regions.extend(world.rooms)
first_rooms: typing.Dict[int, KDL3Room] = dict()
if door_shuffle:
# first, we need to generate the notable edge cases
# 5-6 is the first, being the most restrictive
# half of its rooms are required to be vanilla, but can be in different orders
# the room before it *must* contain the copy ability required to unlock the room's goal
raise NotImplementedError()
else:
for name, room in rooms.items():
if room.room == 0:
if room.stage == 7:
first_rooms[0x770200 + room.level - 1] = room
else:
first_rooms[0x770000 + ((room.level - 1) * 6) + room.stage] = room
exits = dict()
for def_exit in room.default_exits:
target = f"{level_names[room.level]} {room.stage} - {def_exit['room']}"
access_rule = tuple(def_exit["access_rule"])
exits[target] = lambda state, rule=access_rule: state.has_all(rule, world.player)
room.add_exits(
exits.keys(),
exits
)
if world.options.open_world:
if any("Complete" in location.name for location in room.locations):
room.add_locations({f"{level_names[room.level]} {room.stage} - Stage Completion": None},
KDL3Location)
for level in world.player_levels:
for stage in range(6):
proper_stage = world.player_levels[level][stage]
stage_name = world.multiworld.get_location(world.location_id_to_name[proper_stage],
world.player).name.replace(" - Complete", "")
stage_regions = [rooms[room] for room in rooms if stage_name in rooms[room].name]
for region in stage_regions:
region.level = level
region.stage = stage
if world.options.open_world or stage == 0:
level_regions[level].add_exits([first_rooms[proper_stage].name])
else:
world.multiworld.get_location(world.location_id_to_name[world.player_levels[level][stage-1]],
world.player).parent_region.add_exits([first_rooms[proper_stage].name])
level_regions[level].add_exits([first_rooms[0x770200 + level - 1].name])
def generate_valid_levels(world: "KDL3World", enforce_world: bool, enforce_pattern: bool) -> dict:
levels: typing.Dict[int, typing.List[typing.Optional[int]]] = {
1: [None] * 7,
2: [None] * 7,
3: [None] * 7,
4: [None] * 7,
5: [None] * 7,
}
possible_stages = [default_levels[level][stage] for level in default_levels for stage in range(6)]
if world.multiworld.plando_connections[world.player]:
for connection in world.multiworld.plando_connections[world.player]:
try:
entrance_world, entrance_stage = connection.entrance.rsplit(" ", 1)
stage_world, stage_stage = connection.exit.rsplit(" ", 1)
new_stage = default_levels[LocationName.level_names[stage_world.strip()]][int(stage_stage) - 1]
levels[LocationName.level_names[entrance_world.strip()]][int(entrance_stage) - 1] = new_stage
possible_stages.remove(new_stage)
except Exception:
raise Exception(
f"Invalid connection: {connection.entrance} =>"
f" {connection.exit} for player {world.player} ({world.player_name})")
for level in range(1, 6):
for stage in range(6):
# Randomize bosses separately
try:
if levels[level][stage] is None:
stage_candidates = [candidate for candidate in possible_stages
if (enforce_world and candidate in default_levels[level])
or (enforce_pattern and ((candidate - 1) & 0x00FFFF) % 6 == stage)
or (enforce_pattern == enforce_world)
]
new_stage = generate_valid_level(level, stage, stage_candidates,
world.random)
possible_stages.remove(new_stage)
levels[level][stage] = new_stage
except Exception:
raise Exception(f"Failed to find valid stage for {level}-{stage}. Remaining Stages:{possible_stages}")
# now handle bosses
boss_shuffle: typing.Union[int, str] = world.options.boss_shuffle.value
plando_bosses = []
if isinstance(boss_shuffle, str):
# boss plando
options = boss_shuffle.split(";")
boss_shuffle = BossShuffle.options[options.pop()]
for option in options:
if "-" in option:
loc, boss = option.split("-")
loc = loc.title()
boss = boss.title()
levels[LocationName.level_names[loc]][6] = LocationName.boss_names[boss]
plando_bosses.append(LocationName.boss_names[boss])
else:
option = option.title()
for level in levels:
if levels[level][6] is None:
levels[level][6] = LocationName.boss_names[option]
plando_bosses.append(LocationName.boss_names[option])
if boss_shuffle > 0:
if boss_shuffle == BossShuffle.option_full:
possible_bosses = [default_levels[world.random.randint(1, 5)][6]
for _ in range(5 - len(plando_bosses))]
elif boss_shuffle == BossShuffle.option_singularity:
boss = world.random.randint(1, 5)
possible_bosses = [default_levels[boss][6] for _ in range(5 - len(plando_bosses))]
else:
possible_bosses = [default_levels[level][6] for level in default_levels
if default_levels[level][6] not in plando_bosses]
for level in levels:
if levels[level][6] is None:
boss = world.random.choice(possible_bosses)
levels[level][6] = boss
possible_bosses.remove(boss)
else:
for level in levels:
if levels[level][6] is None:
levels[level][6] = default_levels[level][6]
for level in levels:
for stage in range(7):
assert levels[level][stage] is not None, "Level tried to be sent with a None stage, incorrect plando?"
return levels
def create_levels(world: "KDL3World") -> None:
menu = Region("Menu", world.player, world.multiworld)
level1 = Region("Grass Land", world.player, world.multiworld)
level2 = Region("Ripple Field", world.player, world.multiworld)
level3 = Region("Sand Canyon", world.player, world.multiworld)
level4 = Region("Cloudy Park", world.player, world.multiworld)
level5 = Region("Iceberg", world.player, world.multiworld)
level6 = Region("Hyper Zone", world.player, world.multiworld)
levels = {
1: level1,
2: level2,
3: level3,
4: level4,
5: level5,
}
level_shuffle = world.options.stage_shuffle.value
if level_shuffle != 0:
world.player_levels = generate_valid_levels(
world,
level_shuffle == 1,
level_shuffle == 2)
generate_rooms(world, False, levels)
level6.add_locations({LocationName.goals[world.options.goal]: None}, KDL3Location)
menu.connect(level1, "Start Game")
level1.connect(level2, "To Level 2")
level2.connect(level3, "To Level 3")
level3.connect(level4, "To Level 4")
level4.connect(level5, "To Level 5")
menu.connect(level6, "To Level 6") # put the connection on menu, since you can reach it before level 5 on fast goal
world.multiworld.regions += [menu, level1, level2, level3, level4, level5, level6]

577
worlds/kdl3/Rom.py Normal file
View File

@@ -0,0 +1,577 @@
import typing
from pkgutil import get_data
import Utils
from typing import Optional, TYPE_CHECKING
import hashlib
import os
import struct
import settings
from worlds.Files import APDeltaPatch
from .Aesthetics import get_palette_bytes, kirby_target_palettes, get_kirby_palette, gooey_target_palettes, \
get_gooey_palette
from .Compression import hal_decompress
import bsdiff4
if TYPE_CHECKING:
from . import KDL3World
KDL3UHASH = "201e7658f6194458a3869dde36bf8ec2"
KDL3JHASH = "b2f2d004ea640c3db66df958fce122b2"
level_pointers = {
0x770001: 0x0084,
0x770002: 0x009C,
0x770003: 0x00B8,
0x770004: 0x00D8,
0x770005: 0x0104,
0x770006: 0x0124,
0x770007: 0x014C,
0x770008: 0x0170,
0x770009: 0x0190,
0x77000A: 0x01B0,
0x77000B: 0x01E8,
0x77000C: 0x0218,
0x77000D: 0x024C,
0x77000E: 0x0270,
0x77000F: 0x02A0,
0x770010: 0x02C4,
0x770011: 0x02EC,
0x770012: 0x0314,
0x770013: 0x03CC,
0x770014: 0x0404,
0x770015: 0x042C,
0x770016: 0x044C,
0x770017: 0x0478,
0x770018: 0x049C,
0x770019: 0x04E4,
0x77001A: 0x0504,
0x77001B: 0x0530,
0x77001C: 0x0554,
0x77001D: 0x05A8,
0x77001E: 0x0640,
0x770200: 0x0148,
0x770201: 0x0248,
0x770202: 0x03C8,
0x770203: 0x04E0,
0x770204: 0x06A4,
0x770205: 0x06A8,
}
bb_bosses = {
0x770200: 0xED85F1,
0x770201: 0xF01360,
0x770202: 0xEDA3DF,
0x770203: 0xEDC2B9,
0x770204: 0xED7C3F,
0x770205: 0xEC29D2,
}
level_sprites = {
0x19B2C6: 1827,
0x1A195C: 1584,
0x19F6F3: 1679,
0x19DC8B: 1717,
0x197900: 1872
}
stage_tiles = {
0: [
0, 1, 2,
16, 17, 18,
32, 33, 34,
48, 49, 50
],
1: [
3, 4, 5,
19, 20, 21,
35, 36, 37,
51, 52, 53
],
2: [
6, 7, 8,
22, 23, 24,
38, 39, 40,
54, 55, 56
],
3: [
9, 10, 11,
25, 26, 27,
41, 42, 43,
57, 58, 59,
],
4: [
12, 13, 64,
28, 29, 65,
44, 45, 66,
60, 61, 67
],
5: [
14, 15, 68,
30, 31, 69,
46, 47, 70,
62, 63, 71
]
}
heart_star_address = 0x2D0000
heart_star_size = 456
consumable_address = 0x2F91DD
consumable_size = 698
stage_palettes = [0x60964, 0x60B64, 0x60D64, 0x60F64, 0x61164]
music_choices = [
2, # Boss 1
3, # Boss 2 (Unused)
4, # Boss 3 (Miniboss)
7, # Dedede
9, # Event 2 (used once)
10, # Field 1
11, # Field 2
12, # Field 3
13, # Field 4
14, # Field 5
15, # Field 6
16, # Field 7
17, # Field 8
18, # Field 9
19, # Field 10
20, # Field 11
21, # Field 12 (Gourmet Race)
23, # Dark Matter in the Hyper Zone
24, # Zero
25, # Level 1
26, # Level 2
27, # Level 4
28, # Level 3
29, # Heart Star Failed
30, # Level 5
31, # Minigame
38, # Animal Friend 1
39, # Animal Friend 2
40, # Animal Friend 3
]
# extra room pointers we don't want to track other than for music
room_pointers = [
3079990, # Zero
2983409, # BB Whispy
3150688, # BB Acro
2991071, # BB PonCon
2998969, # BB Ado
2980927, # BB Dedede
2894290 # BB Zero
]
enemy_remap = {
"Waddle Dee": 0,
"Bronto Burt": 2,
"Rocky": 3,
"Bobo": 5,
"Chilly": 6,
"Poppy Bros Jr.": 7,
"Sparky": 8,
"Polof": 9,
"Broom Hatter": 11,
"Cappy": 12,
"Bouncy": 13,
"Nruff": 15,
"Glunk": 16,
"Togezo": 18,
"Kabu": 19,
"Mony": 20,
"Blipper": 21,
"Squishy": 22,
"Gabon": 24,
"Oro": 25,
"Galbo": 26,
"Sir Kibble": 27,
"Nidoo": 28,
"Kany": 29,
"Sasuke": 30,
"Yaban": 32,
"Boten": 33,
"Coconut": 34,
"Doka": 35,
"Icicle": 36,
"Pteran": 39,
"Loud": 40,
"Como": 41,
"Klinko": 42,
"Babut": 43,
"Wappa": 44,
"Mariel": 45,
"Tick": 48,
"Apolo": 49,
"Popon Ball": 50,
"KeKe": 51,
"Magoo": 53,
"Raft Waddle Dee": 57,
"Madoo": 58,
"Corori": 60,
"Kapar": 67,
"Batamon": 68,
"Peran": 72,
"Bobin": 73,
"Mopoo": 74,
"Gansan": 75,
"Bukiset (Burning)": 76,
"Bukiset (Stone)": 77,
"Bukiset (Ice)": 78,
"Bukiset (Needle)": 79,
"Bukiset (Clean)": 80,
"Bukiset (Parasol)": 81,
"Bukiset (Spark)": 82,
"Bukiset (Cutter)": 83,
"Waddle Dee Drawing": 84,
"Bronto Burt Drawing": 85,
"Bouncy Drawing": 86,
"Kabu (Dekabu)": 87,
"Wapod": 88,
"Propeller": 89,
"Dogon": 90,
"Joe": 91
}
miniboss_remap = {
"Captain Stitch": 0,
"Yuki": 1,
"Blocky": 2,
"Jumper Shoot": 3,
"Boboo": 4,
"Haboki": 5
}
ability_remap = {
"No Ability": 0,
"Burning Ability": 1,
"Stone Ability": 2,
"Ice Ability": 3,
"Needle Ability": 4,
"Clean Ability": 5,
"Parasol Ability": 6,
"Spark Ability": 7,
"Cutter Ability": 8,
}
class RomData:
def __init__(self, file: str, name: typing.Optional[str] = None):
self.file = bytearray()
self.read_from_file(file)
self.name = name
def read_byte(self, offset: int):
return self.file[offset]
def read_bytes(self, offset: int, length: int):
return self.file[offset:offset + length]
def write_byte(self, offset: int, value: int):
self.file[offset] = value
def write_bytes(self, offset: int, values: typing.Sequence) -> None:
self.file[offset:offset + len(values)] = values
def write_to_file(self, file: str):
with open(file, 'wb') as outfile:
outfile.write(self.file)
def read_from_file(self, file: str):
with open(file, 'rb') as stream:
self.file = bytearray(stream.read())
def apply_patch(self, patch: bytes):
self.file = bytearray(bsdiff4.patch(bytes(self.file), patch))
def write_crc(self):
crc = (sum(self.file[:0x7FDC] + self.file[0x7FE0:]) + 0x01FE) & 0xFFFF
inv = crc ^ 0xFFFF
self.write_bytes(0x7FDC, [inv & 0xFF, (inv >> 8) & 0xFF, crc & 0xFF, (crc >> 8) & 0xFF])
def handle_level_sprites(stages, sprites, palettes):
palette_by_level = list()
for palette in palettes:
palette_by_level.extend(palette[10:16])
for i in range(5):
for j in range(6):
palettes[i][10 + j] = palette_by_level[stages[i][j] - 1]
palettes[i] = [x for palette in palettes[i] for x in palette]
tiles_by_level = list()
for spritesheet in sprites:
decompressed = hal_decompress(spritesheet)
tiles = [decompressed[i:i + 32] for i in range(0, 2304, 32)]
tiles_by_level.extend([[tiles[x] for x in stage_tiles[stage]] for stage in stage_tiles])
for world in range(5):
levels = [stages[world][x] - 1 for x in range(6)]
world_tiles: typing.List[typing.Optional[bytes]] = [None for _ in range(72)]
for i in range(6):
for x in range(12):
world_tiles[stage_tiles[i][x]] = tiles_by_level[levels[i]][x]
sprites[world] = list()
for tile in world_tiles:
sprites[world].extend(tile)
# insert our fake compression
sprites[world][0:0] = [0xe3, 0xff]
sprites[world][1026:1026] = [0xe3, 0xff]
sprites[world][2052:2052] = [0xe0, 0xff]
sprites[world].append(0xff)
return sprites, palettes
def write_heart_star_sprites(rom: RomData):
compressed = rom.read_bytes(heart_star_address, heart_star_size)
decompressed = hal_decompress(compressed)
patch = get_data(__name__, os.path.join("data", "APHeartStar.bsdiff4"))
patched = bytearray(bsdiff4.patch(decompressed, patch))
rom.write_bytes(0x1AF7DF, patched)
patched[0:0] = [0xE3, 0xFF]
patched.append(0xFF)
rom.write_bytes(0x1CD000, patched)
rom.write_bytes(0x3F0EBF, [0x00, 0xD0, 0x39])
def write_consumable_sprites(rom: RomData, consumables: bool, stars: bool):
compressed = rom.read_bytes(consumable_address, consumable_size)
decompressed = hal_decompress(compressed)
patched = bytearray(decompressed)
if consumables:
patch = get_data(__name__, os.path.join("data", "APConsumable.bsdiff4"))
patched = bytearray(bsdiff4.patch(bytes(patched), patch))
if stars:
patch = get_data(__name__, os.path.join("data", "APStars.bsdiff4"))
patched = bytearray(bsdiff4.patch(bytes(patched), patch))
patched[0:0] = [0xE3, 0xFF]
patched.append(0xFF)
rom.write_bytes(0x1CD500, patched)
rom.write_bytes(0x3F0DAE, [0x00, 0xD5, 0x39])
class KDL3DeltaPatch(APDeltaPatch):
hash = [KDL3UHASH, KDL3JHASH]
game = "Kirby's Dream Land 3"
patch_file_ending = ".apkdl3"
@classmethod
def get_source_data(cls) -> bytes:
return get_base_rom_bytes()
def patch(self, target: str):
super().patch(target)
rom = RomData(target)
target_language = rom.read_byte(0x3C020)
rom.write_byte(0x7FD9, target_language)
write_heart_star_sprites(rom)
if rom.read_bytes(0x3D014, 1)[0] > 0:
stages = [struct.unpack("HHHHHHH", rom.read_bytes(0x3D020 + x * 14, 14)) for x in range(5)]
palettes = [rom.read_bytes(full_pal, 512) for full_pal in stage_palettes]
palettes = [[palette[i:i + 32] for i in range(0, 512, 32)] for palette in palettes]
sprites = [rom.read_bytes(offset, level_sprites[offset]) for offset in level_sprites]
sprites, palettes = handle_level_sprites(stages, sprites, palettes)
for addr, palette in zip(stage_palettes, palettes):
rom.write_bytes(addr, palette)
for addr, level_sprite in zip([0x1CA000, 0x1CA920, 0x1CB230, 0x1CBB40, 0x1CC450], sprites):
rom.write_bytes(addr, level_sprite)
rom.write_bytes(0x460A, [0x00, 0xA0, 0x39, 0x20, 0xA9, 0x39, 0x30, 0xB2, 0x39, 0x40, 0xBB, 0x39,
0x50, 0xC4, 0x39])
write_consumable_sprites(rom, rom.read_byte(0x3D018) > 0, rom.read_byte(0x3D01A) > 0)
rom_name = rom.read_bytes(0x3C000, 21)
rom.write_bytes(0x7FC0, rom_name)
rom.write_crc()
rom.write_to_file(target)
def patch_rom(world: "KDL3World", rom: RomData):
rom.apply_patch(get_data(__name__, os.path.join("data", "kdl3_basepatch.bsdiff4")))
tiles = get_data(__name__, os.path.join("data", "APPauseIcons.dat"))
rom.write_bytes(0x3F000, tiles)
# Write open world patch
if world.options.open_world:
rom.write_bytes(0x143C7, [0xAD, 0xC1, 0x5A, 0xCD, 0xC1, 0x5A, ])
# changes the stage flag function to compare $5AC1 to $5AC1,
# always running the "new stage" function
# This has further checks present for bosses already, so we just
# need to handle regular stages
# write check for boss to be unlocked
if world.options.consumables:
# reroute maxim tomatoes to use the 1-UP function, then null out the function
rom.write_bytes(0x3002F, [0x37, 0x00])
rom.write_bytes(0x30037, [0xA9, 0x26, 0x00, # LDA #$0026
0x22, 0x27, 0xD9, 0x00, # JSL $00D927
0xA4, 0xD2, # LDY $D2
0x6B, # RTL
0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, # NOP #10
])
# stars handling is built into the rom, so no changes there
rooms = world.rooms
if world.options.music_shuffle > 0:
if world.options.music_shuffle == 1:
shuffled_music = music_choices.copy()
world.random.shuffle(shuffled_music)
music_map = dict(zip(music_choices, shuffled_music))
# Avoid putting star twinkle in the pool
music_map[5] = world.random.choice(music_choices)
# Heart Star music doesn't work on regular stages
music_map[8] = world.random.choice(music_choices)
for room in rooms:
room.music = music_map[room.music]
for room in room_pointers:
old_music = rom.read_byte(room + 2)
rom.write_byte(room + 2, music_map[old_music])
for i in range(5):
# level themes
old_music = rom.read_byte(0x133F2 + i)
rom.write_byte(0x133F2 + i, music_map[old_music])
# Zero
rom.write_byte(0x9AE79, music_map[0x18])
# Heart Star success and fail
rom.write_byte(0x4A388, music_map[0x08])
rom.write_byte(0x4A38D, music_map[0x1D])
elif world.options.music_shuffle == 2:
for room in rooms:
room.music = world.random.choice(music_choices)
for room in room_pointers:
rom.write_byte(room + 2, world.random.choice(music_choices))
for i in range(5):
# level themes
rom.write_byte(0x133F2 + i, world.random.choice(music_choices))
# Zero
rom.write_byte(0x9AE79, world.random.choice(music_choices))
# Heart Star success and fail
rom.write_byte(0x4A388, world.random.choice(music_choices))
rom.write_byte(0x4A38D, world.random.choice(music_choices))
for room in rooms:
room.patch(rom)
if world.options.virtual_console in [1, 3]:
# Flash Reduction
rom.write_byte(0x9AE68, 0x10)
rom.write_bytes(0x9AE8E, [0x08, 0x00, 0x22, 0x5D, 0xF7, 0x00, 0xA2, 0x08, ])
rom.write_byte(0x9AEA1, 0x08)
rom.write_byte(0x9AEC9, 0x01)
rom.write_bytes(0x9AED2, [0xA9, 0x1F])
rom.write_byte(0x9AEE1, 0x08)
if world.options.virtual_console in [2, 3]:
# Hyper Zone BB colors
rom.write_bytes(0x2C5E16, [0xEE, 0x1B, 0x18, 0x5B, 0xD3, 0x4A, 0xF4, 0x3B, ])
rom.write_bytes(0x2C8217, [0xFF, 0x1E, ])
# boss requirements
rom.write_bytes(0x3D000, struct.pack("HHHHH", world.boss_requirements[0], world.boss_requirements[1],
world.boss_requirements[2], world.boss_requirements[3],
world.boss_requirements[4]))
rom.write_bytes(0x3D00A, struct.pack("H", world.required_heart_stars if world.options.goal_speed == 1 else 0xFFFF))
rom.write_byte(0x3D00C, world.options.goal_speed.value)
rom.write_byte(0x3D00E, world.options.open_world.value)
rom.write_byte(0x3D010, world.options.death_link.value)
rom.write_byte(0x3D012, world.options.goal.value)
rom.write_byte(0x3D014, world.options.stage_shuffle.value)
rom.write_byte(0x3D016, world.options.ow_boss_requirement.value)
rom.write_byte(0x3D018, world.options.consumables.value)
rom.write_byte(0x3D01A, world.options.starsanity.value)
rom.write_byte(0x3D01C, world.options.gifting.value if world.multiworld.players > 1 else 0)
rom.write_byte(0x3D01E, world.options.strict_bosses.value)
# don't write gifting for solo game, since there's no one to send anything to
for level in world.player_levels:
for i in range(len(world.player_levels[level])):
rom.write_bytes(0x3F002E + ((level - 1) * 14) + (i * 2),
struct.pack("H", level_pointers[world.player_levels[level][i]]))
rom.write_bytes(0x3D020 + (level - 1) * 14 + (i * 2),
struct.pack("H", world.player_levels[level][i] & 0x00FFFF))
if (i == 0) or (i > 0 and i % 6 != 0):
rom.write_bytes(0x3D080 + (level - 1) * 12 + (i * 2),
struct.pack("H", (world.player_levels[level][i] & 0x00FFFF) % 6))
for i in range(6):
if world.boss_butch_bosses[i]:
rom.write_bytes(0x3F0000 + (level_pointers[0x770200 + i]), struct.pack("I", bb_bosses[0x770200 + i]))
# copy ability shuffle
if world.options.copy_ability_randomization.value > 0:
for enemy in world.copy_abilities:
if enemy in miniboss_remap:
rom.write_bytes(0xB417E + (miniboss_remap[enemy] << 1),
struct.pack("H", ability_remap[world.copy_abilities[enemy]]))
else:
rom.write_bytes(0xB3CAC + (enemy_remap[enemy] << 1),
struct.pack("H", ability_remap[world.copy_abilities[enemy]]))
# following only needs done on non-door rando
# incredibly lucky this follows the same order (including 5E == star block)
rom.write_byte(0x2F77EA, 0x5E + (ability_remap[world.copy_abilities["Sparky"]] << 1))
rom.write_byte(0x2F7811, 0x5E + (ability_remap[world.copy_abilities["Sparky"]] << 1))
rom.write_byte(0x2F9BC4, 0x5E + (ability_remap[world.copy_abilities["Blocky"]] << 1))
rom.write_byte(0x2F9BEB, 0x5E + (ability_remap[world.copy_abilities["Blocky"]] << 1))
rom.write_byte(0x2FAC06, 0x5E + (ability_remap[world.copy_abilities["Jumper Shoot"]] << 1))
rom.write_byte(0x2FAC2D, 0x5E + (ability_remap[world.copy_abilities["Jumper Shoot"]] << 1))
rom.write_byte(0x2F9E7B, 0x5E + (ability_remap[world.copy_abilities["Yuki"]] << 1))
rom.write_byte(0x2F9EA2, 0x5E + (ability_remap[world.copy_abilities["Yuki"]] << 1))
rom.write_byte(0x2FA951, 0x5E + (ability_remap[world.copy_abilities["Sir Kibble"]] << 1))
rom.write_byte(0x2FA978, 0x5E + (ability_remap[world.copy_abilities["Sir Kibble"]] << 1))
rom.write_byte(0x2FA132, 0x5E + (ability_remap[world.copy_abilities["Haboki"]] << 1))
rom.write_byte(0x2FA159, 0x5E + (ability_remap[world.copy_abilities["Haboki"]] << 1))
rom.write_byte(0x2FA3E8, 0x5E + (ability_remap[world.copy_abilities["Boboo"]] << 1))
rom.write_byte(0x2FA40F, 0x5E + (ability_remap[world.copy_abilities["Boboo"]] << 1))
rom.write_byte(0x2F90E2, 0x5E + (ability_remap[world.copy_abilities["Captain Stitch"]] << 1))
rom.write_byte(0x2F9109, 0x5E + (ability_remap[world.copy_abilities["Captain Stitch"]] << 1))
if world.options.copy_ability_randomization == 2:
for enemy in enemy_remap:
# we just won't include it for minibosses
rom.write_bytes(0xB3E40 + (enemy_remap[enemy] << 1), struct.pack("h", world.random.randint(-1, 2)))
# write jumping goal
rom.write_bytes(0x94F8, struct.pack("H", world.options.jumping_target))
rom.write_bytes(0x944E, struct.pack("H", world.options.jumping_target))
from Utils import __version__
rom.name = bytearray(
f'KDL3{__version__.replace(".", "")[0:3]}_{world.player}_{world.multiworld.seed:11}\0', 'utf8')[:21]
rom.name.extend([0] * (21 - len(rom.name)))
rom.write_bytes(0x3C000, rom.name)
rom.write_byte(0x3C020, world.options.game_language.value)
# handle palette
if world.options.kirby_flavor_preset.value != 0:
for addr in kirby_target_palettes:
target = kirby_target_palettes[addr]
palette = get_kirby_palette(world)
rom.write_bytes(addr, get_palette_bytes(palette, target[0], target[1], target[2]))
if world.options.gooey_flavor_preset.value != 0:
for addr in gooey_target_palettes:
target = gooey_target_palettes[addr]
palette = get_gooey_palette(world)
rom.write_bytes(addr, get_palette_bytes(palette, target[0], target[1], target[2]))
def get_base_rom_bytes() -> bytes:
rom_file: str = get_base_rom_path()
base_rom_bytes: Optional[bytes] = getattr(get_base_rom_bytes, "base_rom_bytes", None)
if not base_rom_bytes:
base_rom_bytes = bytes(Utils.read_snes_rom(open(rom_file, "rb")))
basemd5 = hashlib.md5()
basemd5.update(base_rom_bytes)
if basemd5.hexdigest() not in {KDL3UHASH, KDL3JHASH}:
raise Exception("Supplied Base Rom does not match known MD5 for US or JP release. "
"Get the correct game and version, then dump it")
get_base_rom_bytes.base_rom_bytes = base_rom_bytes
return base_rom_bytes
def get_base_rom_path(file_name: str = "") -> str:
options: settings.Settings = settings.get_settings()
if not file_name:
file_name = options["kdl3_options"]["rom_file"]
if not os.path.exists(file_name):
file_name = Utils.user_path(file_name)
return file_name

95
worlds/kdl3/Room.py Normal file
View File

@@ -0,0 +1,95 @@
import struct
import typing
from BaseClasses import Region, ItemClassification
if typing.TYPE_CHECKING:
from .Rom import RomData
animal_map = {
"Rick Spawn": 0,
"Kine Spawn": 1,
"Coo Spawn": 2,
"Nago Spawn": 3,
"ChuChu Spawn": 4,
"Pitch Spawn": 5
}
class KDL3Room(Region):
pointer: int = 0
level: int = 0
stage: int = 0
room: int = 0
music: int = 0
default_exits: typing.List[typing.Dict[str, typing.Union[int, typing.List[str]]]]
animal_pointers: typing.List[int]
enemies: typing.List[str]
entity_load: typing.List[typing.List[int]]
consumables: typing.List[typing.Dict[str, typing.Union[int, str]]]
def __init__(self, name, player, multiworld, hint, level, stage, room, pointer, music, default_exits,
animal_pointers, enemies, entity_load, consumables, consumable_pointer):
super().__init__(name, player, multiworld, hint)
self.level = level
self.stage = stage
self.room = room
self.pointer = pointer
self.music = music
self.default_exits = default_exits
self.animal_pointers = animal_pointers
self.enemies = enemies
self.entity_load = entity_load
self.consumables = consumables
self.consumable_pointer = consumable_pointer
def patch(self, rom: "RomData"):
rom.write_byte(self.pointer + 2, self.music)
animals = [x.item.name for x in self.locations if "Animal" in x.name]
if len(animals) > 0:
for current_animal, address in zip(animals, self.animal_pointers):
rom.write_byte(self.pointer + address + 7, animal_map[current_animal])
if self.multiworld.worlds[self.player].options.consumables:
load_len = len(self.entity_load)
for consumable in self.consumables:
location = next(x for x in self.locations if x.name == consumable["name"])
assert location.item
is_progression = location.item.classification & ItemClassification.progression
if load_len == 8:
# edge case, there is exactly 1 room with 8 entities and only 1 consumable among them
if not (any(x in self.entity_load for x in [[0, 22], [1, 22]])
and any(x in self.entity_load for x in [[2, 22], [3, 22]])):
replacement_target = self.entity_load.index(
next(x for x in self.entity_load if x in [[0, 22], [1, 22], [2, 22], [3, 22]]))
if is_progression:
vtype = 0
else:
vtype = 2
rom.write_byte(self.pointer + 88 + (replacement_target * 2), vtype)
self.entity_load[replacement_target] = [vtype, 22]
else:
if is_progression:
# we need to see if 1-ups are in our load list
if any(x not in self.entity_load for x in [[0, 22], [1, 22]]):
self.entity_load.append([0, 22])
else:
if any(x not in self.entity_load for x in [[2, 22], [3, 22]]):
# edge case: if (1, 22) is in, we need to load (3, 22) instead
if [1, 22] in self.entity_load:
self.entity_load.append([3, 22])
else:
self.entity_load.append([2, 22])
if load_len < len(self.entity_load):
rom.write_bytes(self.pointer + 88 + (load_len * 2), bytes(self.entity_load[load_len]))
rom.write_bytes(self.pointer + 104 + (load_len * 2),
bytes(struct.pack("H", self.consumable_pointer)))
if is_progression:
if [1, 22] in self.entity_load:
vtype = 1
else:
vtype = 0
else:
if [3, 22] in self.entity_load:
vtype = 3
else:
vtype = 2
rom.write_byte(self.pointer + consumable["pointer"] + 7, vtype)

332
worlds/kdl3/Rules.py Normal file
View File

@@ -0,0 +1,332 @@
from worlds.generic.Rules import set_rule, add_rule
from .Names import LocationName, EnemyAbilities
from .Locations import location_table
from .Options import GoalSpeed
import typing
if typing.TYPE_CHECKING:
from . import KDL3World
from BaseClasses import CollectionState
def can_reach_boss(state: "CollectionState", player: int, level: int, open_world: int,
ow_boss_req: int, player_levels: typing.Dict[int, typing.Dict[int, int]]):
if open_world:
return state.has(f"{LocationName.level_names_inverse[level]} - Stage Completion", player, ow_boss_req)
else:
return state.can_reach(location_table[player_levels[level][5]], "Location", player)
def can_reach_rick(state: "CollectionState", player: int) -> bool:
return state.has("Rick", player) and state.has("Rick Spawn", player)
def can_reach_kine(state: "CollectionState", player: int) -> bool:
return state.has("Kine", player) and state.has("Kine Spawn", player)
def can_reach_coo(state: "CollectionState", player: int) -> bool:
return state.has("Coo", player) and state.has("Coo Spawn", player)
def can_reach_nago(state: "CollectionState", player: int) -> bool:
return state.has("Nago", player) and state.has("Nago Spawn", player)
def can_reach_chuchu(state: "CollectionState", player: int) -> bool:
return state.has("ChuChu", player) and state.has("ChuChu Spawn", player)
def can_reach_pitch(state: "CollectionState", player: int) -> bool:
return state.has("Pitch", player) and state.has("Pitch Spawn", player)
def can_reach_burning(state: "CollectionState", player: int) -> bool:
return state.has("Burning", player) and state.has("Burning Ability", player)
def can_reach_stone(state: "CollectionState", player: int) -> bool:
return state.has("Stone", player) and state.has("Stone Ability", player)
def can_reach_ice(state: "CollectionState", player: int) -> bool:
return state.has("Ice", player) and state.has("Ice Ability", player)
def can_reach_needle(state: "CollectionState", player: int) -> bool:
return state.has("Needle", player) and state.has("Needle Ability", player)
def can_reach_clean(state: "CollectionState", player: int) -> bool:
return state.has("Clean", player) and state.has("Clean Ability", player)
def can_reach_parasol(state: "CollectionState", player: int) -> bool:
return state.has("Parasol", player) and state.has("Parasol Ability", player)
def can_reach_spark(state: "CollectionState", player: int) -> bool:
return state.has("Spark", player) and state.has("Spark Ability", player)
def can_reach_cutter(state: "CollectionState", player: int) -> bool:
return state.has("Cutter", player) and state.has("Cutter Ability", player)
ability_map: typing.Dict[str, typing.Callable[["CollectionState", int], bool]] = {
"No Ability": lambda state, player: True,
"Burning Ability": can_reach_burning,
"Stone Ability": can_reach_stone,
"Ice Ability": can_reach_ice,
"Needle Ability": can_reach_needle,
"Clean Ability": can_reach_clean,
"Parasol Ability": can_reach_parasol,
"Spark Ability": can_reach_spark,
"Cutter Ability": can_reach_cutter,
}
def can_assemble_rob(state: "CollectionState", player: int, copy_abilities: typing.Dict[str, str]):
# check animal requirements
if not (can_reach_coo(state, player) and can_reach_kine(state, player)):
return False
for abilities, bukisets in EnemyAbilities.enemy_restrictive[1:5]:
iterator = iter(x for x in bukisets if copy_abilities[x] in abilities)
target_bukiset = next(iterator, None)
can_reach = False
while target_bukiset is not None:
can_reach = can_reach | ability_map[copy_abilities[target_bukiset]](state, player)
target_bukiset = next(iterator, None)
if not can_reach:
return False
# now the known needed abilities
return can_reach_parasol(state, player) and can_reach_stone(state, player)
def can_fix_angel_wings(state: "CollectionState", player: int, copy_abilities: typing.Dict[str, str]):
can_reach = True
for enemy in {"Sparky", "Blocky", "Jumper Shoot", "Yuki", "Sir Kibble", "Haboki", "Boboo", "Captain Stitch"}:
can_reach = can_reach & ability_map[copy_abilities[enemy]](state, player)
return can_reach
def set_rules(world: "KDL3World") -> None:
# Level 1
set_rule(world.multiworld.get_location(LocationName.grass_land_muchi, world.player),
lambda state: can_reach_chuchu(state, world.player))
set_rule(world.multiworld.get_location(LocationName.grass_land_chao, world.player),
lambda state: can_reach_stone(state, world.player))
set_rule(world.multiworld.get_location(LocationName.grass_land_mine, world.player),
lambda state: can_reach_kine(state, world.player))
# Level 2
set_rule(world.multiworld.get_location(LocationName.ripple_field_5, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(LocationName.ripple_field_kamuribana, world.player),
lambda state: can_reach_pitch(state, world.player) and can_reach_clean(state, world.player))
set_rule(world.multiworld.get_location(LocationName.ripple_field_bakasa, world.player),
lambda state: can_reach_kine(state, world.player) and can_reach_parasol(state, world.player))
set_rule(world.multiworld.get_location(LocationName.ripple_field_toad, world.player),
lambda state: can_reach_needle(state, world.player))
set_rule(world.multiworld.get_location(LocationName.ripple_field_mama_pitch, world.player),
lambda state: (can_reach_pitch(state, world.player) and
can_reach_kine(state, world.player) and
can_reach_burning(state, world.player) and
can_reach_stone(state, world.player)))
# Level 3
set_rule(world.multiworld.get_location(LocationName.sand_canyon_5, world.player),
lambda state: can_reach_cutter(state, world.player))
set_rule(world.multiworld.get_location(LocationName.sand_canyon_auntie, world.player),
lambda state: can_reach_clean(state, world.player))
set_rule(world.multiworld.get_location(LocationName.sand_canyon_nyupun, world.player),
lambda state: can_reach_chuchu(state, world.player) and can_reach_cutter(state, world.player))
set_rule(world.multiworld.get_location(LocationName.sand_canyon_rob, world.player),
lambda state: can_assemble_rob(state, world.player, world.copy_abilities)
)
# Level 4
set_rule(world.multiworld.get_location(LocationName.cloudy_park_hibanamodoki, world.player),
lambda state: can_reach_coo(state, world.player) and can_reach_clean(state, world.player))
set_rule(world.multiworld.get_location(LocationName.cloudy_park_piyokeko, world.player),
lambda state: can_reach_needle(state, world.player))
set_rule(world.multiworld.get_location(LocationName.cloudy_park_mikarin, world.player),
lambda state: can_reach_coo(state, world.player))
set_rule(world.multiworld.get_location(LocationName.cloudy_park_pick, world.player),
lambda state: can_reach_rick(state, world.player))
# Level 5
set_rule(world.multiworld.get_location(LocationName.iceberg_4, world.player),
lambda state: can_reach_burning(state, world.player))
set_rule(world.multiworld.get_location(LocationName.iceberg_kogoesou, world.player),
lambda state: can_reach_burning(state, world.player))
set_rule(world.multiworld.get_location(LocationName.iceberg_samus, world.player),
lambda state: can_reach_ice(state, world.player))
set_rule(world.multiworld.get_location(LocationName.iceberg_name, world.player),
lambda state: (can_reach_coo(state, world.player) and
can_reach_burning(state, world.player) and
can_reach_chuchu(state, world.player)))
# ChuChu is guaranteed here, but we use this for consistency
set_rule(world.multiworld.get_location(LocationName.iceberg_shiro, world.player),
lambda state: can_reach_nago(state, world.player))
set_rule(world.multiworld.get_location(LocationName.iceberg_angel, world.player),
lambda state: can_fix_angel_wings(state, world.player, world.copy_abilities))
# Consumables
if world.options.consumables:
set_rule(world.multiworld.get_location(LocationName.grass_land_1_u1, world.player),
lambda state: can_reach_parasol(state, world.player))
set_rule(world.multiworld.get_location(LocationName.grass_land_1_m1, world.player),
lambda state: can_reach_spark(state, world.player))
set_rule(world.multiworld.get_location(LocationName.grass_land_2_u1, world.player),
lambda state: can_reach_needle(state, world.player))
set_rule(world.multiworld.get_location(LocationName.ripple_field_2_u1, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(LocationName.ripple_field_2_m1, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(LocationName.ripple_field_3_u1, world.player),
lambda state: can_reach_cutter(state, world.player) or can_reach_spark(state, world.player))
set_rule(world.multiworld.get_location(LocationName.ripple_field_4_u1, world.player),
lambda state: can_reach_stone(state, world.player))
set_rule(world.multiworld.get_location(LocationName.ripple_field_4_m2, world.player),
lambda state: can_reach_stone(state, world.player))
set_rule(world.multiworld.get_location(LocationName.ripple_field_5_m1, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(LocationName.ripple_field_5_u1, world.player),
lambda state: (can_reach_kine(state, world.player) and
can_reach_burning(state, world.player) and
can_reach_stone(state, world.player)))
set_rule(world.multiworld.get_location(LocationName.ripple_field_5_m2, world.player),
lambda state: (can_reach_kine(state, world.player) and
can_reach_burning(state, world.player) and
can_reach_stone(state, world.player)))
set_rule(world.multiworld.get_location(LocationName.sand_canyon_4_u1, world.player),
lambda state: can_reach_clean(state, world.player))
set_rule(world.multiworld.get_location(LocationName.sand_canyon_4_m2, world.player),
lambda state: can_reach_needle(state, world.player))
set_rule(world.multiworld.get_location(LocationName.sand_canyon_5_u2, world.player),
lambda state: can_reach_ice(state, world.player) and
(can_reach_rick(state, world.player) or can_reach_coo(state, world.player)))
set_rule(world.multiworld.get_location(LocationName.sand_canyon_5_u3, world.player),
lambda state: can_reach_ice(state, world.player) and
(can_reach_rick(state, world.player) or can_reach_coo(state, world.player)))
set_rule(world.multiworld.get_location(LocationName.sand_canyon_5_u4, world.player),
lambda state: can_reach_ice(state, world.player) and
(can_reach_rick(state, world.player) or can_reach_coo(state, world.player)))
set_rule(world.multiworld.get_location(LocationName.cloudy_park_6_u1, world.player),
lambda state: can_reach_cutter(state, world.player))
if world.options.starsanity:
# ranges are our friend
for i in range(7, 11):
set_rule(world.multiworld.get_location(f"Grass Land 1 - Star {i}", world.player),
lambda state: can_reach_cutter(state, world.player))
for i in range(11, 14):
set_rule(world.multiworld.get_location(f"Grass Land 1 - Star {i}", world.player),
lambda state: can_reach_parasol(state, world.player))
for i in [1, 3, 4, 9, 10]:
set_rule(world.multiworld.get_location(f"Grass Land 2 - Star {i}", world.player),
lambda state: can_reach_stone(state, world.player))
set_rule(world.multiworld.get_location("Grass Land 2 - Star 2", world.player),
lambda state: can_reach_burning(state, world.player))
set_rule(world.multiworld.get_location("Ripple Field 2 - Star 17", world.player),
lambda state: can_reach_kine(state, world.player))
for i in range(41, 43):
# any star past this point also needs kine, but so does the exit
set_rule(world.multiworld.get_location(f"Ripple Field 5 - Star {i}", world.player),
lambda state: can_reach_kine(state, world.player))
for i in range(46, 49):
# also requires kine, but only for access from the prior room
set_rule(world.multiworld.get_location(f"Ripple Field 5 - Star {i}", world.player),
lambda state: can_reach_burning(state, world.player) and can_reach_stone(state, world.player))
for i in range(12, 18):
set_rule(world.multiworld.get_location(f"Sand Canyon 5 - Star {i}", world.player),
lambda state: can_reach_ice(state, world.player) and
(can_reach_rick(state, world.player) or can_reach_coo(state, world.player)))
for i in range(21, 23):
set_rule(world.multiworld.get_location(f"Sand Canyon 5 - Star {i}", world.player),
lambda state: can_reach_chuchu(state, world.player))
for r in [range(19, 21), range(23, 31)]:
for i in r:
set_rule(world.multiworld.get_location(f"Sand Canyon 5 - Star {i}", world.player),
lambda state: can_reach_clean(state, world.player))
for i in range(31, 41):
set_rule(world.multiworld.get_location(f"Sand Canyon 5 - Star {i}", world.player),
lambda state: can_reach_burning(state, world.player))
for r in [range(1, 31), range(44, 51)]:
for i in r:
set_rule(world.multiworld.get_location(f"Cloudy Park 4 - Star {i}", world.player),
lambda state: can_reach_clean(state, world.player))
for i in [18, *list(range(20, 25))]:
set_rule(world.multiworld.get_location(f"Cloudy Park 6 - Star {i}", world.player),
lambda state: can_reach_ice(state, world.player))
for i in [19, *list(range(25, 30))]:
set_rule(world.multiworld.get_location(f"Cloudy Park 6 - Star {i}", world.player),
lambda state: can_reach_ice(state, world.player))
# copy ability access edge cases
# Kirby cannot eat enemies fully submerged in water. Vast majority of cases, the enemy can be brought to the surface
# and eaten by inhaling while falling on top of them
set_rule(world.multiworld.get_location(EnemyAbilities.Ripple_Field_2_E3, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(EnemyAbilities.Ripple_Field_3_E6, world.player),
lambda state: can_reach_kine(state, world.player))
# Ripple Field 4 E5, E7, and E8 are doable, but too strict to leave in logic
set_rule(world.multiworld.get_location(EnemyAbilities.Ripple_Field_4_E5, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(EnemyAbilities.Ripple_Field_4_E7, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(EnemyAbilities.Ripple_Field_4_E8, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(EnemyAbilities.Ripple_Field_5_E1, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(EnemyAbilities.Ripple_Field_5_E2, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(EnemyAbilities.Ripple_Field_5_E3, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(EnemyAbilities.Ripple_Field_5_E4, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(EnemyAbilities.Sand_Canyon_4_E7, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(EnemyAbilities.Sand_Canyon_4_E8, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(EnemyAbilities.Sand_Canyon_4_E9, world.player),
lambda state: can_reach_kine(state, world.player))
set_rule(world.multiworld.get_location(EnemyAbilities.Sand_Canyon_4_E10, world.player),
lambda state: can_reach_kine(state, world.player))
for boss_flag, purification, i in zip(["Level 1 Boss - Purified", "Level 2 Boss - Purified",
"Level 3 Boss - Purified", "Level 4 Boss - Purified",
"Level 5 Boss - Purified"],
[LocationName.grass_land_whispy, LocationName.ripple_field_acro,
LocationName.sand_canyon_poncon, LocationName.cloudy_park_ado,
LocationName.iceberg_dedede],
range(1, 6)):
set_rule(world.multiworld.get_location(boss_flag, world.player),
lambda state, i=i: (state.has("Heart Star", world.player, world.boss_requirements[i - 1])
and can_reach_boss(state, world.player, i,
world.options.open_world.value,
world.options.ow_boss_requirement.value,
world.player_levels)))
set_rule(world.multiworld.get_location(purification, world.player),
lambda state, i=i: (state.has("Heart Star", world.player, world.boss_requirements[i - 1])
and can_reach_boss(state, world.player, i,
world.options.open_world.value,
world.options.ow_boss_requirement.value,
world.player_levels)))
set_rule(world.multiworld.get_entrance("To Level 6", world.player),
lambda state: state.has("Heart Star", world.player, world.required_heart_stars))
for level in range(2, 6):
set_rule(world.multiworld.get_entrance(f"To Level {level}", world.player),
lambda state, i=level: state.has(f"Level {i - 1} Boss Defeated", world.player))
if world.options.strict_bosses:
for level in range(2, 6):
add_rule(world.multiworld.get_entrance(f"To Level {level}", world.player),
lambda state, i=level: state.has(f"Level {i - 1} Boss Purified", world.player))
if world.options.goal_speed == GoalSpeed.option_normal:
add_rule(world.multiworld.get_entrance("To Level 6", world.player),
lambda state: state.has_all(["Level 1 Boss Purified", "Level 2 Boss Purified", "Level 3 Boss Purified",
"Level 4 Boss Purified", "Level 5 Boss Purified"], world.player))

350
worlds/kdl3/__init__.py Normal file
View File

@@ -0,0 +1,350 @@
import logging
import typing
from BaseClasses import Tutorial, ItemClassification, MultiWorld
from Fill import fill_restrictive
from Options import PerGameCommonOptions
from worlds.AutoWorld import World, WebWorld
from .Items import item_table, item_names, copy_ability_table, animal_friend_table, filler_item_weights, KDL3Item, \
trap_item_table, copy_ability_access_table, star_item_weights, total_filler_weights
from .Locations import location_table, KDL3Location, level_consumables, consumable_locations, star_locations
from .Names.AnimalFriendSpawns import animal_friend_spawns
from .Names.EnemyAbilities import vanilla_enemies, enemy_mapping, enemy_restrictive
from .Regions import create_levels, default_levels
from .Options import KDL3Options
from .Presets import kdl3_options_presets
from .Names import LocationName
from .Room import KDL3Room
from .Rules import set_rules
from .Rom import KDL3DeltaPatch, get_base_rom_path, RomData, patch_rom, KDL3JHASH, KDL3UHASH
from .Client import KDL3SNIClient
from typing import Dict, TextIO, Optional, List
import os
import math
import threading
import base64
import settings
logger = logging.getLogger("Kirby's Dream Land 3")
class KDL3Settings(settings.Group):
class RomFile(settings.SNESRomPath):
"""File name of the KDL3 JP or EN rom"""
description = "Kirby's Dream Land 3 ROM File"
copy_to = "Kirby's Dream Land 3.sfc"
md5s = [KDL3JHASH, KDL3UHASH]
rom_file: RomFile = RomFile(RomFile.copy_to)
class KDL3WebWorld(WebWorld):
theme = "partyTime"
tutorials = [
Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the Kirby's Dream Land 3 randomizer connected to an Archipelago Multiworld.",
"English",
"setup_en.md",
"setup/en",
["Silvris"]
)
]
options_presets = kdl3_options_presets
class KDL3World(World):
"""
Join Kirby and his Animal Friends on an adventure to collect Heart Stars and drive Dark Matter away from Dream Land!
"""
game = "Kirby's Dream Land 3"
options_dataclass: typing.ClassVar[typing.Type[PerGameCommonOptions]] = KDL3Options
options: KDL3Options
item_name_to_id = {item: item_table[item].code for item in item_table}
location_name_to_id = {location_table[location]: location for location in location_table}
item_name_groups = item_names
web = KDL3WebWorld()
settings: typing.ClassVar[KDL3Settings]
def __init__(self, multiworld: MultiWorld, player: int):
self.rom_name = None
self.rom_name_available_event = threading.Event()
super().__init__(multiworld, player)
self.copy_abilities: Dict[str, str] = vanilla_enemies.copy()
self.required_heart_stars: int = 0 # we fill this during create_items
self.boss_requirements: Dict[int, int] = dict()
self.player_levels = default_levels.copy()
self.stage_shuffle_enabled = False
self.boss_butch_bosses: List[Optional[bool]] = list()
self.rooms: Optional[List[KDL3Room]] = None
@classmethod
def stage_assert_generate(cls, multiworld: MultiWorld) -> None:
rom_file: str = get_base_rom_path()
if not os.path.exists(rom_file):
raise FileNotFoundError(f"Could not find base ROM for {cls.game}: {rom_file}")
create_regions = create_levels
def create_item(self, name: str, force_non_progression=False) -> KDL3Item:
item = item_table[name]
classification = ItemClassification.filler
if item.progression and not force_non_progression:
classification = ItemClassification.progression_skip_balancing \
if item.skip_balancing else ItemClassification.progression
elif item.trap:
classification = ItemClassification.trap
return KDL3Item(name, classification, item.code, self.player)
def get_filler_item_name(self, include_stars=True) -> str:
if include_stars:
return self.random.choices(list(total_filler_weights.keys()),
weights=list(total_filler_weights.values()))[0]
return self.random.choices(list(filler_item_weights.keys()),
weights=list(filler_item_weights.values()))[0]
def get_trap_item_name(self) -> str:
return self.random.choices(["Gooey Bag", "Slowness", "Eject Ability"],
weights=[self.options.gooey_trap_weight.value,
self.options.slow_trap_weight.value,
self.options.ability_trap_weight.value])[0]
def get_restrictive_copy_ability_placement(self, copy_ability: str, enemies_to_set: typing.List[str],
level: int, stage: int):
valid_rooms = [room for room in self.rooms if (room.level < level)
or (room.level == level and room.stage < stage)] # leave out the stage in question to avoid edge
valid_enemies = set()
for room in valid_rooms:
valid_enemies.update(room.enemies)
placed_enemies = [enemy for enemy in valid_enemies if enemy not in enemies_to_set]
if any(self.copy_abilities[enemy] == copy_ability for enemy in placed_enemies):
return None # a valid enemy got placed by a more restrictive placement
return self.random.choice(sorted([enemy for enemy in valid_enemies if enemy not in placed_enemies]))
def pre_fill(self) -> None:
if self.options.copy_ability_randomization:
# randomize copy abilities
valid_abilities = list(copy_ability_access_table.keys())
enemies_to_set = list(self.copy_abilities.keys())
# now for the edge cases
for abilities, enemies in enemy_restrictive:
available_enemies = list()
for enemy in enemies:
if enemy not in enemies_to_set:
if self.copy_abilities[enemy] in abilities:
break
else:
available_enemies.append(enemy)
else:
chosen_enemy = self.random.choice(available_enemies)
chosen_ability = self.random.choice(abilities)
self.copy_abilities[chosen_enemy] = chosen_ability
enemies_to_set.remove(chosen_enemy)
# two less restrictive ones, we need to ensure Cutter and Burning appear before their required stages
sand_canyon_5 = self.get_region("Sand Canyon 5 - 9")
# this is primarily for typing, but if this ever hits it's fine to crash
assert isinstance(sand_canyon_5, KDL3Room)
cutter_enemy = self.get_restrictive_copy_ability_placement("Cutter Ability", enemies_to_set,
sand_canyon_5.level, sand_canyon_5.stage)
if cutter_enemy:
self.copy_abilities[cutter_enemy] = "Cutter Ability"
enemies_to_set.remove(cutter_enemy)
iceberg_4 = self.get_region("Iceberg 4 - 7")
# this is primarily for typing, but if this ever hits it's fine to crash
assert isinstance(iceberg_4, KDL3Room)
burning_enemy = self.get_restrictive_copy_ability_placement("Burning Ability", enemies_to_set,
iceberg_4.level, iceberg_4.stage)
if burning_enemy:
self.copy_abilities[burning_enemy] = "Burning Ability"
enemies_to_set.remove(burning_enemy)
# place remaining
for enemy in enemies_to_set:
self.copy_abilities[enemy] = self.random.choice(valid_abilities)
for enemy in enemy_mapping:
self.multiworld.get_location(enemy, self.player) \
.place_locked_item(self.create_item(self.copy_abilities[enemy_mapping[enemy]]))
# fill animals
if self.options.animal_randomization != 0:
spawns = [animal for animal in animal_friend_spawns.keys() if
animal not in ["Ripple Field 5 - Animal 2", "Sand Canyon 6 - Animal 1", "Iceberg 4 - Animal 1"]]
self.multiworld.get_location("Iceberg 4 - Animal 1", self.player) \
.place_locked_item(self.create_item("ChuChu Spawn"))
# Not having ChuChu here makes the room impossible (since only she has vertical burning)
self.multiworld.get_location("Ripple Field 5 - Animal 2", self.player) \
.place_locked_item(self.create_item("Pitch Spawn"))
guaranteed_animal = self.random.choice(["Kine Spawn", "Coo Spawn"])
self.multiworld.get_location("Sand Canyon 6 - Animal 1", self.player) \
.place_locked_item(self.create_item(guaranteed_animal))
# Ripple Field 5 - Animal 2 needs to be Pitch to ensure accessibility on non-door rando
if self.options.animal_randomization == 1:
animal_pool = [animal_friend_spawns[spawn] for spawn in animal_friend_spawns
if spawn not in ["Ripple Field 5 - Animal 2", "Sand Canyon 6 - Animal 1",
"Iceberg 4 - Animal 1"]]
else:
animal_base = ["Rick Spawn", "Kine Spawn", "Coo Spawn", "Nago Spawn", "ChuChu Spawn", "Pitch Spawn"]
animal_pool = [self.random.choice(animal_base)
for _ in range(len(animal_friend_spawns) - 9)]
# have to guarantee one of each animal
animal_pool.extend(animal_base)
if guaranteed_animal == "Kine Spawn":
animal_pool.append("Coo Spawn")
else:
animal_pool.append("Kine Spawn")
locations = [self.multiworld.get_location(spawn, self.player) for spawn in spawns]
items = [self.create_item(animal) for animal in animal_pool]
allstate = self.multiworld.get_all_state(False)
fill_restrictive(self.multiworld, allstate, locations, items, True, True)
else:
animal_friends = animal_friend_spawns.copy()
for animal in animal_friends:
self.multiworld.get_location(animal, self.player) \
.place_locked_item(self.create_item(animal_friends[animal]))
def create_items(self) -> None:
itempool = []
itempool.extend([self.create_item(name) for name in copy_ability_table])
itempool.extend([self.create_item(name) for name in animal_friend_table])
required_percentage = self.options.heart_stars_required / 100.0
remaining_items = len(location_table) - len(itempool)
if not self.options.consumables:
remaining_items -= len(consumable_locations)
remaining_items -= len(star_locations)
if self.options.starsanity:
# star fill, keep consumable pool locked to consumable and fill 767 stars specifically
star_items = list(star_item_weights.keys())
star_weights = list(star_item_weights.values())
itempool.extend([self.create_item(item) for item in self.random.choices(star_items, weights=star_weights,
k=767)])
total_heart_stars = self.options.total_heart_stars
# ensure at least 1 heart star required per world
required_heart_stars = max(int(total_heart_stars * required_percentage), 5)
filler_items = total_heart_stars - required_heart_stars
filler_amount = math.floor(filler_items * (self.options.filler_percentage / 100.0))
trap_amount = math.floor(filler_amount * (self.options.trap_percentage / 100.0))
filler_amount -= trap_amount
non_required_heart_stars = filler_items - filler_amount - trap_amount
self.required_heart_stars = required_heart_stars
# handle boss requirements here
requirements = [required_heart_stars]
quotient = required_heart_stars // 5 # since we set the last manually, we can afford imperfect rounding
if self.options.boss_requirement_random:
for i in range(1, 5):
if self.options.strict_bosses:
max_stars = quotient * i
else:
max_stars = required_heart_stars
requirements.insert(i, self.random.randint(
min(1, max_stars), max_stars))
if self.options.strict_bosses:
requirements.sort()
else:
self.random.shuffle(requirements)
else:
for i in range(1, 5):
requirements.insert(i - 1, quotient * i)
self.boss_requirements = requirements
itempool.extend([self.create_item("Heart Star") for _ in range(required_heart_stars)])
itempool.extend([self.create_item(self.get_filler_item_name(False))
for _ in range(filler_amount + (remaining_items - total_heart_stars))])
itempool.extend([self.create_item(self.get_trap_item_name())
for _ in range(trap_amount)])
itempool.extend([self.create_item("Heart Star", True) for _ in range(non_required_heart_stars)])
self.multiworld.itempool += itempool
if self.options.open_world:
for level in self.player_levels:
for stage in range(0, 6):
self.multiworld.get_location(location_table[self.player_levels[level][stage]]
.replace("Complete", "Stage Completion"), self.player) \
.place_locked_item(KDL3Item(
f"{LocationName.level_names_inverse[level]} - Stage Completion",
ItemClassification.progression, None, self.player))
set_rules = set_rules
def generate_basic(self) -> None:
self.stage_shuffle_enabled = self.options.stage_shuffle > 0
goal = self.options.goal
goal_location = self.multiworld.get_location(LocationName.goals[goal], self.player)
goal_location.place_locked_item(KDL3Item("Love-Love Rod", ItemClassification.progression, None, self.player))
for level in range(1, 6):
self.multiworld.get_location(f"Level {level} Boss - Defeated", self.player) \
.place_locked_item(
KDL3Item(f"Level {level} Boss Defeated", ItemClassification.progression, None, self.player))
self.multiworld.get_location(f"Level {level} Boss - Purified", self.player) \
.place_locked_item(
KDL3Item(f"Level {level} Boss Purified", ItemClassification.progression, None, self.player))
self.multiworld.completion_condition[self.player] = lambda state: state.has("Love-Love Rod", self.player)
# this can technically be done at any point before generate_output
if self.options.allow_bb:
if self.options.allow_bb == self.options.allow_bb.option_enforced:
self.boss_butch_bosses = [True for _ in range(6)]
else:
self.boss_butch_bosses = [self.random.choice([True, False]) for _ in range(6)]
def generate_output(self, output_directory: str):
rom_path = ""
try:
rom = RomData(get_base_rom_path())
patch_rom(self, rom)
rom_path = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.sfc")
rom.write_to_file(rom_path)
self.rom_name = rom.name
patch = KDL3DeltaPatch(os.path.splitext(rom_path)[0] + KDL3DeltaPatch.patch_file_ending, player=self.player,
player_name=self.multiworld.player_name[self.player], patched_path=rom_path)
patch.write()
except Exception:
raise
finally:
self.rom_name_available_event.set() # make sure threading continues and errors are collected
if os.path.exists(rom_path):
os.unlink(rom_path)
def modify_multidata(self, multidata: dict):
# wait for self.rom_name to be available.
self.rom_name_available_event.wait()
rom_name = getattr(self, "rom_name", None)
# we skip in case of error, so that the original error in the output thread is the one that gets raised
if rom_name:
new_name = base64.b64encode(bytes(self.rom_name)).decode()
multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]]
def write_spoiler(self, spoiler_handle: TextIO) -> None:
if self.stage_shuffle_enabled:
spoiler_handle.write(f"\nLevel Layout ({self.multiworld.get_player_name(self.player)}):\n")
for level in LocationName.level_names:
for stage, i in zip(self.player_levels[LocationName.level_names[level]], range(1, 7)):
spoiler_handle.write(f"{level} {i}: {location_table[stage].replace(' - Complete', '')}\n")
if self.options.animal_randomization:
spoiler_handle.write(f"\nAnimal Friends ({self.multiworld.get_player_name(self.player)}):\n")
for level in self.player_levels:
for stage in range(6):
rooms = [room for room in self.rooms if room.level == level and room.stage == stage]
animals = []
for room in rooms:
animals.extend([location.item.name.replace(" Spawn", "")
for location in room.locations if "Animal" in location.name])
spoiler_handle.write(f"{location_table[self.player_levels[level][stage]].replace(' - Complete','')}"
f": {', '.join(animals)}\n")
if self.options.copy_ability_randomization:
spoiler_handle.write(f"\nCopy Abilities ({self.multiworld.get_player_name(self.player)}):\n")
for enemy in self.copy_abilities:
spoiler_handle.write(f"{enemy}: {self.copy_abilities[enemy].replace('No Ability', 'None').replace(' Ability', '')}\n")
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]):
if self.stage_shuffle_enabled:
regions = {LocationName.level_names[level]: level for level in LocationName.level_names}
level_hint_data = {}
for level in regions:
for stage in range(7):
stage_name = self.multiworld.get_location(self.location_id_to_name[self.player_levels[level][stage]],
self.player).name.replace(" - Complete", "")
stage_regions = [room for room in self.rooms if stage_name in room.name]
for region in stage_regions:
for location in [location for location in region.locations if location.address]:
level_hint_data[location.address] = f"{regions[level]} {stage + 1 if stage < 6 else 'Boss'}"
hint_data[self.player] = level_hint_data

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +1,38 @@
# Kirby's Dream Land 3
## Where is the settings page?
The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
Kirby will be unable to absorb Copy Abilities and meet up with his animal friends until they are sent to him. Items such
as Heart Stars, 1-Ups, and Invincibility Candy will be shuffled into the pool for Kirby to receive.
## What is considered a location check in Kirby's Dream Land 3?
- Completing a stage for the first time
- Completing the given task of a stage and receiving a Heart Star
- Purifying a boss after acquiring a certain number of Heart Stars
(indicated by their portrait flashing in the level select)
- If enabled, 1-Ups and Maxim Tomatoes
## When the player receives an item, what happens?
A sound effect will play, and Kirby will immediately receive the effects of that item, such as being able to receive Copy Abilities from enemies that
give said Copy Ability. Animal Friends will require leaving the room you are currently in before they will appear.
## What is the goal of Kirby's Dream Land 3?
Under the Zero goal, players must collect enough Heart Stars to purify the five bosses and gain access to the Hyper Zone,
where Zero can be found and defeated.
Under the Boss Butch goal, players must collect enough Heart Stars to purify the five bosses
and then complete the Boss Butch game mode accessible from the main menu.
Under the MG5 goal, players must collect enough Heart Stars to purify the five bosses
and then perfect the Super MG5 game mode accessible from the main menu.
Under the Jumping goal, players must collect enough Heart Stars to purify the five bosses
and then reach a target score in the Jumping game mode accessible from the main menu.
## Why is EmuHawk resizing itself while I'm playing?
Kirby's Dream Land 3 changes the SNES's display resolution from 1x to 2x many times during gameplay (particularly in rooms with foreground effects).
To counter-act this resizing, set SNES -> Options -> "Always use double-size frame buffer".

View File

@@ -0,0 +1,148 @@
# Kirby's Dream Land 3 Randomizer Setup Guide
## Required Software
- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases).
- Hardware or software capable of loading and playing SNES ROM files
- An emulator capable of connecting to SNI with ROM access. Any one of the following will work:
- snes9x-emunwa from: [snes9x-emunwa Releases Page](https://github.com/Skarsnik/snes9x-emunwa/releases)
- snes9x-rr from: [snes9x-rr Releases Page](https://github.com/gocha/snes9x-rr/releases)
- BizHawk from: [BizHawk Website](http://tasvideos.org/BizHawk.html)
- bsnes-plus-nwa from: [bsnes-plus GitHub](https://github.com/black-sliver/bsnes-plus)
- **RetroArch is currently incompatible with Kirby's Dream Land 3**
- Or SD2SNES, FXPak Pro ([FXPak Pro Store Page](https://krikzz.com/store/home/54-fxpak-pro.html)), or other
compatible hardware.
- Your KDL3 ROM file, probably named either `Kirby's Dream Land 3 (USA).sfc` or `Hoshi no Kirby 3 (J).sfc`
## Installation Procedures
1. Download and install Archipelago from the link above, making sure to install the most recent version.
**The installer file is located in the assets section at the bottom of the version information**.
- During generation/patching, you will be asked to locate your base ROM file. This is your Kirby's Dream Land 3 ROM file.
2. If you are using an emulator, you should assign your SNI-compatible emulator as your default program for launching ROM
files.
1. Extract your emulator's folder to your Desktop, or somewhere you will remember.
2. Right-click on a ROM file and select **Open with...**
3. Check the box next to **Always use this app to open .sfc files**
4. Scroll to the bottom of the list and click the grey text **Look for another App on this PC**
5. Browse for your emulator's `.exe` file and click **Open**. This file should be located inside the folder you
extracted in step one.
## Create a Config (.yaml) File
### What is a config file and why do I need one?
Your config 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 config 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.
See the guide on setting up a basic YAML at the Archipelago setup
guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file?
The [Player Settings](/games/Kirby's%20Dream%20Land%203/player-settings) page on the website allows you to configure
your personal settings and export a config file from them.
### Verifying your config file
If you would like to validate your config file to make sure it works, you may do so on the
[YAML Validator](/mysterycheck) page.
## Generating a Single-Player Game
1. Navigate to the [Player Settings](/games/Kirby's%20Dream%20Land%203/player-settings) page, configure your options,
and click the "Generate Game" button.
2. You will be presented with a "Seed Info" page.
3. Click the "Create New Room" link.
4. You will be presented with a server page, from which you can download your patch file.
5. Double-click on your patch file, and SNIClient will launch automatically, create your ROM from the patch file, and
open your emulator for you.
## Joining a MultiWorld Game
### Obtain your patch file and create your ROM
When you join a multiworld game, you will be asked to provide your config file to whoever is hosting. Once that is done,
the host will provide you with either a link to download your patch file, or with a zip file containing everyone's patch
files. Your patch file should have a `.apkdl3` extension.
Put your patch file on your desktop or somewhere convenient, and double click it. This should automatically launch the
client, and will also create your ROM in the same place as your patch file.
### Connect to the client
#### With an emulator
When the client launched automatically, SNI should have also automatically launched in the background. If this is its
first time launching, you may be prompted to allow it to communicate through the Windows Firewall.
##### snes9x-rr
1. Load your ROM file if it hasn't already been loaded.
2. Click on the File menu and hover on **Lua Scripting**
3. Click on **New Lua Script Window...**
4. In the new window, click **Browse...**
5. Select the connector lua file included with your client
- Look in the Archipelago folder for `/SNI/lua/Connector.lua`
6. If you see an error while loading the script that states `socket.dll missing` or similar, navigate to the folder of
the lua you are using in your file explorer and copy the `socket.dll` to the base folder of your snes9x install.
##### BizHawk
1. Ensure you have the BSNES core loaded. You may do this by clicking on the Tools menu in BizHawk and following these
menu options:
`Config --> Cores --> SNES --> BSNES`
Once you have changed the loaded core, you must restart BizHawk.
2. Load your ROM file if it hasn't already been loaded.
3. Click on the Tools menu and click on **Lua Console**
4. Click Script -> Open Script...
5. Select the `Connector.lua` file you downloaded above
- Look in the Archipelago folder for `/SNI/lua/Connector.lua`
##### bsnes-plus-nwa and snes9x-nwa
These should automatically connect to SNI. If this is the first time launching, you may be prompted to allow it to
communicate through the Windows Firewall.
#### With hardware
This guide assumes you have downloaded the correct firmware for your device. If you have not done so already, please do
this now. SD2SNES and FXPak Pro users may download the appropriate firmware
[here](https://github.com/RedGuyyyy/sd2snes/releases). Other hardware may find helpful information
[on this page](http://usb2snes.com/#supported-platforms).
1. Close your emulator, which may have auto-launched.
2. Power on your device and load the ROM.
### Connect to the Archipelago Server
The patch file which launched your client should have automatically connected you to the AP Server. There are a few
reasons this may not happen however, including if the game is hosted on the website but was generated elsewhere. If the
client window shows "Server Status: Not Connected", simply ask the host for the address of the server, and copy/paste it
into the "Server" input field then press enter.
The client will attempt to reconnect to the new server address, and should momentarily show "Server Status: Connected".
### Play the game
When the client shows both SNES Device and Server as connected, you're ready to begin playing. Congratulations on
successfully joining a multiworld game! You can execute various commands in your client. For more information regarding
these commands you can use `/help` for local client commands and `!help` for server commands.
## Hosting a MultiWorld game
The recommended way to host a game is to use our [hosting service](/generate). The process is relatively simple:
1. Collect config files from your players.
2. Create a zip file containing your players' config files.
3. Upload that zip file to the website linked above.
4. Wait a moment while the seed is generated.
5. When the seed is generated, you will be redirected to a "Seed Info" page.
6. Click "Create New Room". This will take you to the server page. Provide the link to this page to your players, so
they may download their patch files from there.
7. Note that a link to a MultiWorld Tracker is at the top of the room page. The tracker shows the progress of all
players in the game. Any observers may also be given the link to this page.
8. Once all players have joined, you may begin playing.

Some files were not shown because too many files have changed in this diff Show More