Add SMZ3 support (#270)
This commit is contained in:
parent
8921baecd0
commit
cfa49ee757
21
Patch.py
21
Patch.py
|
@ -18,12 +18,14 @@ current_patch_version = 3
|
|||
GAME_ALTTP = "A Link to the Past"
|
||||
GAME_SM = "Super Metroid"
|
||||
GAME_SOE = "Secret of Evermore"
|
||||
supported_games = {"A Link to the Past", "Super Metroid", "Secret of Evermore"}
|
||||
GAME_SMZ3 = "SMZ3"
|
||||
supported_games = {"A Link to the Past", "Super Metroid", "Secret of Evermore", "SMZ3"}
|
||||
|
||||
preferred_endings = {
|
||||
GAME_ALTTP: "apbp",
|
||||
GAME_SM: "apm3",
|
||||
GAME_SOE: "apsoe"
|
||||
GAME_SOE: "apsoe",
|
||||
GAME_SMZ3: "apsmz"
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,6 +36,10 @@ def generate_yaml(patch: bytes, metadata: Optional[dict] = None, game: str = GAM
|
|||
from worlds.sm.Rom import JAP10HASH as HASH
|
||||
elif game == GAME_SOE:
|
||||
from worlds.soe.Patch import USHASH as HASH
|
||||
elif game == GAME_SMZ3:
|
||||
from worlds.alttp.Rom import JAP10HASH as ALTTPHASH
|
||||
from worlds.sm.Rom import JAP10HASH as SMHASH
|
||||
HASH = ALTTPHASH + SMHASH
|
||||
else:
|
||||
raise RuntimeError(f"Selected game {game} for base rom not found.")
|
||||
|
||||
|
@ -63,7 +69,7 @@ def create_patch_file(rom_file_to_patch: str, server: str = "", destination: str
|
|||
meta,
|
||||
game)
|
||||
target = destination if destination else os.path.splitext(rom_file_to_patch)[0] + (
|
||||
".apbp" if game == GAME_ALTTP else ".apm3")
|
||||
".apbp" if game == GAME_ALTTP else ".apsmz" if game == GAME_SMZ3 else ".apm3")
|
||||
write_lzma(bytes, target)
|
||||
return target
|
||||
|
||||
|
@ -90,6 +96,8 @@ def get_base_rom_data(game: str):
|
|||
elif game == GAME_SOE:
|
||||
file_name = Utils.get_options()["soe_options"]["rom_file"]
|
||||
get_base_rom_bytes = lambda: bytes(read_rom(open(file_name, "rb")))
|
||||
elif game == GAME_SMZ3:
|
||||
from worlds.smz3.Rom import get_base_rom_bytes
|
||||
else:
|
||||
raise RuntimeError("Selected game for base rom not found.")
|
||||
return get_base_rom_bytes()
|
||||
|
@ -218,6 +226,13 @@ if __name__ == "__main__":
|
|||
if 'server' in data:
|
||||
Utils.persistent_store("servers", data['hash'], data['server'])
|
||||
print(f"Host is {data['server']}")
|
||||
elif rom.endswith(".apsmz"):
|
||||
print(f"Applying patch {rom}")
|
||||
data, target = create_rom_file(rom)
|
||||
print(f"Created rom {target}.")
|
||||
if 'server' in data:
|
||||
Utils.persistent_store("servers", data['hash'], data['server'])
|
||||
print(f"Host is {data['server']}")
|
||||
elif rom.endswith(".archipelago"):
|
||||
import json
|
||||
import zlib
|
||||
|
|
102
SNIClient.py
102
SNIClient.py
|
@ -23,9 +23,10 @@ from NetUtils import *
|
|||
from worlds.alttp import Regions, Shops
|
||||
from worlds.alttp.Rom import ROM_PLAYER_LIMIT
|
||||
from worlds.sm.Rom import ROM_PLAYER_LIMIT as SM_ROM_PLAYER_LIMIT
|
||||
from worlds.smz3.Rom import ROM_PLAYER_LIMIT as SMZ3_ROM_PLAYER_LIMIT
|
||||
import Utils
|
||||
from CommonClient import CommonContext, server_loop, console_loop, ClientCommandProcessor, gui_enabled, get_base_parser
|
||||
from Patch import GAME_ALTTP, GAME_SM
|
||||
from Patch import GAME_ALTTP, GAME_SM, GAME_SMZ3
|
||||
|
||||
snes_logger = logging.getLogger("SNES")
|
||||
|
||||
|
@ -266,6 +267,18 @@ SM_RECV_ITEM_PLAYER_ADDR = SAVEDATA_START + 0x4D3 # 1 byte
|
|||
|
||||
SM_DEATH_LINK_ACTIVE_ADDR = ROM_START + 0x277f04 # 1 byte
|
||||
|
||||
# SMZ3
|
||||
SMZ3_ROMNAME_START = 0x00FFC0
|
||||
|
||||
SMZ3_INGAME_MODES = {0x07, 0x09, 0x0b}
|
||||
SMZ3_ENDGAME_MODES = {0x26, 0x27}
|
||||
SMZ3_DEATH_MODES = {0x15, 0x17, 0x18, 0x19, 0x1A}
|
||||
|
||||
SMZ3_RECV_PROGRESS_ADDR = SRAM_START + 0x4000 # 2 bytes
|
||||
SMZ3_RECV_ITEM_ADDR = SAVEDATA_START + 0x4D2 # 1 byte
|
||||
SMZ3_RECV_ITEM_PLAYER_ADDR = SAVEDATA_START + 0x4D3 # 1 byte
|
||||
|
||||
|
||||
location_shop_ids = set([info[0] for name, info in Shops.shop_table.items()])
|
||||
|
||||
location_table_uw = {"Blind's Hideout - Top": (0x11d, 0x10),
|
||||
|
@ -968,23 +981,29 @@ async def game_watcher(ctx: Context):
|
|||
game_name = await snes_read(ctx, SM_ROMNAME_START, 2)
|
||||
if game_name is None:
|
||||
continue
|
||||
elif game_name == b"SM":
|
||||
elif game_name[:2] == b"SM":
|
||||
ctx.game = GAME_SM
|
||||
ctx.items_handling = 0b001 # full local
|
||||
else:
|
||||
ctx.game = GAME_ALTTP
|
||||
ctx.items_handling = 0b001 # full local
|
||||
game_name = await snes_read(ctx, SMZ3_ROMNAME_START, 3)
|
||||
if game_name == b"ZSM":
|
||||
ctx.game = GAME_SMZ3
|
||||
ctx.items_handling = 0b101 # local items and remote start inventory
|
||||
else:
|
||||
ctx.game = GAME_ALTTP
|
||||
ctx.items_handling = 0b001 # full local
|
||||
|
||||
rom = await snes_read(ctx, SM_ROMNAME_START if ctx.game == GAME_SM else ROMNAME_START, ROMNAME_SIZE)
|
||||
rom = await snes_read(ctx, SM_ROMNAME_START if ctx.game == GAME_SM else SMZ3_ROMNAME_START if ctx.game == GAME_SMZ3 else ROMNAME_START, ROMNAME_SIZE)
|
||||
if rom is None or rom == bytes([0] * ROMNAME_SIZE):
|
||||
continue
|
||||
|
||||
ctx.rom = rom
|
||||
death_link = await snes_read(ctx, DEATH_LINK_ACTIVE_ADDR if ctx.game == GAME_ALTTP else
|
||||
SM_DEATH_LINK_ACTIVE_ADDR, 1)
|
||||
if death_link:
|
||||
ctx.death_link_allow_survive = bool(death_link[0] & 0b10)
|
||||
await ctx.update_death_link(bool(death_link[0] & 0b1))
|
||||
if ctx.game != GAME_SMZ3:
|
||||
death_link = await snes_read(ctx, DEATH_LINK_ACTIVE_ADDR if ctx.game == GAME_ALTTP else
|
||||
SM_DEATH_LINK_ACTIVE_ADDR, 1)
|
||||
if death_link:
|
||||
ctx.death_link_allow_survive = bool(death_link[0] & 0b10)
|
||||
await ctx.update_death_link(bool(death_link[0] & 0b1))
|
||||
if not ctx.prev_rom or ctx.prev_rom != ctx.rom:
|
||||
ctx.locations_checked = set()
|
||||
ctx.locations_scouted = set()
|
||||
|
@ -1129,6 +1148,69 @@ async def game_watcher(ctx: Context):
|
|||
color(ctx.player_names[item.player], 'yellow'),
|
||||
ctx.location_name_getter(item.location), itemOutPtr, len(ctx.items_received)))
|
||||
await snes_flush_writes(ctx)
|
||||
elif ctx.game == GAME_SMZ3:
|
||||
currentGame = await snes_read(ctx, SRAM_START + 0x33FE, 2)
|
||||
if (currentGame is not None):
|
||||
if (currentGame[0] != 0):
|
||||
gamemode = await snes_read(ctx, WRAM_START + 0x0998, 1)
|
||||
endGameModes = SM_ENDGAME_MODES
|
||||
else:
|
||||
gamemode = await snes_read(ctx, WRAM_START + 0x10, 1)
|
||||
endGameModes = ENDGAME_MODES
|
||||
|
||||
if gamemode is not None and (gamemode[0] in endGameModes):
|
||||
if not ctx.finished_game:
|
||||
await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}])
|
||||
ctx.finished_game = True
|
||||
continue
|
||||
|
||||
data = await snes_read(ctx, SMZ3_RECV_PROGRESS_ADDR + 0x680, 4)
|
||||
if data is None:
|
||||
continue
|
||||
|
||||
recv_index = data[0] | (data[1] << 8)
|
||||
recv_item = data[2] | (data[3] << 8)
|
||||
|
||||
while (recv_index < recv_item):
|
||||
itemAdress = recv_index * 8
|
||||
message = await snes_read(ctx, SMZ3_RECV_PROGRESS_ADDR + 0x700 + itemAdress, 8)
|
||||
# worldId = message[0] | (message[1] << 8) # unused
|
||||
# itemId = message[2] | (message[3] << 8) # unused
|
||||
isZ3Item = ((message[5] & 0x80) != 0)
|
||||
maskedPart = (message[5] & 0x7F) if isZ3Item else message[5]
|
||||
itemIndex = ((message[4] | (maskedPart << 8)) >> 3) + (256 if isZ3Item else 0)
|
||||
|
||||
recv_index += 1
|
||||
snes_buffered_write(ctx, SMZ3_RECV_PROGRESS_ADDR + 0x680, bytes([recv_index & 0xFF, (recv_index >> 8) & 0xFF]))
|
||||
|
||||
from worlds.smz3.TotalSMZ3.Location import locations_start_id
|
||||
location_id = locations_start_id + itemIndex
|
||||
|
||||
ctx.locations_checked.add(location_id)
|
||||
location = ctx.location_name_getter(location_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": [location_id]}])
|
||||
|
||||
data = await snes_read(ctx, SMZ3_RECV_PROGRESS_ADDR + 0x600, 4)
|
||||
if data is None:
|
||||
continue
|
||||
|
||||
# recv_itemOutPtr = data[0] | (data[1] << 8) # unused
|
||||
itemOutPtr = data[2] | (data[3] << 8)
|
||||
|
||||
from worlds.smz3.TotalSMZ3.Item import items_start_id
|
||||
if itemOutPtr < len(ctx.items_received):
|
||||
item = ctx.items_received[itemOutPtr]
|
||||
itemId = item.item - items_start_id
|
||||
|
||||
playerID = item.player if item.player <= SMZ3_ROM_PLAYER_LIMIT else 0
|
||||
snes_buffered_write(ctx, SMZ3_RECV_PROGRESS_ADDR + itemOutPtr * 4, bytes([playerID & 0xFF, (playerID >> 8) & 0xFF, itemId & 0xFF, (itemId >> 8) & 0xFF]))
|
||||
itemOutPtr += 1
|
||||
snes_buffered_write(ctx, SMZ3_RECV_PROGRESS_ADDR + 0x602, bytes([itemOutPtr & 0xFF, (itemOutPtr >> 8) & 0xFF]))
|
||||
logging.info('Received %s from %s (%s) (%d/%d in list)' % (
|
||||
color(ctx.item_name_getter(item.item), 'red', 'bold'), color(ctx.player_names[item.player], 'yellow'),
|
||||
ctx.location_name_getter(item.location), itemOutPtr, len(ctx.items_received)))
|
||||
await snes_flush_writes(ctx)
|
||||
|
||||
|
||||
async def run_game(romfile):
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# SMZ3
|
||||
|
||||
## 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?
|
||||
|
||||
Items which the player would normally acquire throughout the game have been moved around. Logic remains, so the game is
|
||||
always able to be completed, but because of the item shuffle the player may need to access certain areas before they
|
||||
would in the vanilla game.
|
||||
|
||||
## What items and locations get shuffled?
|
||||
|
||||
All main inventory items, collectables, power-ups and ammunition can be shuffled, and all locations in the game which
|
||||
could contain any of those items may have their contents changed.
|
||||
|
||||
## Which items can be in another player's world?
|
||||
|
||||
Any of the items which can be shuffled may also be placed into another player's world. It is possible to choose to limit
|
||||
certain items to your own world.
|
||||
|
||||
## What does another world's item look like in Super Metroid?
|
||||
|
||||
A unique item sprite has been added to the game to represent items belonging to another world.
|
||||
|
||||
## What does another world's item look like in LttP?
|
||||
|
||||
Items belonging to other worlds are represented by a Power Star from Super Mario World.
|
||||
|
||||
## When the player receives an item, what happens?
|
||||
|
||||
When the player receives an item, a text box will appear to show which item was received, and from whom.
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
# Super Metroid Setup Guide
|
||||
|
||||
## Required Software
|
||||
|
||||
- One of the client programs:
|
||||
- [SNIClient](https://github.com/ArchipelagoMW/Archipelago/releases), included with the main
|
||||
Archipelago install. Make sure to check the box for `SNI Client - Super Metroid Patch Setup` and
|
||||
`SNI Client - A Link to the Past Patch Setup`
|
||||
- Hardware or software capable of loading and playing SNES ROM files
|
||||
- An emulator capable of connecting to SNI such as:
|
||||
- snes9x Multitroid
|
||||
from: [snes9x Multitroid Download](https://drive.google.com/drive/folders/1_ej-pwWtCAHYXIrvs5Hro16A1s9Hi3Jz),
|
||||
- BizHawk from: [BizHawk Website](http://tasvideos.org/BizHawk.html)
|
||||
- An SD2SNES, FXPak Pro ([FXPak Pro Store Page](https://krikzz.com/store/home/54-fxpak-pro.html)), or other
|
||||
compatible hardware
|
||||
- Your legally obtained Super Metroid ROM file, probably named `Super Metroid (Japan, USA).sfc` and
|
||||
Your Japanese Zelda3 v1.0 ROM file, probably named `Zelda no Densetsu - Kamigami no Triforce (Japan).sfc`
|
||||
|
||||
## Installation Procedures
|
||||
|
||||
### Windows Setup
|
||||
|
||||
1. During the installation of Archipelago, you will have been asked to install the SNI Client. If you did not do this,
|
||||
or you are on an older version, you may run the installer again to install the SNI Client.
|
||||
2. During setup, you will be asked to locate your base ROM files. This is your Super Metroid and Zelda3 ROM files.
|
||||
3. If you are using an emulator, you should assign your Lua capable 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?
|
||||
|
||||
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 page on the website allows you to configure your personal settings and export a config file from
|
||||
them. Player settings page: [SMZ3 Player Settings Page](/games/SMZ3/player-settings)
|
||||
|
||||
### 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 page. YAML
|
||||
validator page: [YAML Validation page](/mysterycheck)
|
||||
|
||||
## Generating a Single-Player Game
|
||||
|
||||
1. Navigate to the Player Settings page, configure your options, and click the "Generate Game" button.
|
||||
- Player Settings page: [SMZ3 Player Settings Page](/games/SMZ3/player-settings)
|
||||
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 the SMZ3 Client will launch automatically, create your ROM from the
|
||||
patch file, and open your emulator for you.
|
||||
6. Since this is a single-player game, you will no longer need the client, so feel free to close it.
|
||||
|
||||
## 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 `.apsmz` 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 Multitroid
|
||||
|
||||
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
|
||||
- SuperNintendoClient users should download `sniConnector.lua` from the client download page
|
||||
- SNIClient users should look in their Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the
|
||||
emulator is 64-bit or 32-bit.
|
||||
|
||||
##### 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 the button to open a new Lua script.
|
||||
5. Select the `Connector.lua` file included with your client
|
||||
- SuperNintendoClient users should download `sniConnector.lua` from the client download page
|
||||
- SNIClient users should look in their Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the
|
||||
emulator is 64-bit or 32-bit. Please note the most recent versions of BizHawk are 64-bit only.
|
||||
|
||||
#### 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 on the SD2SNES releases page. SD2SNES
|
||||
releases page: [SD2SNES Releases Page](https://github.com/RedGuyyyy/sd2snes/releases)
|
||||
|
||||
Other hardware may find helpful information on the usb2snes platforms
|
||||
page: [usb2snes Supported Platforms 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!
|
||||
|
||||
## Hosting a MultiWorld game
|
||||
|
||||
The recommended way to host a game is to use our hosting service. 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 Generate page above.
|
||||
- Generate page: [WebHost Seed Generation Page](/generate)
|
||||
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.
|
|
@ -40,7 +40,7 @@
|
|||
{% elif patch.game == "Super Mario 64" and room.seed.slots|length == 1 %}
|
||||
<a href="{{ url_for("download_slot_file", room_id=room.id, player_id=patch.player_id) }}" download>
|
||||
Download APSM64EX File...</a>
|
||||
{% elif patch.game in ["A Link to the Past", "Secret of Evermore", "Super Metroid"] %}
|
||||
{% elif patch.game in ["A Link to the Past", "Secret of Evermore", "Super Metroid", "SMZ3"] %}
|
||||
<a href="{{ url_for("download_patch", patch_id=patch.id, room_id=room.id) }}" download>
|
||||
Download Patch File...</a>
|
||||
{% else %}
|
||||
|
|
11
host.yaml
11
host.yaml
|
@ -102,10 +102,10 @@ sm_options:
|
|||
rom_start: true
|
||||
factorio_options:
|
||||
executable: "factorio\\bin\\x64\\factorio"
|
||||
minecraft_options:
|
||||
minecraft_options:
|
||||
forge_directory: "Minecraft Forge server"
|
||||
max_heap_size: "2G"
|
||||
oot_options:
|
||||
oot_options:
|
||||
# File name of the OoT v1.0 ROM
|
||||
rom_file: "The Legend of Zelda - Ocarina of Time.z64"
|
||||
soe_options:
|
||||
|
@ -113,3 +113,10 @@ soe_options:
|
|||
rom_file: "Secret of Evermore (USA).sfc"
|
||||
ffr_options:
|
||||
display_msgs: true
|
||||
smz3_options:
|
||||
# Set this to your SNI folder location if you want the MultiClient to attempt an auto start, does nothing if not found
|
||||
sni: "SNI"
|
||||
# Set this to false to never autostart a rom (such as after patching)
|
||||
# True for operating system default program
|
||||
# Alternatively, a path to a program to open the .sfc file with
|
||||
rom_start: true
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Thomas Backmark
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,63 @@
|
|||
import typing
|
||||
from Options import Choice, Option
|
||||
|
||||
class SMLogic(Choice):
|
||||
"""This option selects what kind of logic to use for item placement inside
|
||||
Super Metroid.
|
||||
|
||||
Normal - Normal logic includes only what Super Metroid teaches players
|
||||
itself. Anything that's not demonstrated in-game or by the intro cutscenes
|
||||
will not be required here.
|
||||
|
||||
Hard - Hard logic is based upon the "no major glitches" ruleset and
|
||||
includes most tricks that are considered minor glitches, with some
|
||||
restrictions. You'll want to be somewhat of a Super Metroid veteran for
|
||||
this logic.
|
||||
|
||||
See https://samus.link/information for required moves."""
|
||||
display_name = "SMLogic"
|
||||
option_Normal = 0
|
||||
option_Hard = 1
|
||||
default = 0
|
||||
|
||||
class SwordLocation(Choice):
|
||||
"""This option decides where the first sword will be placed.
|
||||
Randomized - The sword can be placed anywhere.
|
||||
Early - The sword will be placed in a location accessible from the start of
|
||||
the game.
|
||||
Unce assured - The sword will always be placed on Link's Uncle."""
|
||||
display_name = "Sword Location"
|
||||
option_Randomized = 0
|
||||
option_Early = 1
|
||||
option_Uncle = 2
|
||||
default = 0
|
||||
|
||||
class MorphLocation(Choice):
|
||||
"""This option decides where the morph ball will be placed.
|
||||
Randomized - The morph ball can be placed anywhere.
|
||||
Early - The morph ball will be placed in a location accessible from the
|
||||
start of the game.
|
||||
Original location - The morph ball will always be placed at its original
|
||||
location."""
|
||||
display_name = "Morph Location"
|
||||
option_Randomized = 0
|
||||
option_Early = 1
|
||||
option_Original = 2
|
||||
default = 0
|
||||
|
||||
class KeyShuffle(Choice):
|
||||
"""This option decides how dungeon items such as keys are shuffled.
|
||||
None - A Link to the Past dungeon items can only be placed inside the
|
||||
dungeon they belong to, and there are no changes to Super Metroid.
|
||||
Keysanity - See https://samus.link/information"""
|
||||
display_name = "Key Shuffle"
|
||||
option_None = 0
|
||||
option_Keysanity = 1
|
||||
default = 0
|
||||
|
||||
smz3_options: typing.Dict[str, type(Option)] = {
|
||||
"sm_logic": SMLogic,
|
||||
"sword_location": SwordLocation,
|
||||
"morph_location": MorphLocation,
|
||||
"key_shuffle": KeyShuffle
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import Utils
|
||||
from Patch import read_rom
|
||||
|
||||
SMJAP10HASH = '21f3e98df4780ee1c667b84e57d88675'
|
||||
LTTPJAP10HASH = '03a63945398191337e896e5771f77173'
|
||||
ROM_PLAYER_LIMIT = 256
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
|
||||
def get_base_rom_bytes(file_name: str = "") -> bytes:
|
||||
base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None)
|
||||
if not base_rom_bytes:
|
||||
sm_file_name = get_sm_base_rom_path()
|
||||
sm_base_rom_bytes = bytes(read_rom(open(sm_file_name, "rb")))
|
||||
|
||||
basemd5 = hashlib.md5()
|
||||
basemd5.update(sm_base_rom_bytes)
|
||||
if SMJAP10HASH != basemd5.hexdigest():
|
||||
raise Exception('Supplied Base Rom does not match known MD5 for JAP(1.0) release. '
|
||||
'Get the correct game and version, then dump it')
|
||||
lttp_file_name = get_lttp_base_rom_path()
|
||||
lttp_base_rom_bytes = bytes(read_rom(open(lttp_file_name, "rb")))
|
||||
|
||||
basemd5 = hashlib.md5()
|
||||
basemd5.update(lttp_base_rom_bytes)
|
||||
if LTTPJAP10HASH != basemd5.hexdigest():
|
||||
raise Exception('Supplied Base Rom does not match known MD5 for JAP(1.0) release. '
|
||||
'Get the correct game and version, then dump it')
|
||||
|
||||
get_base_rom_bytes.base_rom_bytes = bytes(combine_smz3_rom(sm_base_rom_bytes, lttp_base_rom_bytes))
|
||||
return get_base_rom_bytes.base_rom_bytes
|
||||
|
||||
def get_sm_base_rom_path(file_name: str = "") -> str:
|
||||
options = Utils.get_options()
|
||||
if not file_name:
|
||||
file_name = options["sm_options"]["rom_file"]
|
||||
if not os.path.exists(file_name):
|
||||
file_name = Utils.local_path(file_name)
|
||||
return file_name
|
||||
|
||||
def get_lttp_base_rom_path(file_name: str = "") -> str:
|
||||
options = Utils.get_options()
|
||||
if not file_name:
|
||||
file_name = options["lttp_options"]["rom_file"]
|
||||
if not os.path.exists(file_name):
|
||||
file_name = Utils.local_path(file_name)
|
||||
return file_name
|
||||
|
||||
def combine_smz3_rom(sm_rom: bytes, lttp_rom: bytes):
|
||||
combined = bytearray(0x600000)
|
||||
# SM hi bank
|
||||
pos = 0
|
||||
srcpos = 0
|
||||
for i in range(0x40):
|
||||
combined[pos + 0x8000:pos + 0x8000 + 0x8000] = sm_rom[srcpos:srcpos + 0x8000]
|
||||
srcpos += 0x8000
|
||||
pos += 0x10000
|
||||
|
||||
# SM lo bank
|
||||
pos = 0
|
||||
for i in range(0x20):
|
||||
combined[pos:pos + 0x8000] = sm_rom[srcpos:srcpos + 0x8000]
|
||||
srcpos += 0x8000
|
||||
pos += 0x10000
|
||||
|
||||
# Z3 hi bank
|
||||
pos = 0x400000
|
||||
srcpos = 0
|
||||
for i in range(0x20):
|
||||
combined[pos + 0x8000:pos + 0x8000 + 0x8000] = lttp_rom[srcpos:srcpos + 0x8000]
|
||||
srcpos += 0x8000
|
||||
pos += 0x10000
|
||||
|
||||
return combined
|
|
@ -0,0 +1,107 @@
|
|||
from enum import Enum
|
||||
from typing import Dict, List
|
||||
|
||||
class GameMode(Enum):
|
||||
Normal = 0
|
||||
Multiworld = 1
|
||||
|
||||
class Z3Logic(Enum):
|
||||
Normal = 0
|
||||
Nmg = 1
|
||||
Owg = 2
|
||||
|
||||
class SMLogic(Enum):
|
||||
Normal = 0
|
||||
Hard = 1
|
||||
|
||||
class SwordLocation(Enum):
|
||||
Randomized = 0
|
||||
Early = 1
|
||||
Uncle = 2
|
||||
|
||||
class MorphLocation(Enum):
|
||||
Randomized = 0
|
||||
Early = 1
|
||||
Original = 2
|
||||
|
||||
class Goal(Enum):
|
||||
DefeatBoth = 0
|
||||
|
||||
class KeyShuffle(Enum):
|
||||
Null = 0
|
||||
Keysanity = 1
|
||||
|
||||
class GanonInvincible(Enum):
|
||||
Never = 0
|
||||
BeforeCrystals = 1
|
||||
BeforeAllDungeons = 2
|
||||
Always = 3
|
||||
|
||||
class Config:
|
||||
GameMode: GameMode = GameMode.Multiworld
|
||||
Z3Logic: Z3Logic = Z3Logic.Normal
|
||||
SMLogic: SMLogic = SMLogic.Normal
|
||||
SwordLocation: SwordLocation= SwordLocation.Randomized
|
||||
MorphLocation: MorphLocation = MorphLocation.Randomized
|
||||
Goal: Goal = Goal.DefeatBoth
|
||||
KeyShuffle: KeyShuffle = KeyShuffle.Null
|
||||
Keysanity: bool = KeyShuffle != KeyShuffle.Null
|
||||
Race: bool = False
|
||||
GanonInvincible: GanonInvincible = GanonInvincible.BeforeCrystals
|
||||
|
||||
def __init__(self, options: Dict[str, str]):
|
||||
self.GameMode = self.ParseOption(options, GameMode.Multiworld)
|
||||
self.Z3Logic = self.ParseOption(options, Z3Logic.Normal)
|
||||
self.SMLogic = self.ParseOption(options, SMLogic.Normal)
|
||||
self.SwordLocation = self.ParseOption(options, SwordLocation.Randomized)
|
||||
self.MorphLocation = self.ParseOption(options, MorphLocation.Randomized)
|
||||
self.Goal = self.ParseOption(options, Goal.DefeatBoth)
|
||||
self.GanonInvincible = self.ParseOption(options, GanonInvincible.BeforeCrystals)
|
||||
self.KeyShuffle = self.ParseOption(options, KeyShuffle.Null)
|
||||
self.Keysanity = self.KeyShuffle != KeyShuffle.Null
|
||||
self.Race = self.ParseOptionWith(options, "Race", False)
|
||||
|
||||
def ParseOption(self, options:Dict[str, str], defaultValue:Enum):
|
||||
enumKey = defaultValue.__class__.__name__.lower()
|
||||
if (enumKey in options):
|
||||
return defaultValue.__class__[options[enumKey]]
|
||||
return defaultValue
|
||||
|
||||
def ParseOptionWith(self, options:Dict[str, str], option:str, defaultValue:bool):
|
||||
if (option.lower() in options):
|
||||
return options[option.lower()]
|
||||
return defaultValue
|
||||
|
||||
""" public static RandomizerOption GetRandomizerOption<T>(string description, string defaultOption = "") where T : Enum {
|
||||
var enumType = typeof(T);
|
||||
var values = Enum.GetValues(enumType).Cast<Enum>();
|
||||
|
||||
return new RandomizerOption {
|
||||
Key = enumType.Name.ToLower(),
|
||||
Description = description,
|
||||
Type = RandomizerOptionType.Dropdown,
|
||||
Default = string.IsNullOrEmpty(defaultOption) ? GetDefaultValue<T>().ToLString() : defaultOption,
|
||||
Values = values.ToDictionary(k => k.ToLString(), v => v.GetDescription())
|
||||
};
|
||||
}
|
||||
|
||||
public static RandomizerOption GetRandomizerOption(string name, string description, bool defaultOption = false) {
|
||||
return new RandomizerOption {
|
||||
Key = name.ToLower(),
|
||||
Description = description,
|
||||
Type = RandomizerOptionType.Checkbox,
|
||||
Default = defaultOption.ToString().ToLower(),
|
||||
Values = new Dictionary<string, string>()
|
||||
};
|
||||
}
|
||||
|
||||
public static TEnum GetDefaultValue<TEnum>() where TEnum : Enum {
|
||||
Type t = typeof(TEnum);
|
||||
var attributes = (DefaultValueAttribute[])t.GetCustomAttributes(typeof(DefaultValueAttribute), false);
|
||||
if ((attributes?.Length ?? 0) > 0) {
|
||||
return (TEnum)attributes.First().Value;
|
||||
}
|
||||
else {
|
||||
return default;
|
||||
}
|
||||
} """
|
|
@ -0,0 +1,782 @@
|
|||
from enum import Enum
|
||||
import re
|
||||
import copy
|
||||
from typing import List
|
||||
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
|
||||
class ItemType(Enum):
|
||||
Nothing = 0
|
||||
MapHC = 0x7F
|
||||
MapEP = 0x7D
|
||||
MapDP = 0x7C
|
||||
MapTH = 0x75
|
||||
MapPD = 0x79
|
||||
MapSP = 0x7A
|
||||
MapSW = 0x77
|
||||
MapTT = 0x74
|
||||
MapIP = 0x76
|
||||
MapMM = 0x78
|
||||
MapTR = 0x73
|
||||
MapGT = 0x72
|
||||
|
||||
CompassEP = 0x8D
|
||||
CompassDP = 0x8C
|
||||
CompassTH = 0x85
|
||||
CompassPD = 0x89
|
||||
CompassSP = 0x8A
|
||||
CompassSW = 0x87
|
||||
CompassTT = 0x84
|
||||
CompassIP = 0x86
|
||||
CompassMM = 0x88
|
||||
CompassTR = 0x83
|
||||
CompassGT = 0x82
|
||||
|
||||
BigKeyEP = 0x9D
|
||||
BigKeyDP = 0x9C
|
||||
BigKeyTH = 0x95
|
||||
BigKeyPD = 0x99
|
||||
BigKeySP = 0x9A
|
||||
BigKeySW = 0x97
|
||||
BigKeyTT = 0x94
|
||||
BigKeyIP = 0x96
|
||||
BigKeyMM = 0x98
|
||||
BigKeyTR = 0x93
|
||||
BigKeyGT = 0x92
|
||||
|
||||
KeyHC = 0xA0
|
||||
KeyCT = 0xA4
|
||||
KeyDP = 0xA3
|
||||
KeyTH = 0xAA
|
||||
KeyPD = 0xA6
|
||||
KeySP = 0xA5
|
||||
KeySW = 0xA8
|
||||
KeyTT = 0xAB
|
||||
KeyIP = 0xA9
|
||||
KeyMM = 0xA7
|
||||
KeyTR = 0xAC
|
||||
KeyGT = 0xAD
|
||||
|
||||
Key = 0x24
|
||||
Compass = 0x25
|
||||
BigKey = 0x32
|
||||
Map = 0x33
|
||||
|
||||
Something = 0x6B
|
||||
|
||||
ProgressiveTunic = 0x60
|
||||
ProgressiveShield = 0x5F
|
||||
ProgressiveSword = 0x5E
|
||||
Bow = 0x0B
|
||||
SilverArrows = 0x58
|
||||
BlueBoomerang = 0x0C
|
||||
RedBoomerang = 0x2A
|
||||
Hookshot = 0x0A
|
||||
Mushroom = 0x29
|
||||
Powder = 0x0D
|
||||
Firerod = 0x07
|
||||
Icerod = 0x08
|
||||
Bombos = 0x0f
|
||||
Ether = 0x10
|
||||
Quake = 0x11
|
||||
Lamp = 0x12
|
||||
Hammer = 0x09
|
||||
Shovel = 0x13
|
||||
Flute = 0x14
|
||||
Bugnet = 0x21
|
||||
Book = 0x1D
|
||||
Bottle = 0x16
|
||||
Somaria = 0x15
|
||||
Byrna = 0x18
|
||||
Cape = 0x19
|
||||
Mirror = 0x1A
|
||||
Boots = 0x4B
|
||||
ProgressiveGlove = 0x61
|
||||
Flippers = 0x1E
|
||||
MoonPearl = 0x1F
|
||||
HalfMagic = 0x4E
|
||||
HeartPiece = 0x17
|
||||
HeartContainer = 0x3E
|
||||
HeartContainerRefill = 0x3F
|
||||
ThreeBombs = 0x28
|
||||
Arrow = 0x43
|
||||
TenArrows = 0x44
|
||||
OneRupee = 0x34
|
||||
FiveRupees = 0x35
|
||||
TwentyRupees = 0x36
|
||||
TwentyRupees2 = 0x47
|
||||
FiftyRupees = 0x41
|
||||
OneHundredRupees = 0x40
|
||||
ThreeHundredRupees = 0x46
|
||||
BombUpgrade5 = 0x51
|
||||
BombUpgrade10 = 0x52
|
||||
ArrowUpgrade5 = 0x53
|
||||
ArrowUpgrade10 = 0x54
|
||||
|
||||
CardCrateriaL1 = 0xD0
|
||||
CardCrateriaL2 = 0xD1
|
||||
CardCrateriaBoss = 0xD2
|
||||
CardBrinstarL1 = 0xD3
|
||||
CardBrinstarL2 = 0xD4
|
||||
CardBrinstarBoss = 0xD5
|
||||
CardNorfairL1 = 0xD6
|
||||
CardNorfairL2 = 0xD7
|
||||
CardNorfairBoss = 0xD8
|
||||
CardMaridiaL1 = 0xD9
|
||||
CardMaridiaL2 = 0xDA
|
||||
CardMaridiaBoss = 0xDB
|
||||
CardWreckedShipL1 = 0xDC
|
||||
CardWreckedShipBoss = 0xDD
|
||||
CardLowerNorfairL1 = 0xDE
|
||||
CardLowerNorfairBoss = 0xDF
|
||||
|
||||
Missile = 0xC2
|
||||
Super = 0xC3
|
||||
PowerBomb = 0xC4
|
||||
Grapple = 0xB0
|
||||
XRay = 0xB1
|
||||
ETank = 0xC0
|
||||
ReserveTank = 0xC1
|
||||
Charge = 0xBB
|
||||
Ice = 0xBC
|
||||
Wave = 0xBD
|
||||
Spazer = 0xBE
|
||||
Plasma = 0xBF
|
||||
Varia = 0xB2
|
||||
Gravity = 0xB6
|
||||
Morph = 0xB4
|
||||
Bombs = 0xB9
|
||||
SpringBall = 0xB3
|
||||
ScrewAttack = 0xB5
|
||||
HiJump = 0xB7
|
||||
SpaceJump = 0xB8
|
||||
SpeedBooster = 0xBA
|
||||
|
||||
BottleWithRedPotion = 0x2B
|
||||
BottleWithGreenPotion = 0x2C
|
||||
BottleWithBluePotion = 0x2D
|
||||
BottleWithFairy = 0x3D
|
||||
BottleWithBee = 0x3C
|
||||
BottleWithGoldBee = 0x48
|
||||
RedContent = 0x2E
|
||||
GreenContent = 0x2F
|
||||
BlueContent = 0x30
|
||||
BeeContent = 0x0E
|
||||
|
||||
class Item:
|
||||
Name: str
|
||||
Type: ItemType
|
||||
Progression: bool
|
||||
|
||||
dungeon = re.compile("^(BigKey|Key|Map|Compass)")
|
||||
bigKey = re.compile("^BigKey")
|
||||
key = re.compile("^Key")
|
||||
map = re.compile("^Map")
|
||||
compass = re.compile("^Compass")
|
||||
keycard = re.compile("^Card")
|
||||
|
||||
def IsDungeonItem(self): return self.dungeon.match(self.Type.name)
|
||||
def IsBigKey(self): return self.bigKey.match(self.Type.name)
|
||||
def IsKey(self): return self.key.match(self.Type.name)
|
||||
def IsMap(self): return self.map.match(self.Type.name)
|
||||
def IsCompass(self): return self.compass.match(self.Type.name)
|
||||
def IsKeycard(self): return self.keycard.match(self.Type.name)
|
||||
|
||||
def Is(self, type: ItemType, world):
|
||||
return self.Type == type and self.World == world
|
||||
|
||||
def IsNot(self, type: ItemType, world):
|
||||
return not self.Is(type, world)
|
||||
|
||||
def __init__(self, itemType: ItemType, world = None):
|
||||
self.Type = itemType
|
||||
self.World = world
|
||||
self.Progression = False
|
||||
#self.Name = itemType.GetDescription()
|
||||
|
||||
@staticmethod
|
||||
def Nothing(world):
|
||||
return Item(ItemType.Nothing, world)
|
||||
|
||||
@staticmethod
|
||||
def AddRange(itemPool, count, item):
|
||||
for i in range(count):
|
||||
itemPool.append(copy.copy(item))
|
||||
|
||||
@staticmethod
|
||||
def CreateProgressionPool(world):
|
||||
itemPool = [
|
||||
Item(ItemType.ProgressiveShield),
|
||||
Item(ItemType.ProgressiveShield),
|
||||
Item(ItemType.ProgressiveShield),
|
||||
Item(ItemType.ProgressiveSword),
|
||||
Item(ItemType.ProgressiveSword),
|
||||
Item(ItemType.Bow),
|
||||
Item(ItemType.Hookshot),
|
||||
Item(ItemType.Mushroom),
|
||||
Item(ItemType.Powder),
|
||||
Item(ItemType.Firerod),
|
||||
Item(ItemType.Icerod),
|
||||
Item(ItemType.Bombos),
|
||||
Item(ItemType.Ether),
|
||||
Item(ItemType.Quake),
|
||||
Item(ItemType.Lamp),
|
||||
Item(ItemType.Hammer),
|
||||
Item(ItemType.Shovel),
|
||||
Item(ItemType.Flute),
|
||||
Item(ItemType.Bugnet),
|
||||
Item(ItemType.Book),
|
||||
Item(ItemType.Bottle),
|
||||
Item(ItemType.Somaria),
|
||||
Item(ItemType.Byrna),
|
||||
Item(ItemType.Cape),
|
||||
Item(ItemType.Mirror),
|
||||
Item(ItemType.Boots),
|
||||
Item(ItemType.ProgressiveGlove),
|
||||
Item(ItemType.ProgressiveGlove),
|
||||
Item(ItemType.Flippers),
|
||||
Item(ItemType.MoonPearl),
|
||||
Item(ItemType.HalfMagic),
|
||||
|
||||
Item(ItemType.Grapple),
|
||||
Item(ItemType.Charge),
|
||||
Item(ItemType.Ice),
|
||||
Item(ItemType.Wave),
|
||||
Item(ItemType.Plasma),
|
||||
Item(ItemType.Varia),
|
||||
Item(ItemType.Gravity),
|
||||
Item(ItemType.Morph),
|
||||
Item(ItemType.Bombs),
|
||||
Item(ItemType.SpringBall),
|
||||
Item(ItemType.ScrewAttack),
|
||||
Item(ItemType.HiJump),
|
||||
Item(ItemType.SpaceJump),
|
||||
Item(ItemType.SpeedBooster),
|
||||
|
||||
Item(ItemType.Missile),
|
||||
Item(ItemType.Super),
|
||||
Item(ItemType.PowerBomb),
|
||||
Item(ItemType.PowerBomb),
|
||||
Item(ItemType.ETank),
|
||||
Item(ItemType.ETank),
|
||||
Item(ItemType.ETank),
|
||||
Item(ItemType.ETank),
|
||||
Item(ItemType.ETank),
|
||||
|
||||
Item(ItemType.ReserveTank),
|
||||
Item(ItemType.ReserveTank),
|
||||
Item(ItemType.ReserveTank),
|
||||
Item(ItemType.ReserveTank),
|
||||
]
|
||||
|
||||
for item in itemPool:
|
||||
item.Progression = True
|
||||
item.World = world
|
||||
return itemPool
|
||||
|
||||
@staticmethod
|
||||
def CreateNicePool(world):
|
||||
itemPool = [
|
||||
Item(ItemType.ProgressiveTunic),
|
||||
Item(ItemType.ProgressiveTunic),
|
||||
Item(ItemType.ProgressiveSword),
|
||||
Item(ItemType.ProgressiveSword),
|
||||
Item(ItemType.SilverArrows),
|
||||
Item(ItemType.BlueBoomerang),
|
||||
Item(ItemType.RedBoomerang),
|
||||
Item(ItemType.Bottle),
|
||||
Item(ItemType.Bottle),
|
||||
Item(ItemType.Bottle),
|
||||
Item(ItemType.HeartContainerRefill),
|
||||
|
||||
Item(ItemType.Spazer),
|
||||
Item(ItemType.XRay),
|
||||
]
|
||||
|
||||
Item.AddRange(itemPool, 10, Item(ItemType.HeartContainer, world))
|
||||
|
||||
for item in itemPool:
|
||||
item.World = world
|
||||
return itemPool
|
||||
|
||||
@staticmethod
|
||||
def CreateJunkPool(world):
|
||||
itemPool = [
|
||||
Item(ItemType.Arrow),
|
||||
Item(ItemType.OneHundredRupees)
|
||||
]
|
||||
|
||||
Item.AddRange(itemPool, 24, Item(ItemType.HeartPiece))
|
||||
Item.AddRange(itemPool, 8, Item(ItemType.TenArrows))
|
||||
Item.AddRange(itemPool, 13, Item(ItemType.ThreeBombs))
|
||||
Item.AddRange(itemPool, 4, Item(ItemType.ArrowUpgrade5))
|
||||
Item.AddRange(itemPool, 4, Item(ItemType.BombUpgrade5))
|
||||
Item.AddRange(itemPool, 2, Item(ItemType.OneRupee))
|
||||
Item.AddRange(itemPool, 4, Item(ItemType.FiveRupees))
|
||||
Item.AddRange(itemPool, 25 if world.Config.Keysanity else 28, Item(ItemType.TwentyRupees))
|
||||
Item.AddRange(itemPool, 7, Item(ItemType.FiftyRupees))
|
||||
Item.AddRange(itemPool, 5, Item(ItemType.ThreeHundredRupees))
|
||||
|
||||
Item.AddRange(itemPool, 9, Item(ItemType.ETank))
|
||||
Item.AddRange(itemPool, 39, Item(ItemType.Missile))
|
||||
Item.AddRange(itemPool, 15, Item(ItemType.Super))
|
||||
Item.AddRange(itemPool, 8, Item(ItemType.PowerBomb))
|
||||
|
||||
for item in itemPool:
|
||||
item.World = world
|
||||
|
||||
return itemPool
|
||||
|
||||
# The order of the dungeon pool is significant
|
||||
@staticmethod
|
||||
def CreateDungeonPool(world):
|
||||
itemPool = [Item(ItemType.BigKeyGT)]
|
||||
Item.AddRange(itemPool, 4, Item(ItemType.KeyGT))
|
||||
if (not world.Config.Keysanity):
|
||||
itemPool += [
|
||||
Item(ItemType.MapGT),
|
||||
Item(ItemType.CompassGT),
|
||||
]
|
||||
itemPool += [
|
||||
Item(ItemType.BigKeyEP),
|
||||
Item(ItemType.BigKeyDP),
|
||||
Item(ItemType.BigKeyTH),
|
||||
Item(ItemType.BigKeyPD),
|
||||
Item(ItemType.BigKeySP),
|
||||
Item(ItemType.BigKeySW),
|
||||
Item(ItemType.BigKeyTT),
|
||||
Item(ItemType.BigKeyIP),
|
||||
Item(ItemType.BigKeyMM),
|
||||
Item(ItemType.BigKeyTR),
|
||||
]
|
||||
|
||||
Item.AddRange(itemPool, 1, Item(ItemType.KeyHC))
|
||||
Item.AddRange(itemPool, 2, Item(ItemType.KeyCT))
|
||||
Item.AddRange(itemPool, 1, Item(ItemType.KeyDP))
|
||||
Item.AddRange(itemPool, 1, Item(ItemType.KeyTH))
|
||||
Item.AddRange(itemPool, 6, Item(ItemType.KeyPD))
|
||||
Item.AddRange(itemPool, 1, Item(ItemType.KeySP))
|
||||
Item.AddRange(itemPool, 3, Item(ItemType.KeySW))
|
||||
Item.AddRange(itemPool, 1, Item(ItemType.KeyTT))
|
||||
Item.AddRange(itemPool, 2, Item(ItemType.KeyIP))
|
||||
Item.AddRange(itemPool, 3, Item(ItemType.KeyMM))
|
||||
Item.AddRange(itemPool, 4, Item(ItemType.KeyTR))
|
||||
|
||||
itemPool += [
|
||||
Item(ItemType.MapEP),
|
||||
Item(ItemType.MapDP),
|
||||
Item(ItemType.MapTH),
|
||||
Item(ItemType.MapPD),
|
||||
Item(ItemType.MapSP),
|
||||
Item(ItemType.MapSW),
|
||||
Item(ItemType.MapTT),
|
||||
Item(ItemType.MapIP),
|
||||
Item(ItemType.MapMM),
|
||||
Item(ItemType.MapTR),
|
||||
]
|
||||
if (not world.Config.Keysanity):
|
||||
itemPool += [
|
||||
Item(ItemType.MapHC),
|
||||
Item(ItemType.CompassEP),
|
||||
Item(ItemType.CompassDP),
|
||||
Item(ItemType.CompassTH),
|
||||
Item(ItemType.CompassPD),
|
||||
Item(ItemType.CompassSP),
|
||||
Item(ItemType.CompassSW),
|
||||
Item(ItemType.CompassTT),
|
||||
Item(ItemType.CompassIP),
|
||||
Item(ItemType.CompassMM),
|
||||
Item(ItemType.CompassTR),
|
||||
]
|
||||
|
||||
for item in itemPool:
|
||||
item.World = world
|
||||
|
||||
return itemPool
|
||||
|
||||
@staticmethod
|
||||
def CreateKeycards(world):
|
||||
itemPool = [
|
||||
Item(ItemType.CardCrateriaL1, world),
|
||||
Item(ItemType.CardCrateriaL2, world),
|
||||
Item(ItemType.CardCrateriaBoss, world),
|
||||
Item(ItemType.CardBrinstarL1, world),
|
||||
Item(ItemType.CardBrinstarL2, world),
|
||||
Item(ItemType.CardBrinstarBoss, world),
|
||||
Item(ItemType.CardNorfairL1, world),
|
||||
Item(ItemType.CardNorfairL2, world),
|
||||
Item(ItemType.CardNorfairBoss, world),
|
||||
Item(ItemType.CardMaridiaL1, world),
|
||||
Item(ItemType.CardMaridiaL2, world),
|
||||
Item(ItemType.CardMaridiaBoss, world),
|
||||
Item(ItemType.CardWreckedShipL1, world),
|
||||
Item(ItemType.CardWreckedShipBoss, world),
|
||||
Item(ItemType.CardLowerNorfairL1, world),
|
||||
Item(ItemType.CardLowerNorfairBoss, world),
|
||||
]
|
||||
|
||||
for item in itemPool:
|
||||
item.Progression = True
|
||||
item.World = world
|
||||
|
||||
return itemPool
|
||||
|
||||
@staticmethod
|
||||
def Get(items, itemType:ItemType):
|
||||
item = next((i for i in items if i.Type == itemType), None)
|
||||
if (item == None):
|
||||
raise Exception(f"Could not find an item of type {itemType}")
|
||||
return item
|
||||
|
||||
@staticmethod
|
||||
def Get(items, itemType:ItemType, world):
|
||||
item = next((i for i in items if i.Is(itemType, world)), None)
|
||||
if (item == None):
|
||||
raise Exception(f"Could not find an item of type {itemType} in world {world.Id}")
|
||||
return item
|
||||
|
||||
class Progression:
|
||||
BigKeyEP: bool
|
||||
BigKeyDP: bool
|
||||
BigKeyTH: bool
|
||||
BigKeyPD: bool
|
||||
BigKeySP: bool
|
||||
BigKeySW: bool
|
||||
BigKeyTT: bool
|
||||
BigKeyIP: bool
|
||||
BigKeyMM: bool
|
||||
BigKeyTR: bool
|
||||
BigKeyGT: bool
|
||||
KeyHC: bool
|
||||
KeyDP: bool
|
||||
KeyTH: bool
|
||||
KeySP: bool
|
||||
KeyTT: bool
|
||||
KeyCT: bool
|
||||
KeyPD: bool
|
||||
KeySW: bool
|
||||
KeyIP: bool
|
||||
KeyMM: bool
|
||||
KeyTR: bool
|
||||
KeyGT: bool
|
||||
CardCrateriaL1: bool
|
||||
CardCrateriaL2: bool
|
||||
CardCrateriaBoss: bool
|
||||
CardBrinstarL1: bool
|
||||
CardBrinstarL2: bool
|
||||
CardBrinstarBoss: bool
|
||||
CardNorfairL1: bool
|
||||
CardNorfairL2: bool
|
||||
CardNorfairBoss: bool
|
||||
CardMaridiaL1: bool
|
||||
CardMaridiaL2: bool
|
||||
CardMaridiaBoss: bool
|
||||
CardWreckedShipL1: bool
|
||||
CardWreckedShipBoss: bool
|
||||
CardLowerNorfairL1: bool
|
||||
CardLowerNorfairBoss: bool
|
||||
def CanBlockLasers(self): return self.shield >= 3
|
||||
Sword: bool
|
||||
MasterSword: bool
|
||||
Bow: bool
|
||||
Hookshot: bool
|
||||
Mushroom: bool
|
||||
Powder: bool
|
||||
Firerod: bool
|
||||
Icerod: bool
|
||||
Bombos: bool
|
||||
Ether: bool
|
||||
Quake: bool
|
||||
Lamp: bool
|
||||
Hammer: bool
|
||||
Shovel: bool
|
||||
Flute: bool
|
||||
Book: bool
|
||||
Bottle: bool
|
||||
Somaria: bool
|
||||
Byrna: bool
|
||||
Cape: bool
|
||||
Mirror: bool
|
||||
Boots: bool
|
||||
Glove: bool
|
||||
Mitt: bool
|
||||
Flippers: bool
|
||||
MoonPearl: bool
|
||||
HalfMagic: bool
|
||||
Grapple: bool
|
||||
Charge: bool
|
||||
Ice: bool
|
||||
Wave: bool
|
||||
Plasma: bool
|
||||
Varia: bool
|
||||
Gravity: bool
|
||||
Morph: bool
|
||||
Bombs: bool
|
||||
SpringBall: bool
|
||||
ScrewAttack: bool
|
||||
HiJump: bool
|
||||
SpaceJump: bool
|
||||
SpeedBooster: bool
|
||||
Missile: bool
|
||||
Super: bool
|
||||
PowerBomb: bool
|
||||
TwoPowerBombs: bool
|
||||
ETank: int
|
||||
ReserveTank: int
|
||||
|
||||
shield: int
|
||||
|
||||
itemMapping = [
|
||||
ItemType.BigKeyEP,
|
||||
ItemType.BigKeyDP,
|
||||
ItemType.BigKeyTH,
|
||||
ItemType.BigKeyPD,
|
||||
ItemType.BigKeySP,
|
||||
ItemType.BigKeySW,
|
||||
ItemType.BigKeyTT,
|
||||
ItemType.BigKeyIP,
|
||||
ItemType.BigKeyMM,
|
||||
ItemType.BigKeyTR,
|
||||
ItemType.BigKeyGT,
|
||||
ItemType.KeyHC,
|
||||
ItemType.KeyDP,
|
||||
ItemType.KeyTH,
|
||||
ItemType.KeySP,
|
||||
ItemType.KeyTT,
|
||||
ItemType.CardCrateriaL1,
|
||||
ItemType.CardCrateriaL2,
|
||||
ItemType.CardCrateriaBoss,
|
||||
ItemType.CardBrinstarL1,
|
||||
ItemType.CardBrinstarL2,
|
||||
ItemType.CardBrinstarBoss,
|
||||
ItemType.CardNorfairL1,
|
||||
ItemType.CardNorfairL2,
|
||||
ItemType.CardNorfairBoss,
|
||||
ItemType.CardMaridiaL1,
|
||||
ItemType.CardMaridiaL2,
|
||||
ItemType.CardMaridiaBoss,
|
||||
ItemType.CardWreckedShipL1,
|
||||
ItemType.CardWreckedShipBoss,
|
||||
ItemType.CardLowerNorfairL1,
|
||||
ItemType.CardLowerNorfairBoss,
|
||||
ItemType.Bow,
|
||||
ItemType.Hookshot,
|
||||
ItemType.Mushroom,
|
||||
ItemType.Powder,
|
||||
ItemType.Firerod,
|
||||
ItemType.Icerod,
|
||||
ItemType.Bombos,
|
||||
ItemType.Ether,
|
||||
ItemType.Quake,
|
||||
ItemType.Lamp,
|
||||
ItemType.Hammer,
|
||||
ItemType.Shovel,
|
||||
ItemType.Flute,
|
||||
ItemType.Book,
|
||||
ItemType.Bottle,
|
||||
ItemType.Somaria,
|
||||
ItemType.Byrna,
|
||||
ItemType.Cape,
|
||||
ItemType.Mirror,
|
||||
ItemType.Boots,
|
||||
ItemType.Flippers,
|
||||
ItemType.MoonPearl,
|
||||
ItemType.HalfMagic,
|
||||
ItemType.Grapple,
|
||||
ItemType.Charge,
|
||||
ItemType.Ice,
|
||||
ItemType.Wave,
|
||||
ItemType.Plasma,
|
||||
ItemType.Varia,
|
||||
ItemType.Gravity,
|
||||
ItemType.Morph,
|
||||
ItemType.Bombs,
|
||||
ItemType.SpringBall,
|
||||
ItemType.ScrewAttack,
|
||||
ItemType.HiJump,
|
||||
ItemType.SpaceJump,
|
||||
ItemType.SpeedBooster,
|
||||
ItemType.Missile,
|
||||
ItemType.Super,
|
||||
]
|
||||
|
||||
def __init__(self, items):
|
||||
for item in Progression.itemMapping:
|
||||
setattr(self, item.name, False)
|
||||
self.KeyCT = 0
|
||||
self.KeyPD = 0
|
||||
self.KeySW = 0
|
||||
self.KeyIP = 0
|
||||
self.KeyMM = 0
|
||||
self.KeyTR = 0
|
||||
self.KeyGT = 0
|
||||
self.ETank = 0
|
||||
self.ReserveTank = 0
|
||||
self.shield = 0
|
||||
self.MasterSword = False
|
||||
self.Sword = False
|
||||
self.Mitt = False
|
||||
self.Glove = False
|
||||
self.TwoPowerBombs = False
|
||||
self.PowerBomb = False
|
||||
self.Add(items)
|
||||
|
||||
def Add(self, items:List[Item]):
|
||||
for item in items:
|
||||
found = item.Type in Progression.itemMapping
|
||||
if found:
|
||||
setattr(self, item.Type.name, True)
|
||||
continue
|
||||
|
||||
if (item.Type == ItemType.KeyCT):
|
||||
self.KeyCT += 1
|
||||
elif (item.Type == ItemType.KeyPD):
|
||||
self.KeyPD += 1
|
||||
elif (item.Type == ItemType.KeySW):
|
||||
self.KeySW += 1
|
||||
elif (item.Type == ItemType.KeyIP):
|
||||
self.KeyIP += 1
|
||||
elif (item.Type == ItemType.KeyMM):
|
||||
self.KeyMM += 1
|
||||
elif (item.Type == ItemType.KeyTR):
|
||||
self.KeyTR += 1
|
||||
elif (item.Type == ItemType.KeyGT):
|
||||
self.KeyGT += 1
|
||||
elif (item.Type == ItemType.ETank):
|
||||
self.ETank += 1
|
||||
elif (item.Type == ItemType.ReserveTank):
|
||||
self.ReserveTank += 1
|
||||
elif (item.Type == ItemType.KeyPD):
|
||||
self.shield += 1
|
||||
elif (item.Type == ItemType.ProgressiveSword):
|
||||
self.MasterSword = self.Sword
|
||||
self.Sword = True
|
||||
elif (item.Type == ItemType.ProgressiveGlove):
|
||||
self.Mitt = self.Glove
|
||||
self.Glove = True
|
||||
elif (item.Type == ItemType.PowerBomb):
|
||||
self.TwoPowerBombs = self.PowerBomb
|
||||
self.PowerBomb = True
|
||||
|
||||
def Remove(self, items:List[Item]):
|
||||
for item in items:
|
||||
found = item.Type in Progression.itemMapping
|
||||
if found:
|
||||
setattr(self, item.Type.name, False)
|
||||
continue
|
||||
|
||||
if (item.Type == ItemType.KeyCT):
|
||||
self.KeyCT -= 1
|
||||
elif (item.Type == ItemType.KeyPD):
|
||||
self.KeyPD -= 1
|
||||
elif (item.Type == ItemType.KeySW):
|
||||
self.KeySW -= 1
|
||||
elif (item.Type == ItemType.KeyIP):
|
||||
self.KeyIP -= 1
|
||||
elif (item.Type == ItemType.KeyMM):
|
||||
self.KeyMM -= 1
|
||||
elif (item.Type == ItemType.KeyTR):
|
||||
self.KeyTR -= 1
|
||||
elif (item.Type == ItemType.KeyGT):
|
||||
self.KeyGT -= 1
|
||||
elif (item.Type == ItemType.ETank):
|
||||
self.ETank -= 1
|
||||
elif (item.Type == ItemType.ReserveTank):
|
||||
self.ReserveTank -= 1
|
||||
elif (item.Type == ItemType.KeyPD):
|
||||
self.shield -= 1
|
||||
elif (item.Type == ItemType.ProgressiveSword):
|
||||
self.Sword = self.MasterSword
|
||||
self.MasterSword = False
|
||||
elif (item.Type == ItemType.ProgressiveGlove):
|
||||
self.Glove = self.Mitt
|
||||
self.Mitt = False
|
||||
elif (item.Type == ItemType.PowerBomb):
|
||||
self.PowerBomb = self.TwoPowerBombs
|
||||
self.TwoPowerBombs = False
|
||||
|
||||
def CanLiftLight(self): return self.Glove
|
||||
|
||||
def CanLiftHeavy(self): return self.Mitt
|
||||
|
||||
def CanLightTorches(self): return self.Firerod or self.Lamp
|
||||
|
||||
def CanMeltFreezors(self): return self.Firerod or self.Bombos and self.Sword
|
||||
|
||||
def CanExtendMagic(self, bars:int = 2): return (2 if self.HalfMagic else 1) * (2 if self.Bottle else 1) >= bars
|
||||
|
||||
def CanKillManyEnemies(self):
|
||||
return self.Sword or self.Hammer or self.Bow or self.Firerod or \
|
||||
self.Somaria or self.Byrna and self.CanExtendMagic()
|
||||
|
||||
def CanAccessDeathMountainPortal(self):
|
||||
return (self.CanDestroyBombWalls() or self.SpeedBooster) and self.Super and self.Morph
|
||||
|
||||
|
||||
def CanAccessDarkWorldPortal(self, config: Config):
|
||||
if (config.SMLogic == SMLogic.Normal):
|
||||
return self.CardMaridiaL1 and self.CardMaridiaL2 and self.CanUsePowerBombs() and self.Super and self.Gravity and self.SpeedBooster
|
||||
else:
|
||||
return self.CardMaridiaL1 and self.CardMaridiaL2 and self.CanUsePowerBombs() and self.Super and \
|
||||
(self.Charge or self.Super and self.Missile) and \
|
||||
(self.Gravity or self.HiJump and self.Ice and self.Grapple) and \
|
||||
(self.Ice or self.Gravity and self.SpeedBooster)
|
||||
|
||||
|
||||
def CanAccessMiseryMirePortal(self, config: Config):
|
||||
if (config.SMLogic == SMLogic.Normal):
|
||||
return (self.CardNorfairL2 or (self.SpeedBooster and self.Wave)) and self.Varia and self.Super and (self.Gravity and self.SpaceJump) and self.CanUsePowerBombs()
|
||||
else:
|
||||
return (self.CardNorfairL2 or self.SpeedBooster) and self.Varia and self.Super and \
|
||||
(self.CanFly() or self.HiJump or self.SpeedBooster or self.CanSpringBallJump() or self.Ice) \
|
||||
and (self.Gravity or self.HiJump) and self.CanUsePowerBombs()
|
||||
|
||||
def CanIbj(self):
|
||||
return self.Morph and self.Bombs
|
||||
|
||||
def CanFly(self):
|
||||
return self.SpaceJump or self.CanIbj()
|
||||
|
||||
def CanUsePowerBombs(self):
|
||||
return self.Morph and self.PowerBomb
|
||||
|
||||
def CanPassBombPassages(self):
|
||||
return self.Morph and (self.Bombs or self.PowerBomb)
|
||||
|
||||
def CanDestroyBombWalls(self):
|
||||
return self.CanPassBombPassages() or self.ScrewAttack
|
||||
|
||||
def CanSpringBallJump(self):
|
||||
return self.Morph and self.SpringBall
|
||||
|
||||
def CanHellRun(self):
|
||||
return self.Varia or self.HasEnergyReserves(5)
|
||||
|
||||
def HasEnergyReserves(self, amount: int):
|
||||
return (self.ETank + self.ReserveTank) >= amount
|
||||
|
||||
def CanOpenRedDoors(self):
|
||||
return self.Missile or self.Super
|
||||
|
||||
def CanAccessNorfairUpperPortal(self):
|
||||
return self.Flute or self.CanLiftLight() and self.Lamp
|
||||
|
||||
def CanAccessNorfairLowerPortal(self):
|
||||
return self.Flute and self.CanLiftHeavy()
|
||||
|
||||
def CanAccessMaridiaPortal(self, world):
|
||||
import worlds.smz3.TotalSMZ3.Region
|
||||
if (world.Config.SMLogic == SMLogic.Normal):
|
||||
return self.MoonPearl and self.Flippers and \
|
||||
self.Gravity and self.Morph and \
|
||||
(world.CanAquire(self, worlds.smz3.TotalSMZ3.Region.RewardType.Agahnim) or self.Hammer and self.CanLiftLight() or self.CanLiftHeavy())
|
||||
else:
|
||||
return self.MoonPearl and self.Flippers and \
|
||||
(self.CanSpringBallJump() or self.HiJump or self.Gravity) and self.Morph and \
|
||||
(world.CanAquire(self, worlds.smz3.TotalSMZ3.Region.RewardType.Agahnim) or self.Hammer and self.CanLiftLight() or self.CanLiftHeavy())
|
||||
|
||||
# Start of AP integration
|
||||
items_start_id = 84000
|
||||
|
||||
lookup_id_to_name = { items_start_id + enum.value : enum.name for enum in ItemType }
|
||||
lookup_name_to_id = { item_name : item_id for item_id, item_name in lookup_id_to_name.items() }
|
|
@ -0,0 +1,112 @@
|
|||
from enum import Enum
|
||||
from typing import List, Callable
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
import worlds.smz3.TotalSMZ3.Region as Region
|
||||
import worlds.smz3.TotalSMZ3.World as World
|
||||
|
||||
class LocationType(Enum):
|
||||
Regular = 0
|
||||
HeraStandingKey = 1
|
||||
Pedestal = 2
|
||||
Ether = 3
|
||||
Bombos = 4
|
||||
NotInDungeon = 5
|
||||
|
||||
Visible = 6
|
||||
Chozo = 7
|
||||
Hidden = 8
|
||||
|
||||
# delegate bool Requirement(Progression items);
|
||||
# delegate bool Verification(Item item, Progression items);
|
||||
|
||||
class Location:
|
||||
Id: int
|
||||
Name: str
|
||||
Type: LocationType
|
||||
Address: int
|
||||
Region: Region
|
||||
|
||||
def Weight(self): return self.weight if self.weight != None else self.Region.Weight
|
||||
|
||||
canAccess: Callable = lambda items: True
|
||||
alwaysAllow: Callable = lambda item, items: True
|
||||
allow: Callable = lambda item, items: True
|
||||
weight: int
|
||||
|
||||
def ItemIs(self, type, world: World):
|
||||
item = self.APLocation.item.item if self.APLocation.item is not None and self.APLocation.item.game == "SMZ3" else None
|
||||
return item.Is(type, world) if item != None else False
|
||||
def ItemIsNot(self, type, world: World): return not self.ItemIs(type, world)
|
||||
|
||||
def __init__(self, region: Region, id: int, address: int, type: LocationType, name: str, access: Callable = lambda items : True):
|
||||
self.Region = region
|
||||
self.Id = id
|
||||
self.Name = name
|
||||
self.Type = type
|
||||
self.Item = None
|
||||
self.Address = address
|
||||
self.canAccess = access
|
||||
self.alwaysAllow = lambda item, items: False
|
||||
self.allow = lambda item, items: True
|
||||
|
||||
def Weighted(self, weight: int):
|
||||
self.weight = weight
|
||||
return self
|
||||
|
||||
def AlwaysAllow(self, allow: Callable):
|
||||
self.alwaysAllow = allow
|
||||
return self
|
||||
|
||||
def Allow(self, allow: Callable):
|
||||
self.allow = allow
|
||||
return self
|
||||
|
||||
def Available(self, items: Progression):
|
||||
return self.Region.CanEnter(items) and self.canAccess(items)
|
||||
|
||||
def CanFill(self, item, items: Progression):
|
||||
oldItem = self.Item
|
||||
self.Item = item
|
||||
fillable = self.alwaysAllow(item, items) or (self.Region.CanFill(item) and self.allow(item, items) and self.Available(items))
|
||||
self.Item = oldItem
|
||||
return fillable
|
||||
|
||||
@staticmethod
|
||||
def Get(locations, name: str):
|
||||
loc = next((l for l in locations if l.Name == name), None)
|
||||
if (loc == None):
|
||||
raise Exception(f"Could not find location name {name}")
|
||||
return loc
|
||||
|
||||
@staticmethod
|
||||
def Empty(locations):
|
||||
return [l for l in locations if l.Item == None]
|
||||
|
||||
@staticmethod
|
||||
def Filled(locations):
|
||||
return [l for l in locations if l.Item != None]
|
||||
|
||||
@staticmethod
|
||||
def AvailableWithinWorld(locations, items):
|
||||
result = []
|
||||
worldList = []
|
||||
[worldList.append(l.Region.World) for l in locations if l.Region.World not in worldList]
|
||||
for world in worldList:
|
||||
result += Location.AvailableGlobal([l for l in locations if l.Region.World == world], [i for i in items if i.World == world])
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def AvailableGlobal(locations, items):
|
||||
progression = Progression(items)
|
||||
return [l for l in locations if l.Available(progression)]
|
||||
|
||||
@staticmethod
|
||||
def CanFillWithinWorld(locations, item, items):
|
||||
itemWorldProgression = Progression([i for i in items if i.World == item.World].append(item))
|
||||
worldList = []
|
||||
[worldList.append(l.Region.World) for l in locations if l.Region.World not in worldList]
|
||||
worldProgression = {world.Id : Progression([i for i in items if i.World == world]) for world in worldList}
|
||||
return [l for l in locations if l.CanFill(item, worldProgression[l.Region.World.Id] and next(ll for ll in item.World.Locations if ll.Id == l.Id).Available(itemWorldProgression))]
|
||||
|
||||
# Start of AP integration
|
||||
locations_start_id = 85000
|
|
@ -0,0 +1,809 @@
|
|||
from enum import Enum
|
||||
from logging import exception
|
||||
from typing import Any, Callable, List, Sequence
|
||||
import random
|
||||
import typing
|
||||
from BaseClasses import Location
|
||||
from worlds.smz3.TotalSMZ3.Item import Item, ItemType
|
||||
from worlds.smz3.TotalSMZ3.Location import LocationType
|
||||
from worlds.smz3.TotalSMZ3.Region import IMedallionAccess, IReward, RewardType, SMRegion, Z3Region
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.EasternPalace import EasternPalace
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.DesertPalace import DesertPalace
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.TowerOfHera import TowerOfHera
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.PalaceOfDarkness import PalaceOfDarkness
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.SwampPalace import SwampPalace
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.SkullWoods import SkullWoods
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.ThievesTown import ThievesTown
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.IcePalace import IcePalace
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.MiseryMire import MiseryMire
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.TurtleRock import TurtleRock
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.GanonsTower import GanonsTower
|
||||
from worlds.smz3.TotalSMZ3.Text.StringTable import StringTable
|
||||
|
||||
from worlds.smz3.TotalSMZ3.World import World
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, GameMode, GanonInvincible
|
||||
from worlds.smz3.TotalSMZ3.Text.Texts import Texts
|
||||
from worlds.smz3.TotalSMZ3.Text.Dialog import Dialog
|
||||
|
||||
class KeycardPlaque:
|
||||
Level1 = 0xe0
|
||||
Level2 = 0xe1
|
||||
Boss = 0xe2
|
||||
Null = 0x00
|
||||
|
||||
class KeycardDoors:
|
||||
Left = 0xd414
|
||||
Right = 0xd41a
|
||||
Up = 0xd420
|
||||
Down = 0xd426
|
||||
BossLeft = 0xc842
|
||||
BossRight = 0xc848
|
||||
|
||||
|
||||
class KeycardEvents:
|
||||
CrateriaLevel1 = 0x0000
|
||||
CrateriaLevel2 = 0x0100
|
||||
CrateriaBoss = 0x0200
|
||||
BrinstarLevel1 = 0x0300
|
||||
BrinstarLevel2 = 0x0400
|
||||
BrinstarBoss = 0x0500
|
||||
NorfairLevel1 = 0x0600
|
||||
NorfairLevel2 = 0x0700
|
||||
NorfairBoss = 0x0800
|
||||
MaridiaLevel1 = 0x0900
|
||||
MaridiaLevel2 = 0x0a00
|
||||
MaridiaBoss = 0x0b00
|
||||
WreckedShipLevel1 = 0x0c00
|
||||
WreckedShipBoss = 0x0d00
|
||||
LowerNorfairLevel1 = 0x0e00
|
||||
LowerNorfairBoss = 0x0f00
|
||||
|
||||
class DropPrize(Enum):
|
||||
Heart = 0xD8
|
||||
Green = 0xD9
|
||||
Blue = 0xDA
|
||||
Red = 0xDB
|
||||
Bomb1 = 0xDC
|
||||
Bomb4 = 0xDD
|
||||
Bomb8 = 0xDE
|
||||
Magic = 0xDF
|
||||
FullMagic = 0xE0
|
||||
Arrow5 = 0xE1
|
||||
Arrow10 = 0xE2
|
||||
Fairy = 0xE3
|
||||
|
||||
class Patch:
|
||||
Major = 0
|
||||
Minor = 1
|
||||
allWorlds: List[World]
|
||||
myWorld: World
|
||||
seedGuid: str
|
||||
seed: int
|
||||
rnd: random.Random
|
||||
patches: Sequence[Any]
|
||||
stringTable: StringTable
|
||||
silversWorldID: int
|
||||
|
||||
def __init__(self, myWorld: World, allWorlds: List[World], seedGuid: str, seed: int, rnd: random.Random, playerNames: List[str], silversWorldID: int):
|
||||
self.myWorld = myWorld
|
||||
self.allWorlds = allWorlds
|
||||
self.seedGuid = seedGuid
|
||||
self.seed = seed
|
||||
self.rnd = rnd
|
||||
self.playerNames = playerNames
|
||||
self.playerIDToNames = {id:name for name, id in playerNames.items()}
|
||||
self.silversWorldID = silversWorldID
|
||||
|
||||
def Create(self, config: Config):
|
||||
self.stringTable = StringTable()
|
||||
self.patches = []
|
||||
self.title = ""
|
||||
|
||||
self.WriteMedallions()
|
||||
self.WriteRewards()
|
||||
self.WriteDungeonMusic(config.Keysanity)
|
||||
|
||||
self.WriteDiggingGameRng()
|
||||
|
||||
self.WritePrizeShuffle()
|
||||
|
||||
self.WriteRemoveEquipmentFromUncle( self.myWorld.GetLocation("Link's Uncle").APLocation.item.item if
|
||||
self.myWorld.GetLocation("Link's Uncle").APLocation.item.game == "SMZ3" else
|
||||
Item(ItemType.Something))
|
||||
|
||||
self.WriteGanonInvicible(config.GanonInvincible)
|
||||
self.WriteRngBlock()
|
||||
|
||||
self.WriteSaveAndQuitFromBossRoom()
|
||||
self.WriteWorldOnAgahnimDeath()
|
||||
|
||||
self.WriteTexts(config)
|
||||
|
||||
self.WriteSMLocations([loc for region in self.myWorld.Regions for loc in region.Locations if isinstance(region, SMRegion)])
|
||||
self.WriteZ3Locations([loc for region in self.myWorld.Regions for loc in region.Locations if isinstance(region, Z3Region)])
|
||||
|
||||
self.WriteStringTable()
|
||||
|
||||
self.WriteSMKeyCardDoors()
|
||||
self.WriteZ3KeysanityFlags()
|
||||
|
||||
self.WritePlayerNames()
|
||||
self.WriteSeedData()
|
||||
self.WriteGameTitle()
|
||||
self.WriteCommonFlags()
|
||||
|
||||
return {patch[0]:patch[1] for patch in self.patches}
|
||||
|
||||
def WriteMedallions(self):
|
||||
turtleRock = next(region for region in self.myWorld.Regions if isinstance(region, TurtleRock))
|
||||
miseryMire = next(region for region in self.myWorld.Regions if isinstance(region, MiseryMire))
|
||||
|
||||
turtleRockAddresses = [0x308023, 0xD020, 0xD0FF, 0xD1DE ]
|
||||
miseryMireAddresses = [ 0x308022, 0xCFF2, 0xD0D1, 0xD1B0 ]
|
||||
|
||||
if turtleRock.Medallion == ItemType.Bombos:
|
||||
turtleRockValues = [0x00, 0x51, 0x10, 0x00]
|
||||
elif turtleRock.Medallion == ItemType.Ether:
|
||||
turtleRockValues = [0x01, 0x51, 0x18, 0x00]
|
||||
elif turtleRock.Medallion == ItemType.Quake:
|
||||
turtleRockValues = [0x02, 0x14, 0xEF, 0xC4]
|
||||
else:
|
||||
raise exception(f"Tried using {turtleRock.Medallion} in place of Turtle Rock medallion")
|
||||
|
||||
if miseryMire.Medallion == ItemType.Bombos:
|
||||
miseryMireValues = [0x00, 0x51, 0x00, 0x00]
|
||||
elif miseryMire.Medallion == ItemType.Ether:
|
||||
miseryMireValues = [0x01, 0x13, 0x9F, 0xF1]
|
||||
elif miseryMire.Medallion == ItemType.Quake:
|
||||
miseryMireValues = [0x02, 0x51, 0x08, 0x00]
|
||||
else:
|
||||
raise exception(f"Tried using {miseryMire.Medallion} in place of Misery Mire medallion")
|
||||
|
||||
self.patches += [(Snes(addr), [value]) for addr, value in zip(turtleRockAddresses, turtleRockValues)]
|
||||
self.patches += [(Snes(addr), [value]) for addr, value in zip(miseryMireAddresses, miseryMireValues)]
|
||||
|
||||
def WriteRewards(self):
|
||||
crystalsBlue = [ 1, 2, 3, 4, 7 ]
|
||||
self.rnd.shuffle(crystalsBlue)
|
||||
crystalsRed = [ 5, 6 ]
|
||||
self.rnd.shuffle(crystalsRed)
|
||||
crystalRewards = crystalsBlue + crystalsRed
|
||||
|
||||
pendantsGreen = [ 1 ]
|
||||
pendantsBlueRed = [ 2, 3 ]
|
||||
self.rnd.shuffle(pendantsBlueRed)
|
||||
pendantRewards = pendantsGreen + pendantsBlueRed
|
||||
|
||||
regions = [region for region in self.myWorld.Regions if isinstance(region, IReward)]
|
||||
crystalRegions = [region for region in regions if region.Reward == RewardType.CrystalBlue] + [region for region in regions if region.Reward == RewardType.CrystalRed]
|
||||
pendantRegions = [region for region in regions if region.Reward == RewardType.PendantGreen] + [region for region in regions if region.Reward == RewardType.PendantNonGreen]
|
||||
|
||||
self.patches += self.RewardPatches(crystalRegions, crystalRewards, self.CrystalValues)
|
||||
self.patches += self.RewardPatches(pendantRegions, pendantRewards, self.PendantValues)
|
||||
|
||||
def RewardPatches(self, regions: List[IReward], rewards: List[int], rewardValues: Callable):
|
||||
addresses = [self.RewardAddresses(region) for region in regions]
|
||||
values = [rewardValues(reward) for reward in rewards]
|
||||
associations = zip(addresses, values)
|
||||
return [(Snes(i), [b]) for association in associations for i,b in zip(association[0], association[1])]
|
||||
|
||||
def RewardAddresses(self, region: IReward):
|
||||
regionType = {
|
||||
EasternPalace : [ 0x2A09D, 0xABEF8, 0xABEF9, 0x308052, 0x30807C, 0x1C6FE ],
|
||||
DesertPalace : [ 0x2A09E, 0xABF1C, 0xABF1D, 0x308053, 0x308078, 0x1C6FF ],
|
||||
TowerOfHera : [ 0x2A0A5, 0xABF0A, 0xABF0B, 0x30805A, 0x30807A, 0x1C706 ],
|
||||
PalaceOfDarkness : [ 0x2A0A1, 0xABF00, 0xABF01, 0x308056, 0x30807D, 0x1C702 ],
|
||||
SwampPalace : [ 0x2A0A0, 0xABF6C, 0xABF6D, 0x308055, 0x308071, 0x1C701 ],
|
||||
SkullWoods : [ 0x2A0A3, 0xABF12, 0xABF13, 0x308058, 0x30807B, 0x1C704 ],
|
||||
ThievesTown : [ 0x2A0A6, 0xABF36, 0xABF37, 0x30805B, 0x308077, 0x1C707 ],
|
||||
IcePalace : [ 0x2A0A4, 0xABF5A, 0xABF5B, 0x308059, 0x308073, 0x1C705 ],
|
||||
MiseryMire : [ 0x2A0A2, 0xABF48, 0xABF49, 0x308057, 0x308075, 0x1C703 ],
|
||||
TurtleRock : [ 0x2A0A7, 0xABF24, 0xABF25, 0x30805C, 0x308079, 0x1C708 ]
|
||||
}
|
||||
result = regionType.get(type(region), None)
|
||||
if result is None:
|
||||
raise exception(f"Region {region} should not be a dungeon reward region")
|
||||
else:
|
||||
return result
|
||||
|
||||
def CrystalValues(self, crystal: int):
|
||||
crystalMap = {
|
||||
1 : [ 0x02, 0x34, 0x64, 0x40, 0x7F, 0x06 ],
|
||||
2 : [ 0x10, 0x34, 0x64, 0x40, 0x79, 0x06 ],
|
||||
3 : [ 0x40, 0x34, 0x64, 0x40, 0x6C, 0x06 ],
|
||||
4 : [ 0x20, 0x34, 0x64, 0x40, 0x6D, 0x06 ],
|
||||
5 : [ 0x04, 0x32, 0x64, 0x40, 0x6E, 0x06 ],
|
||||
6 : [ 0x01, 0x32, 0x64, 0x40, 0x6F, 0x06 ],
|
||||
7 : [ 0x08, 0x34, 0x64, 0x40, 0x7C, 0x06 ],
|
||||
}
|
||||
result = crystalMap.get(crystal, None)
|
||||
if result is None:
|
||||
raise exception(f"Tried using {crystal} as a crystal number")
|
||||
else:
|
||||
return result
|
||||
|
||||
def PendantValues(self, pendant: int):
|
||||
pendantMap = {
|
||||
1 : [ 0x04, 0x38, 0x62, 0x00, 0x69, 0x01 ],
|
||||
2 : [ 0x01, 0x32, 0x60, 0x00, 0x69, 0x03 ],
|
||||
3 : [ 0x02, 0x34, 0x60, 0x00, 0x69, 0x02 ],
|
||||
}
|
||||
result = pendantMap.get(pendant, None)
|
||||
if result is None:
|
||||
raise exception(f"Tried using {pendant} as a pendant number")
|
||||
else:
|
||||
return result
|
||||
|
||||
def WriteSMLocations(self, locations: List[Location]):
|
||||
def GetSMItemPLM(location:Location):
|
||||
itemMap = {
|
||||
ItemType.ETank : 0xEED7,
|
||||
ItemType.Missile : 0xEEDB,
|
||||
ItemType.Super : 0xEEDF,
|
||||
ItemType.PowerBomb : 0xEEE3,
|
||||
ItemType.Bombs : 0xEEE7,
|
||||
ItemType.Charge : 0xEEEB,
|
||||
ItemType.Ice : 0xEEEF,
|
||||
ItemType.HiJump : 0xEEF3,
|
||||
ItemType.SpeedBooster : 0xEEF7,
|
||||
ItemType.Wave : 0xEEFB,
|
||||
ItemType.Spazer : 0xEEFF,
|
||||
ItemType.SpringBall : 0xEF03,
|
||||
ItemType.Varia : 0xEF07,
|
||||
ItemType.Plasma : 0xEF13,
|
||||
ItemType.Grapple : 0xEF17,
|
||||
ItemType.Morph : 0xEF23,
|
||||
ItemType.ReserveTank : 0xEF27,
|
||||
ItemType.Gravity : 0xEF0B,
|
||||
ItemType.XRay : 0xEF0F,
|
||||
ItemType.SpaceJump : 0xEF1B,
|
||||
ItemType.ScrewAttack : 0xEF1F
|
||||
}
|
||||
plmId = 0xEFE0 if self.myWorld.Config.GameMode == GameMode.Multiworld else \
|
||||
itemMap.get(location.APLocation.item.item.Type, 0xEFE0)
|
||||
if (plmId == 0xEFE0):
|
||||
plmId += 4 if location.Type == LocationType.Chozo else 8 if location.Type == LocationType.Hidden else 0
|
||||
else:
|
||||
plmId += 0x54 if location.Type == LocationType.Chozo else 0xA8 if location.Type == LocationType.Hidden else 0
|
||||
return plmId
|
||||
|
||||
for location in locations:
|
||||
if (self.myWorld.Config.GameMode == GameMode.Multiworld):
|
||||
self.patches.append((Snes(location.Address), getWordArray(GetSMItemPLM(location))))
|
||||
self.patches.append(self.ItemTablePatch(location, self.GetZ3ItemId(location)))
|
||||
else:
|
||||
plmId = GetSMItemPLM(location)
|
||||
self.patches.append((Snes(location.Address), getWordArray(plmId)))
|
||||
if (plmId >= 0xEFE0):
|
||||
self.patches.append((Snes(location.Address + 5), [self.GetZ3ItemId(location)]))
|
||||
|
||||
def WriteZ3Locations(self, locations: List[Location]):
|
||||
for location in locations:
|
||||
if (location.Type == LocationType.HeraStandingKey):
|
||||
self.patches.append((Snes(0x9E3BB), [0xE4] if location.APLocation.item.game == "SMZ3" and location.APLocation.item.item.Type == ItemType.KeyTH else [0xEB]))
|
||||
elif (location.Type in [LocationType.Pedestal, LocationType.Ether, LocationType.Bombos]):
|
||||
text = Texts.ItemTextbox(location.APLocation.item.item if location.APLocation.item.game == "SMZ3" else Item(ItemType.Something))
|
||||
dialog = Dialog.Simple(text)
|
||||
if (location.Type == LocationType.Pedestal):
|
||||
self.stringTable.SetPedestalText(text)
|
||||
self.patches.append((Snes(0x308300), dialog))
|
||||
elif (location.Type == LocationType.Ether):
|
||||
self.stringTable.SetEtherText(text)
|
||||
self.patches.append((Snes(0x308F00), dialog))
|
||||
elif (location.Type == LocationType.Bombos):
|
||||
self.stringTable.SetBombosText(text)
|
||||
self.patches.append((Snes(0x309000), dialog))
|
||||
|
||||
if (self.myWorld.Config.GameMode == GameMode.Multiworld):
|
||||
self.patches.append((Snes(location.Address), [(location.Id - 256)]))
|
||||
self.patches.append(self.ItemTablePatch(location, self.GetZ3ItemId(location)))
|
||||
else:
|
||||
self.patches.append((Snes(location.Address), [self.GetZ3ItemId(location)]))
|
||||
|
||||
def GetZ3ItemId(self, location: Location):
|
||||
if (location.APLocation.item.game == "SMZ3"):
|
||||
item = location.APLocation.item.item
|
||||
itemDungeon = None
|
||||
if item.IsKey():
|
||||
itemDungeon = ItemType.Key if (not item.World.Config.Keysanity or item.Type != ItemType.KeyHC) else ItemType.KeyHC
|
||||
elif item.IsBigKey():
|
||||
itemDungeon = ItemType.BigKey
|
||||
elif item.IsMap():
|
||||
itemDungeon = ItemType.Map if (not item.World.Config.Keysanity or item.Type != ItemType.MapHC) else ItemType.MapHC
|
||||
elif item.IsCompass():
|
||||
itemDungeon = ItemType.Compass
|
||||
|
||||
value = item.Type if location.Type == LocationType.NotInDungeon or \
|
||||
not (item.IsDungeonItem() and location.Region.IsRegionItem(item) and item.World == self.myWorld) else itemDungeon
|
||||
|
||||
return value.value
|
||||
else:
|
||||
return ItemType.Something.value
|
||||
|
||||
def ItemTablePatch(self, location: Location, itemId: int):
|
||||
itemtype = 0 if location.APLocation.item.player == location.Region.world.Id else 1
|
||||
owner = location.APLocation.item.player
|
||||
return (0x386000 + (location.Id * 8), getWordArray(itemtype) + getWordArray(itemId) + getWordArray(owner))
|
||||
|
||||
def WriteDungeonMusic(self, keysanity: bool):
|
||||
if (not keysanity):
|
||||
regions = [region for region in self.myWorld.Regions if isinstance(region, IReward)]
|
||||
music = []
|
||||
pendantRegions = [region for region in regions if region.Reward in [RewardType.PendantGreen, RewardType.PendantNonGreen]]
|
||||
crystalRegions = [region for region in regions if region.Reward in [RewardType.CrystalBlue, RewardType.CrystalRed]]
|
||||
regions = pendantRegions + crystalRegions
|
||||
music = [
|
||||
0x11, 0x11, 0x11, 0x16, 0x16,
|
||||
0x16, 0x16, 0x16, 0x16, 0x16,
|
||||
]
|
||||
self.patches += self.MusicPatches(regions, music)
|
||||
|
||||
#IEnumerable<byte> RandomDungeonMusic() {
|
||||
# while (true) yield return rnd.Next(2) == 0 ? (byte)0x11 : (byte)0x16;
|
||||
#}
|
||||
|
||||
def MusicPatches(self, regions: List[IReward], music: List[int]):
|
||||
addresses = [self.MusicAddresses(region) for region in regions]
|
||||
associations = zip(addresses, music)
|
||||
return [(Snes(i), [association[1]]) for association in associations for i in association[0]]
|
||||
|
||||
def MusicAddresses(self, region: IReward):
|
||||
regionMap = {
|
||||
EasternPalace : [ 0x2D59A ],
|
||||
DesertPalace : [ 0x2D59B, 0x2D59C, 0x2D59D, 0x2D59E ],
|
||||
TowerOfHera : [ 0x2D5C5, 0x2907A, 0x28B8C ],
|
||||
PalaceOfDarkness : [ 0x2D5B8 ],
|
||||
SwampPalace : [ 0x2D5B7 ],
|
||||
SkullWoods : [ 0x2D5BA, 0x2D5BB, 0x2D5BC, 0x2D5BD, 0x2D608, 0x2D609, 0x2D60A, 0x2D60B ],
|
||||
ThievesTown : [ 0x2D5C6 ],
|
||||
IcePalace : [ 0x2D5BF ],
|
||||
MiseryMire : [ 0x2D5B9 ],
|
||||
TurtleRock : [ 0x2D5C7, 0x2D5A7, 0x2D5AA, 0x2D5AB ],
|
||||
}
|
||||
result = regionMap.get(type(region), None)
|
||||
if result is None:
|
||||
raise exception(f"Region {region} should not be a dungeon music region")
|
||||
else:
|
||||
return result
|
||||
|
||||
def WritePrizeShuffle(self):
|
||||
prizePackItems = 56
|
||||
treePullItems = 3
|
||||
|
||||
bytes = []
|
||||
drop = 0
|
||||
final = 0
|
||||
|
||||
pool = [
|
||||
DropPrize.Heart, DropPrize.Heart, DropPrize.Heart, DropPrize.Heart, DropPrize.Green, DropPrize.Heart, DropPrize.Heart, DropPrize.Green, #// pack 1
|
||||
DropPrize.Blue, DropPrize.Green, DropPrize.Blue, DropPrize.Red, DropPrize.Blue, DropPrize.Green, DropPrize.Blue, DropPrize.Blue, #// pack 2
|
||||
DropPrize.FullMagic, DropPrize.Magic, DropPrize.Magic, DropPrize.Blue, DropPrize.FullMagic, DropPrize.Magic, DropPrize.Heart, DropPrize.Magic, #// pack 3
|
||||
DropPrize.Bomb1, DropPrize.Bomb1, DropPrize.Bomb1, DropPrize.Bomb4, DropPrize.Bomb1, DropPrize.Bomb1, DropPrize.Bomb8, DropPrize.Bomb1, #// pack 4
|
||||
DropPrize.Arrow5, DropPrize.Heart, DropPrize.Arrow5, DropPrize.Arrow10, DropPrize.Arrow5, DropPrize.Heart, DropPrize.Arrow5, DropPrize.Arrow10,#// pack 5
|
||||
DropPrize.Magic, DropPrize.Green, DropPrize.Heart, DropPrize.Arrow5, DropPrize.Magic, DropPrize.Bomb1, DropPrize.Green, DropPrize.Heart, #// pack 6
|
||||
DropPrize.Heart, DropPrize.Fairy, DropPrize.FullMagic, DropPrize.Red, DropPrize.Bomb8, DropPrize.Heart, DropPrize.Red, DropPrize.Arrow10, #// pack 7
|
||||
DropPrize.Green, DropPrize.Blue, DropPrize.Red,#// from pull trees
|
||||
DropPrize.Green, DropPrize.Red,#// from prize crab
|
||||
DropPrize.Green, #// stunned prize
|
||||
DropPrize.Red,#// saved fish prize
|
||||
]
|
||||
|
||||
prizes = pool
|
||||
self.rnd.shuffle(prizes)
|
||||
|
||||
#/* prize pack drop order */
|
||||
(bytes, prizes) = SplitOff(prizes, prizePackItems)
|
||||
self.patches.append((Snes(0x6FA78), [byte.value for byte in bytes]))
|
||||
|
||||
#/* tree pull prizes */
|
||||
(bytes, prizes) = SplitOff(prizes, treePullItems)
|
||||
self.patches.append((Snes(0x1DFBD4), [byte.value for byte in bytes]))
|
||||
|
||||
#/* crab prizes */
|
||||
(drop, final, prizes) = (prizes[0], prizes[1], prizes[2:])
|
||||
self.patches.append((Snes(0x6A9C8), [ drop.value ]))
|
||||
self.patches.append((Snes(0x6A9C4), [ final.value ]))
|
||||
|
||||
#/* stun prize */
|
||||
(drop, prizes) = (prizes[0], prizes[1:])
|
||||
self.patches.append((Snes(0x6F993), [ drop.value ]))
|
||||
|
||||
#/* fish prize */
|
||||
drop = prizes[0]
|
||||
self.patches.append((Snes(0x1D82CC), [ drop.value ]))
|
||||
|
||||
self.patches += self.EnemyPrizePackDistribution()
|
||||
|
||||
#/* Pack drop chance */
|
||||
#/* Normal difficulty is 50%. 0 => 100%, 1 => 50%, 3 => 25% */
|
||||
nrPacks = 7
|
||||
probability = 1
|
||||
self.patches.append((Snes(0x6FA62), [probability] * nrPacks))
|
||||
|
||||
def EnemyPrizePackDistribution(self):
|
||||
(prizePacks, duplicatePacks) = self.EnemyPrizePacks()
|
||||
|
||||
n = sum(len(x[1]) for x in prizePacks)
|
||||
randomization = self.PrizePackRandomization(n, 1)
|
||||
patches = []
|
||||
for prizepack in prizePacks:
|
||||
(packs, randomization) = SplitOff(randomization, len(prizepack[1]))
|
||||
patches.append((prizepack[0], [(b | p) for b,p in zip(prizepack[1], packs)]))
|
||||
|
||||
duplicates = [(d[1], p[1])
|
||||
for d in duplicatePacks
|
||||
for p in patches
|
||||
if p[0] == d[0]]
|
||||
patches += duplicates
|
||||
|
||||
return [(Snes(x[0]), x[1]) for x in patches]
|
||||
|
||||
#/* Guarantees at least s of each prize pack, over a total of n packs.
|
||||
#* In each iteration, from the product n * m, use the guaranteed number
|
||||
#* at k, where k is the "row" (integer division by m), when k falls
|
||||
#* within the list boundary. Otherwise use the "column" (modulo by m)
|
||||
#* as the random element.
|
||||
#*/
|
||||
def PrizePackRandomization(self, n: int, s: int):
|
||||
m = 7
|
||||
g = list(range(0, m)) * s
|
||||
|
||||
def randomization(n: int):
|
||||
result = []
|
||||
n = m * n
|
||||
while (n > 0):
|
||||
r = self.rnd.randrange(0, n)
|
||||
k = r // m
|
||||
result.append(g[k] if k < len(g) else r % m)
|
||||
if (k < len(g)): del g[k]
|
||||
n -= m
|
||||
return result
|
||||
|
||||
return [(x + 1) for x in randomization(n)]
|
||||
|
||||
#/* Todo: Deadrock turns into $8F Blob when powdered, but those "onion blobs" always drop prize pack 1. */
|
||||
def EnemyPrizePacks(self):
|
||||
offset = 0xDB632
|
||||
patches = [
|
||||
#/* sprite_prep */
|
||||
(0x6888D, [ 0x00 ]), #// Keese DW
|
||||
(0x688A8, [ 0x00 ]), #// Rope
|
||||
(0x68967, [ 0x00, 0x00 ]), #// Crow/Dacto
|
||||
(0x69125, [ 0x00, 0x00 ]), #// Red/Blue Hardhat Bettle
|
||||
#/* sprite properties */
|
||||
(offset+0x01, [ 0x90 ]), #// Vulture
|
||||
(offset+0x08, [ 0x00 ]), #// Octorok (One Way)
|
||||
(offset+0x0A, [ 0x00 ]), #// Octorok (Four Way)
|
||||
(offset+0x0D, [ 0x80, 0x90 ]), #// Buzzblob, Snapdragon
|
||||
(offset+0x11, [ 0x90, 0x90, 0x00 ]), #// Hinox, Moblin, Mini Helmasaur
|
||||
(offset+0x18, [ 0x90, 0x90 ]), #// Mini Moldorm, Poe/Hyu
|
||||
(offset+0x20, [ 0x00 ]), #// Sluggula
|
||||
(offset+0x22, [ 0x80, 0x00, 0x00 ]), #// Ropa, Red Bari, Blue Bari
|
||||
#// Blue Soldier/Tarus, Green Soldier, Red Spear Soldier
|
||||
#// Blue Assault Soldier, Red Assault Spear Soldier/Tarus
|
||||
#// Blue Archer, Green Archer
|
||||
#// Red Javelin Soldier, Red Bush Javelin Soldier
|
||||
#// Red Bomb Soldiers, Green Soldier Recruits,
|
||||
#// Geldman, Toppo
|
||||
(offset+0x41, [ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x10, 0x90, 0x90, 0x80 ]),
|
||||
(offset+0x4F, [ 0x80 ]), #// Popo 2
|
||||
(offset+0x51, [ 0x80 ]), #// Armos
|
||||
(offset+0x55, [ 0x00, 0x00 ]), #// Ku, Zora
|
||||
(offset+0x58, [ 0x90 ]), #// Crab
|
||||
(offset+0x64, [ 0x80 ]), #// Devalant (Shooter)
|
||||
(offset+0x6A, [ 0x90, 0x90 ]), #// Ball N' Chain Trooper, Cannon Soldier
|
||||
(offset+0x6D, [ 0x80, 0x80 ]), #// Rat/Buzz, (Stal)Rope
|
||||
(offset+0x71, [ 0x80 ]), #// Leever
|
||||
(offset+0x7C, [ 0x90 ]), #// Initially Floating Stal
|
||||
(offset+0x81, [ 0xC0 ]), #// Hover
|
||||
#// Green Eyegore/Mimic, Red Eyegore/Mimic
|
||||
#// Detached Stalfos Body, Kodongo
|
||||
(offset+0x83, [ 0x10, 0x10, 0x10, 0x00 ]),
|
||||
(offset+0x8B, [ 0x10 ]), #// Gibdo
|
||||
(offset+0x8E, [ 0x00, 0x00 ]), #// Terrorpin, Blob
|
||||
(offset+0x91, [ 0x10 ]), #// Stalfos Knight
|
||||
(offset+0x99, [ 0x10 ]), #// Pengator
|
||||
(offset+0x9B, [ 0x10 ]), #// Wizzrobe
|
||||
#// Blue Zazak, Red Zazak, Stalfos
|
||||
#// Green Zirro, Blue Zirro, Pikit
|
||||
(offset+0xA5, [ 0x10, 0x10, 0x10, 0x80, 0x80, 0x80 ]),
|
||||
(offset+0xC7, [ 0x10 ]), #// Hokku-Bokku
|
||||
(offset+0xC9, [ 0x10 ]), #// Tektite
|
||||
(offset+0xD0, [ 0x10 ]), #// Lynel
|
||||
(offset+0xD3, [ 0x00 ]), #// Stal
|
||||
]
|
||||
duplicates = [
|
||||
#/* Popo2 -> Popo. Popo is not used in vanilla Z3, but we duplicate from Popo2 just to be sure */
|
||||
(offset + 0x4F, offset + 0x4E),
|
||||
]
|
||||
return (patches, duplicates)
|
||||
|
||||
def WriteTexts(self, config: Config):
|
||||
regions = [region for region in self.myWorld.Regions if isinstance(region, IReward)]
|
||||
greenPendantDungeon = [region for region in regions if region.Reward == RewardType.PendantGreen][0]
|
||||
redCrystalDungeons = [region for region in regions if region.Reward == RewardType.CrystalRed]
|
||||
|
||||
sahasrahla = Texts.SahasrahlaReveal(greenPendantDungeon)
|
||||
self.patches.append((Snes(0x308A00), Dialog.Simple(sahasrahla)))
|
||||
self.stringTable.SetSahasrahlaRevealText(sahasrahla)
|
||||
|
||||
bombShop = Texts.BombShopReveal(redCrystalDungeons)
|
||||
self.patches.append((Snes(0x308E00), Dialog.Simple(bombShop)))
|
||||
self.stringTable.SetBombShopRevealText(bombShop)
|
||||
|
||||
blind = Texts.Blind(self.rnd)
|
||||
self.patches.append((Snes(0x308800), Dialog.Simple(blind)))
|
||||
self.stringTable.SetBlindText(blind)
|
||||
|
||||
tavernMan = Texts.TavernMan(self.rnd)
|
||||
self.patches.append((Snes(0x308C00), Dialog.Simple(tavernMan)))
|
||||
self.stringTable.SetTavernManText(tavernMan)
|
||||
|
||||
ganon = Texts.GanonFirstPhase(self.rnd)
|
||||
self.patches.append((Snes(0x308600), Dialog.Simple(ganon)))
|
||||
self.stringTable.SetGanonFirstPhaseText(ganon)
|
||||
|
||||
#// Todo: Verify these two are correct if ganon invincible patch is ever added
|
||||
#// ganon_fall_in_alt in v30
|
||||
ganonFirstPhaseInvincible = "You think you\nare ready to\nface me?\n\nI will not die\n\nunless you\ncomplete your\ngoals. Dingus!"
|
||||
self.patches.append((Snes(0x309100), Dialog.Simple(ganonFirstPhaseInvincible)))
|
||||
|
||||
#// ganon_phase_3_alt in v30
|
||||
ganonThirdPhaseInvincible = "Got wax in\nyour ears?\nI cannot die!"
|
||||
self.patches.append((Snes(0x309200), Dialog.Simple(ganonThirdPhaseInvincible)))
|
||||
#// ---
|
||||
|
||||
silversLocation = [loc for world in self.allWorlds for loc in world.Locations if loc.ItemIs(ItemType.SilverArrows, self.myWorld)]
|
||||
if len(silversLocation) == 0:
|
||||
silvers = Texts.GanonThirdPhaseMulti(None, self.myWorld, self.silversWorldID, self.playerIDToNames[self.silversWorldID])
|
||||
else:
|
||||
silvers = Texts.GanonThirdPhaseMulti(silversLocation[0].Region, self.myWorld) if config.GameMode == GameMode.Multiworld else \
|
||||
Texts.GanonThirdPhaseSingle(silversLocation[0].Region)
|
||||
self.patches.append((Snes(0x308700), Dialog.Simple(silvers)))
|
||||
self.stringTable.SetGanonThirdPhaseText(silvers)
|
||||
|
||||
triforceRoom = Texts.TriforceRoom(self.rnd)
|
||||
self.patches.append((Snes(0x308400), Dialog.Simple(triforceRoom)))
|
||||
self.stringTable.SetTriforceRoomText(triforceRoom)
|
||||
|
||||
def WriteStringTable(self):
|
||||
#// Todo: v12, base table in asm, use move instructions in seed patch
|
||||
self.patches.append((Snes(0x1C8000), self.stringTable.GetPaddedBytes()))
|
||||
|
||||
def WritePlayerNames(self):
|
||||
self.patches += [(0x385000 + (0 * 16), self.PlayerNameBytes("Archipelago"))]
|
||||
self.patches += [(0x385000 + (id * 16), self.PlayerNameBytes(name)) for name, id in self.playerNames.items()]
|
||||
|
||||
def PlayerNameBytes(self, name: str):
|
||||
name = (name[:16] if len(name) > 16 else name).center(16)
|
||||
return bytearray(name, 'utf8')
|
||||
|
||||
def WriteSeedData(self):
|
||||
configField = \
|
||||
((1 if self.myWorld.Config.Race else 0) << 15) | \
|
||||
((1 if self.myWorld.Config.Keysanity else 0) << 13) | \
|
||||
((1 if self.myWorld.Config.GameMode == Config.GameMode.Multiworld else 0) << 12) | \
|
||||
(self.myWorld.Config.Z3Logic.value << 10) | \
|
||||
(self.myWorld.Config.SMLogic.value << 8) | \
|
||||
(Patch.Major << 4) | \
|
||||
(Patch.Minor << 0)
|
||||
|
||||
self.patches.append((Snes(0x80FF50), getWordArray(self.myWorld.Id)))
|
||||
self.patches.append((Snes(0x80FF52), getWordArray(configField)))
|
||||
self.patches.append((Snes(0x80FF54), getDoubleWordArray(self.seed)))
|
||||
#/* Reserve the rest of the space for future use */
|
||||
self.patches.append((Snes(0x80FF58), [0x00] * 8))
|
||||
self.patches.append((Snes(0x80FF60), bytearray(self.seedGuid, 'utf8')))
|
||||
self.patches.append((Snes(0x80FF80), bytearray(self.myWorld.Guid, 'utf8')))
|
||||
|
||||
def WriteCommonFlags(self):
|
||||
#/* Common Combo Configuration flags at [asm]/config.asm */
|
||||
if (self.myWorld.Config.GameMode == GameMode.Multiworld):
|
||||
self.patches.append((Snes(0xF47000), getWordArray(0x0001)))
|
||||
if (self.myWorld.Config.Keysanity):
|
||||
self.patches.append((Snes(0xF47006), getWordArray(0x0001)))
|
||||
|
||||
def WriteGameTitle(self):
|
||||
z3Glitch = "N" if self.myWorld.Config.Z3Logic == Config.Z3Logic.Nmg else \
|
||||
"O" if self.myWorld.Config.Z3Logic == Config.Z3Logic.Owg else \
|
||||
"C"
|
||||
smGlitch = "N" if self.myWorld.Config.SMLogic == Config.SMLogic.Normal else \
|
||||
"H" if self.myWorld.Config.SMLogic == Config.SMLogic.Hard else \
|
||||
"X"
|
||||
|
||||
self.title = f"ZSM{Patch.Major}{Patch.Minor}{z3Glitch}{smGlitch}{self.myWorld.Id}{self.seed:08x}".ljust(21)[:21]
|
||||
self.patches.append((Snes(0x00FFC0), bytearray(self.title, 'utf8')))
|
||||
self.patches.append((Snes(0x80FFC0), bytearray(self.title, 'utf8')))
|
||||
|
||||
def WriteZ3KeysanityFlags(self):
|
||||
if (self.myWorld.Config.Keysanity):
|
||||
self.patches.append((Snes(0x40003B), [ 1 ])) #// MapMode #$00 = Always On (default) - #$01 = Require Map Item
|
||||
self.patches.append((Snes(0x400045), [ 0x0f ])) #// display ----dcba a: Small Keys, b: Big Key, c: Map, d: Compass
|
||||
|
||||
def WriteSMKeyCardDoors(self):
|
||||
if (not self.myWorld.Config.Keysanity):
|
||||
return
|
||||
|
||||
plaquePLm = 0xd410
|
||||
|
||||
doorList = [
|
||||
#// RoomId Door Facing yyxx Keycard Event Type Plaque type yyxx, Address (if 0 a dynamic PLM is created)
|
||||
#// Crateria
|
||||
[ 0x91F8, KeycardDoors.Right, 0x2601, KeycardEvents.CrateriaLevel1, KeycardPlaque.Level1, 0x2400, 0x0000 ], #// Crateria - Landing Site - Door to gauntlet
|
||||
[ 0x91F8, KeycardDoors.Left, 0x168E, KeycardEvents.CrateriaLevel1, KeycardPlaque.Level1, 0x148F, 0x801E ], #// Crateria - Landing Site - Door to landing site PB
|
||||
[ 0x948C, KeycardDoors.Left, 0x062E, KeycardEvents.CrateriaLevel2, KeycardPlaque.Level2, 0x042F, 0x8222 ], #// Crateria - Before Moat - Door to moat (overwrite PB door)
|
||||
[ 0x99BD, KeycardDoors.Left, 0x660E, KeycardEvents.CrateriaBoss, KeycardPlaque.Boss, 0x640F, 0x8470 ], #// Crateria - Before G4 - Door to G4
|
||||
[ 0x9879, KeycardDoors.Left, 0x062E, KeycardEvents.CrateriaBoss, KeycardPlaque.Boss, 0x042F, 0x8420 ], #// Crateria - Before BT - Door to Bomb Torizo
|
||||
|
||||
#// Brinstar
|
||||
[ 0x9F11, KeycardDoors.Left, 0x060E, KeycardEvents.BrinstarLevel1, KeycardPlaque.Level1, 0x040F, 0x8784 ], #// Brinstar - Blue Brinstar - Door to ceiling e-tank room
|
||||
|
||||
[ 0x9AD9, KeycardDoors.Right, 0xA601, KeycardEvents.BrinstarLevel2, KeycardPlaque.Level2, 0xA400, 0x0000 ], #// Brinstar - Green Brinstar - Door to etecoon area
|
||||
[ 0x9D9C, KeycardDoors.Down, 0x0336, KeycardEvents.BrinstarBoss, KeycardPlaque.Boss, 0x0234, 0x863A ], #// Brinstar - Pink Brinstar - Door to spore spawn
|
||||
[ 0xA130, KeycardDoors.Left, 0x161E, KeycardEvents.BrinstarLevel2, KeycardPlaque.Level2, 0x141F, 0x881C ], #// Brinstar - Pink Brinstar - Door to wave gate e-tank
|
||||
[ 0xA0A4, KeycardDoors.Left, 0x062E, KeycardEvents.BrinstarLevel2, KeycardPlaque.Level2, 0x042F, 0x0000 ], #// Brinstar - Pink Brinstar - Door to spore spawn super
|
||||
|
||||
[ 0xA56B, KeycardDoors.Left, 0x161E, KeycardEvents.BrinstarBoss, KeycardPlaque.Boss, 0x141F, 0x8A1A ], #// Brinstar - Before Kraid - Door to Kraid
|
||||
|
||||
#// Upper Norfair
|
||||
[ 0xA7DE, KeycardDoors.Right, 0x3601, KeycardEvents.NorfairLevel1, KeycardPlaque.Level1, 0x3400, 0x8B00 ], #// Norfair - Business Centre - Door towards Ice
|
||||
[ 0xA923, KeycardDoors.Right, 0x0601, KeycardEvents.NorfairLevel1, KeycardPlaque.Level1, 0x0400, 0x0000 ], #// Norfair - Pre-Crocomire - Door towards Ice
|
||||
|
||||
[ 0xA788, KeycardDoors.Left, 0x162E, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x142F, 0x8AEA ], #// Norfair - Lava Missile Room - Door towards Bubble Mountain
|
||||
[ 0xAF72, KeycardDoors.Left, 0x061E, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x041F, 0x0000 ], #// Norfair - After frog speedway - Door to Bubble Mountain
|
||||
[ 0xAEDF, KeycardDoors.Down, 0x0206, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x0204, 0x0000 ], #// Norfair - Below bubble mountain - Door to Bubble Mountain
|
||||
[ 0xAD5E, KeycardDoors.Right, 0x0601, KeycardEvents.NorfairLevel2, KeycardPlaque.Level2, 0x0400, 0x0000 ], #// Norfair - LN Escape - Door to Bubble Mountain
|
||||
|
||||
[ 0xA923, KeycardDoors.Up, 0x2DC6, KeycardEvents.NorfairBoss, KeycardPlaque.Boss, 0x2EC4, 0x8B96 ], #// Norfair - Pre-Crocomire - Door to Crocomire
|
||||
|
||||
#// Lower Norfair
|
||||
[ 0xB4AD, KeycardDoors.Left, 0x160E, KeycardEvents.LowerNorfairLevel1, KeycardPlaque.Level1, 0x140F, 0x0000 ], #// Lower Norfair - WRITG - Door to Amphitheatre
|
||||
[ 0xAD5E, KeycardDoors.Left, 0x065E, KeycardEvents.LowerNorfairLevel1, KeycardPlaque.Level1, 0x045F, 0x0000 ], #// Lower Norfair - Exit - Door to "Reverse LN Entry"
|
||||
[ 0xB37A, KeycardDoors.Right, 0x0601, KeycardEvents.LowerNorfairBoss, KeycardPlaque.Boss, 0x0400, 0x8EA6 ], #// Lower Norfair - Pre-Ridley - Door to Ridley
|
||||
|
||||
#// Maridia
|
||||
[ 0xD0B9, KeycardDoors.Left, 0x065E, KeycardEvents.MaridiaLevel1, KeycardPlaque.Level1, 0x045F, 0x0000 ], #// Maridia - Mt. Everest - Door to Pink Maridia
|
||||
[ 0xD5A7, KeycardDoors.Right, 0x1601, KeycardEvents.MaridiaLevel1, KeycardPlaque.Level1, 0x1400, 0x0000 ], #// Maridia - Aqueduct - Door towards Beach
|
||||
|
||||
[ 0xD617, KeycardDoors.Left, 0x063E, KeycardEvents.MaridiaLevel2, KeycardPlaque.Level2, 0x043F, 0x0000 ], #// Maridia - Pre-Botwoon - Door to Botwoon
|
||||
[ 0xD913, KeycardDoors.Right, 0x2601, KeycardEvents.MaridiaLevel2, KeycardPlaque.Level2, 0x2400, 0x0000 ], #// Maridia - Pre-Colloseum - Door to post-botwoon
|
||||
|
||||
[ 0xD78F, KeycardDoors.Right, 0x2601, KeycardEvents.MaridiaBoss, KeycardPlaque.Boss, 0x2400, 0xC73B ], #// Maridia - Precious Room - Door to Draygon
|
||||
|
||||
[ 0xDA2B, KeycardDoors.BossLeft, 0x164E, 0x00f0, KeycardPlaque.Null, 0x144F, 0x0000 ], #// Maridia - Change Cac Alley Door to Boss Door (prevents key breaking)
|
||||
|
||||
#// Wrecked Ship
|
||||
[ 0x93FE, KeycardDoors.Left, 0x167E, KeycardEvents.WreckedShipLevel1, KeycardPlaque.Level1, 0x147F, 0x0000 ], #// Wrecked Ship - Outside Wrecked Ship West - Door to Reserve Tank Check
|
||||
[ 0x968F, KeycardDoors.Left, 0x060E, KeycardEvents.WreckedShipLevel1, KeycardPlaque.Level1, 0x040F, 0x0000 ], #// Wrecked Ship - Outside Wrecked Ship West - Door to Bowling Alley
|
||||
[ 0xCE40, KeycardDoors.Left, 0x060E, KeycardEvents.WreckedShipLevel1, KeycardPlaque.Level1, 0x040F, 0x0000 ], #// Wrecked Ship - Gravity Suit - Door to Bowling Alley
|
||||
|
||||
[ 0xCC6F, KeycardDoors.Left, 0x064E, KeycardEvents.WreckedShipBoss, KeycardPlaque.Boss, 0x044F, 0xC29D ], #// Wrecked Ship - Pre-Phantoon - Door to Phantoon
|
||||
]
|
||||
|
||||
doorId = 0x0000
|
||||
plmTablePos = 0xf800
|
||||
for door in doorList:
|
||||
doorArgs = doorId | door[3] if door[4] != KeycardPlaque.Null else door[3]
|
||||
if (door[6] == 0):
|
||||
#// Write dynamic door
|
||||
doorData = []
|
||||
for x in door[0:3]:
|
||||
doorData += getWordArray(x)
|
||||
doorData += getWordArray(doorArgs)
|
||||
self.patches.append((Snes(0x8f0000 + plmTablePos), doorData))
|
||||
plmTablePos += 0x08
|
||||
else:
|
||||
#// Overwrite existing door
|
||||
doorData = []
|
||||
for x in door[1:3]:
|
||||
doorData += getWordArray(x)
|
||||
doorData += getWordArray(doorArgs)
|
||||
self.patches.append((Snes(0x8f0000 + door[6]), doorData))
|
||||
if((door[3] == KeycardEvents.BrinstarBoss and door[0] != 0x9D9C) or door[3] == KeycardEvents.LowerNorfairBoss or door[3] == KeycardEvents.MaridiaBoss or door[3] == KeycardEvents.WreckedShipBoss):
|
||||
#// Overwrite the extra parts of the Gadora with a PLM that just deletes itself
|
||||
self.patches.append((Snes(0x8f0000 + door[6] + 0x06), [ 0x2F, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x2F, 0xB6, 0x00, 0x00, 0x00, 0x00 ]))
|
||||
|
||||
#// Plaque data
|
||||
if (door[4] != KeycardPlaque.Null):
|
||||
plaqueData = getWordArray(door[0]) + getWordArray(plaquePLm) + getWordArray(door[5]) + getWordArray(door[4])
|
||||
self.patches.append((Snes(0x8f0000 + plmTablePos), plaqueData))
|
||||
plmTablePos += 0x08
|
||||
doorId += 1
|
||||
|
||||
self.patches.append((Snes(0x8f0000 + plmTablePos), [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]))
|
||||
|
||||
def WriteDiggingGameRng(self):
|
||||
digs = (self.rnd.randrange(30) + 1)
|
||||
self.patches.append((Snes(0x308020), [ digs ]))
|
||||
self.patches.append((Snes(0x1DFD95), [ digs ]))
|
||||
|
||||
#// Removes Sword/Shield from Uncle by moving the tiles for
|
||||
#// sword/shield to his head and replaces them with his head.
|
||||
def WriteRemoveEquipmentFromUncle(self, item: Item):
|
||||
if (item.Type != ItemType.ProgressiveSword):
|
||||
self.patches += [
|
||||
(Snes(0xDD263), [ 0x00, 0x00, 0xF6, 0xFF, 0x00, 0x0E ]),
|
||||
(Snes(0xDD26B), [ 0x00, 0x00, 0xF6, 0xFF, 0x00, 0x0E ]),
|
||||
(Snes(0xDD293), [ 0x00, 0x00, 0xF6, 0xFF, 0x00, 0x0E ]),
|
||||
(Snes(0xDD29B), [ 0x00, 0x00, 0xF7, 0xFF, 0x00, 0x0E ]),
|
||||
(Snes(0xDD2B3), [ 0x00, 0x00, 0xF6, 0xFF, 0x02, 0x0E ]),
|
||||
(Snes(0xDD2BB), [ 0x00, 0x00, 0xF6, 0xFF, 0x02, 0x0E ]),
|
||||
(Snes(0xDD2E3), [ 0x00, 0x00, 0xF7, 0xFF, 0x02, 0x0E ]),
|
||||
(Snes(0xDD2EB), [ 0x00, 0x00, 0xF7, 0xFF, 0x02, 0x0E ]),
|
||||
(Snes(0xDD31B), [ 0x00, 0x00, 0xE4, 0xFF, 0x08, 0x0E ]),
|
||||
(Snes(0xDD323), [ 0x00, 0x00, 0xE4, 0xFF, 0x08, 0x0E ]),
|
||||
]
|
||||
if (item.Type != ItemType.ProgressiveShield):
|
||||
self.patches += [
|
||||
(Snes(0xDD253), [ 0x00, 0x00, 0xF6, 0xFF, 0x00, 0x0E ]),
|
||||
(Snes(0xDD25B), [ 0x00, 0x00, 0xF6, 0xFF, 0x00, 0x0E ]),
|
||||
(Snes(0xDD283), [ 0x00, 0x00, 0xF6, 0xFF, 0x00, 0x0E ]),
|
||||
(Snes(0xDD28B), [ 0x00, 0x00, 0xF7, 0xFF, 0x00, 0x0E ]),
|
||||
(Snes(0xDD2CB), [ 0x00, 0x00, 0xF6, 0xFF, 0x02, 0x0E ]),
|
||||
(Snes(0xDD2FB), [ 0x00, 0x00, 0xF7, 0xFF, 0x02, 0x0E ]),
|
||||
(Snes(0xDD313), [ 0x00, 0x00, 0xE4, 0xFF, 0x08, 0x0E ]),
|
||||
]
|
||||
|
||||
def WriteGanonInvicible(self, invincible: GanonInvincible):
|
||||
#/* Defaults to $00 (never) at [asm]/z3/randomizer/tables.asm */
|
||||
invincibleMap = {
|
||||
GanonInvincible.Never : 0x00,
|
||||
GanonInvincible.Always : 0x01,
|
||||
GanonInvincible.BeforeAllDungeons : 0x02,
|
||||
GanonInvincible.BeforeCrystals : 0x03,
|
||||
}
|
||||
value = invincibleMap.get(invincible, None)
|
||||
if (value is None):
|
||||
raise exception(f"Unknown Ganon invincible value {invincible}")
|
||||
else:
|
||||
self.patches.append((Snes(0x30803E), [value]))
|
||||
|
||||
|
||||
def WriteRngBlock(self):
|
||||
#/* Repoint RNG Block */
|
||||
self.patches.append((0x420000, [self.rnd.randrange(0, 0x100) for x in range(0, 1024)]))
|
||||
|
||||
def WriteSaveAndQuitFromBossRoom(self):
|
||||
#/* Defaults to $00 at [asm]/z3/randomizer/tables.asm */
|
||||
self.patches.append((Snes(0x308042), [ 0x01 ]))
|
||||
|
||||
def WriteWorldOnAgahnimDeath(self):
|
||||
pass
|
||||
#/* Defaults to $01 at [asm]/z3/randomizer/tables.asm */
|
||||
#// Todo: Z3r major glitches disables this, reconsider extending or dropping with glitched logic later.
|
||||
#//patches.Add((Snes(0x3080A3), new byte[] { 0x01 }));
|
||||
|
||||
def Snes(addr: int):
|
||||
#/* Redirect hi bank $30 access into ExHiRom lo bank $40 */
|
||||
if (addr & 0xFF8000) == 0x308000:
|
||||
addr = 0x400000 | (addr & 0x7FFF)
|
||||
else: #/* General case, add ExHi offset for banks < $80, and collapse mirroring */
|
||||
addr = (0x400000 if addr < 0x800000 else 0)| (addr & 0x3FFFFF)
|
||||
if (addr > 0x600000):
|
||||
raise Exception(f"Unmapped pc address target ${addr:x}")
|
||||
return addr
|
||||
|
||||
def getWord(w):
|
||||
return (w & 0x00FF, (w & 0xFF00) >> 8)
|
||||
|
||||
def getWordArray(w):
|
||||
return [w & 0x00FF, (w & 0xFF00) >> 8]
|
||||
|
||||
def getDoubleWordArray(w):
|
||||
return [w & 0x000000FF, (w & 0x0000FF00) >> 8, (w & 0x00FF0000) >> 16, (w & 0xFF000000) >> 24]
|
||||
|
||||
"""
|
||||
byte[] UintBytes(int value) => BitConverter.GetBytes((uint)value);
|
||||
|
||||
byte[] UshortBytes(int value) => BitConverter.GetBytes((ushort)value);
|
||||
|
||||
byte[] AsAscii(string text) => Encoding.ASCII.GetBytes(text);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
"""
|
||||
def SplitOff(source: List[Any], count: int):
|
||||
head = source[:count]
|
||||
tail = source[count:]
|
||||
return (head, tail)
|
|
@ -0,0 +1,63 @@
|
|||
from enum import Enum
|
||||
from typing import Dict, List
|
||||
from worlds.smz3.TotalSMZ3.Config import *
|
||||
from worlds.smz3.TotalSMZ3.Item import Item, ItemType
|
||||
|
||||
class RewardType(Enum):
|
||||
Null = 0
|
||||
Agahnim = 1
|
||||
PendantGreen = 2
|
||||
PendantNonGreen = 3
|
||||
CrystalBlue = 4
|
||||
CrystalRed = 5
|
||||
GoldenFourBoss = 6
|
||||
|
||||
class IReward:
|
||||
Reward: RewardType
|
||||
def CanComplete(self, items):
|
||||
pass
|
||||
|
||||
class IMedallionAccess:
|
||||
Medallion: object
|
||||
|
||||
class Region:
|
||||
import worlds.smz3.TotalSMZ3.Location as Location
|
||||
Name: str
|
||||
Area: str
|
||||
|
||||
Locations: List[Location.Location]
|
||||
Weight: int = 0
|
||||
|
||||
Config: Config
|
||||
|
||||
locationLookup: Dict[str, Location.Location]
|
||||
|
||||
def GetLocation(self, name: str):
|
||||
return self.locationLookup[name]
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
self.Config = config
|
||||
self.world = world
|
||||
self.locationLookup = {}
|
||||
self.RegionItems = []
|
||||
|
||||
def GenerateLocationLookup(self):
|
||||
self.locationLookup = {loc.Name:loc for loc in self.Locations}
|
||||
|
||||
def IsRegionItem(self, item):
|
||||
return item.Type in self.RegionItems
|
||||
|
||||
def CanFill(self, item):
|
||||
return self.Config.Keysanity or not item.IsDungeonItem() or self.IsRegionItem(item)
|
||||
|
||||
def CanEnter(self, items):
|
||||
return True
|
||||
|
||||
class SMRegion(Region):
|
||||
Logic: SMLogic = Config.SMLogic
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
|
||||
class Z3Region(Region):
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
|
@ -0,0 +1,26 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
|
||||
class Blue(SMRegion):
|
||||
Name = "Brinstar Blue"
|
||||
Area = "Brinstar"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 26, 0x8F86EC, LocationType.Visible, "Morphing Ball"),
|
||||
Location(self, 27, 0x8F874C, LocationType.Visible, "Power Bomb (blue Brinstar)",
|
||||
lambda items: items.CanUsePowerBombs()),
|
||||
Location(self, 28, 0x8F8798, LocationType.Visible, "Missile (blue Brinstar middle)",
|
||||
lambda items: items.CardBrinstarL1 and items.Morph),
|
||||
Location(self, 29, 0x8F879E, LocationType.Hidden, "Energy Tank, Brinstar Ceiling",
|
||||
lambda items: items.CardBrinstarL1 and (items.CanFly() or items.HiJump or items.SpeedBooster or items.Ice) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CardBrinstarL1),
|
||||
Location(self, 34, 0x8F8802, LocationType.Chozo, "Missile (blue Brinstar bottom)",
|
||||
lambda items: items.Morph),
|
||||
Location(self, 36, 0x8F8836, LocationType.Visible, "Missile (blue Brinstar top)",
|
||||
lambda items: items.CardBrinstarL1 and items.CanUsePowerBombs()),
|
||||
Location(self, 37, 0x8F883C, LocationType.Hidden, "Missile (blue Brinstar behind missile)",
|
||||
lambda items: items.CardBrinstarL1 and items.CanUsePowerBombs())
|
||||
]
|
|
@ -0,0 +1,37 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class Green(SMRegion):
|
||||
Name = "Brinstar Green"
|
||||
Area = "Brinstar"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Weight = -6
|
||||
self.Locations = [
|
||||
Location(self, 13, 0x8F84AC, LocationType.Chozo, "Power Bomb (green Brinstar bottom)",
|
||||
lambda items: items.CardBrinstarL2 and items.CanUsePowerBombs()),
|
||||
Location(self, 15, 0x8F8518, LocationType.Visible, "Missile (green Brinstar below super missile)",
|
||||
lambda items: items.CanPassBombPassages() and items.CanOpenRedDoors()),
|
||||
Location(self, 16, 0x8F851E, LocationType.Visible, "Super Missile (green Brinstar top)",
|
||||
lambda items: items.CanOpenRedDoors() and items.SpeedBooster if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CanOpenRedDoors() and (items.Morph or items.SpeedBooster)),
|
||||
Location(self, 17, 0x8F852C, LocationType.Chozo, "Reserve Tank, Brinstar",
|
||||
lambda items: items.CanOpenRedDoors() and items.SpeedBooster if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CanOpenRedDoors() and (items.Morph or items.SpeedBooster)),
|
||||
Location(self, 18, 0x8F8532, LocationType.Hidden, "Missile (green Brinstar behind missile)",
|
||||
lambda items: items.SpeedBooster and items.CanPassBombPassages() and items.CanOpenRedDoors() if self.Logic == SMLogic.Normal else \
|
||||
lambda items: (items.CanPassBombPassages() or items.Morph and items.ScrewAttack) and items.CanOpenRedDoors()),
|
||||
Location(self, 19, 0x8F8538, LocationType.Visible, "Missile (green Brinstar behind reserve tank)",
|
||||
lambda items: items.SpeedBooster and items.CanOpenRedDoors() and items.Morph if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CanOpenRedDoors() and items.Morph),
|
||||
Location(self, 30, 0x8F87C2, LocationType.Visible, "Energy Tank, Etecoons",
|
||||
lambda items: items.CardBrinstarL2 and items.CanUsePowerBombs()),
|
||||
Location(self, 31, 0x8F87D0, LocationType.Visible, "Super Missile (green Brinstar bottom)",
|
||||
lambda items: items.CardBrinstarL2 and items.CanUsePowerBombs() and items.Super)
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.CanDestroyBombWalls() or items.SpeedBooster
|
|
@ -0,0 +1,25 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion, IReward, RewardType
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class Kraid(SMRegion, IReward):
|
||||
Name = "Brinstar Kraid"
|
||||
Area = "Brinstar"
|
||||
|
||||
Reward = RewardType.GoldenFourBoss
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 43, 0x8F899C, LocationType.Hidden, "Energy Tank, Kraid", lambda items: items.CardBrinstarBoss),
|
||||
Location(self, 48, 0x8F8ACA, LocationType.Chozo, "Varia Suit", lambda items: items.CardBrinstarBoss),
|
||||
Location(self, 44, 0x8F89EC, LocationType.Hidden, "Missile (Kraid)", lambda items: items.CanUsePowerBombs())
|
||||
]
|
||||
|
||||
def CanEnter(self, items:Progression):
|
||||
return (items.CanDestroyBombWalls() or items.SpeedBooster or items.CanAccessNorfairUpperPortal()) and \
|
||||
items.Super and items.CanPassBombPassages()
|
||||
|
||||
def CanComplete(self, items:Progression):
|
||||
return self.GetLocation("Varia Suit").Available(items)
|
|
@ -0,0 +1,44 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class Pink(SMRegion):
|
||||
Name = "Brinstar Pink"
|
||||
Area = "Brinstar"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Weight = -4
|
||||
self.Locations = [
|
||||
Location(self, 14, 0x8F84E4, LocationType.Chozo, "Super Missile (pink Brinstar)",
|
||||
lambda items: items.CardBrinstarBoss and items.CanPassBombPassages() and items.Super if self.Logic == SMLogic.Normal else \
|
||||
lambda items: (items.CardBrinstarBoss or items.CardBrinstarL2) and items.CanPassBombPassages() and items.Super),
|
||||
Location(self, 21, 0x8F8608, LocationType.Visible, "Missile (pink Brinstar top)"),
|
||||
Location(self, 22, 0x8F860E, LocationType.Visible, "Missile (pink Brinstar bottom)"),
|
||||
Location(self, 23, 0x8F8614, LocationType.Chozo, "Charge Beam",
|
||||
lambda items: items.CanPassBombPassages()),
|
||||
Location(self, 24, 0x8F865C, LocationType.Visible, "Power Bomb (pink Brinstar)",
|
||||
lambda items: items.CanUsePowerBombs() and items.Super and items.HasEnergyReserves(1) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CanUsePowerBombs() and items.Super),
|
||||
Location(self, 25, 0x8F8676, LocationType.Visible, "Missile (green Brinstar pipe)",
|
||||
lambda items: items.Morph and (items.PowerBomb or items.Super or items.CanAccessNorfairUpperPortal())),
|
||||
Location(self, 33, 0x8F87FA, LocationType.Visible, "Energy Tank, Waterway",
|
||||
lambda items: items.CanUsePowerBombs() and items.CanOpenRedDoors() and items.SpeedBooster and (items.HasEnergyReserves(1) or items.Gravity)),
|
||||
Location(self, 35, 0x8F8824, LocationType.Visible, "Energy Tank, Brinstar Gate",
|
||||
lambda items: items.CardBrinstarL2 and items.CanUsePowerBombs() and items.Wave and items.HasEnergyReserves(1) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CardBrinstarL2 and items.CanUsePowerBombs() and (items.Wave or items.Super))
|
||||
]
|
||||
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return items.CanOpenRedDoors() and (items.CanDestroyBombWalls() or items.SpeedBooster) or \
|
||||
items.CanUsePowerBombs() or \
|
||||
items.CanAccessNorfairUpperPortal() and items.Morph and items.Wave and \
|
||||
(items.Ice or items.HiJump or items.SpaceJump)
|
||||
else:
|
||||
return items.CanOpenRedDoors() and (items.CanDestroyBombWalls() or items.SpeedBooster) or \
|
||||
items.CanUsePowerBombs() or \
|
||||
items.CanAccessNorfairUpperPortal() and items.Morph and (items.CanOpenRedDoors() or items.Wave) and \
|
||||
(items.Ice or items.HiJump or items.CanSpringBallJump() or items.CanFly())
|
|
@ -0,0 +1,36 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class Red(SMRegion):
|
||||
Name = "Brinstar Red"
|
||||
Area = "Brinstar"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 38, 0x8F8876, LocationType.Chozo, "X-Ray Scope",
|
||||
lambda items: items.CanUsePowerBombs() and items.CanOpenRedDoors() and (items.Grapple or items.SpaceJump) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CanUsePowerBombs() and items.CanOpenRedDoors() and (
|
||||
items.Grapple or items.SpaceJump or
|
||||
(items.CanIbj() or items.HiJump and items.SpeedBooster or items.CanSpringBallJump()) and
|
||||
(items.Varia and items.HasEnergyReserves(3) or items.HasEnergyReserves(5)))),
|
||||
Location(self, 39, 0x8F88CA, LocationType.Visible, "Power Bomb (red Brinstar sidehopper room)",
|
||||
lambda items: items.CanUsePowerBombs() and items.Super),
|
||||
Location(self, 40, 0x8F890E, LocationType.Chozo, "Power Bomb (red Brinstar spike room)",
|
||||
lambda items: (items.CanUsePowerBombs() or items.Ice) and items.Super if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.Super),
|
||||
Location(self, 41, 0x8F8914, LocationType.Visible, "Missile (red Brinstar spike room)",
|
||||
lambda items: items.CanUsePowerBombs() and items.Super),
|
||||
Location(self, 42, 0x8F896E, LocationType.Chozo, "Spazer",
|
||||
lambda items: items.CanPassBombPassages() and items.Super)
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return (items.CanDestroyBombWalls() or items.SpeedBooster) and items.Super and items.Morph or \
|
||||
items.CanAccessNorfairUpperPortal() and (items.Ice or items.HiJump or items.SpaceJump)
|
||||
else:
|
||||
return (items.CanDestroyBombWalls() or items.SpeedBooster) and items.Super and items.Morph or \
|
||||
items.CanAccessNorfairUpperPortal() and (items.Ice or items.CanSpringBallJump() or items.HiJump or items.CanFly())
|
|
@ -0,0 +1,23 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
|
||||
class Central(SMRegion):
|
||||
Name = "Crateria Central"
|
||||
Area = "Crateria"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 0, 0x8F81CC, LocationType.Visible, "Power Bomb (Crateria surface)",
|
||||
lambda items: (items.CardCrateriaL1 if config.Keysanity else items.CanUsePowerBombs()) and (items.SpeedBooster or items.CanFly())),
|
||||
Location(self, 12, 0x8F8486, LocationType.Visible, "Missile (Crateria middle)",
|
||||
lambda items: items.CanPassBombPassages()),
|
||||
Location(self, 6, 0x8F83EE, LocationType.Visible, "Missile (Crateria bottom)",
|
||||
lambda items: items.CanDestroyBombWalls()),
|
||||
Location(self, 11, 0x8F8478, LocationType.Visible, "Super Missile (Crateria)",
|
||||
lambda items: items.CanUsePowerBombs() and items.HasEnergyReserves(2) and items.SpeedBooster),
|
||||
Location(self, 7, 0x8F8404, LocationType.Chozo, "Bombs",
|
||||
lambda items: (items.CardCrateriaBoss if config.Keysanity else items.CanOpenRedDoors()) and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
||||
lambda items: (items.CardCrateriaBoss if config.Keysanity else items.CanOpenRedDoors()) and items.Morph)
|
||||
]
|
|
@ -0,0 +1,59 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class East(SMRegion):
|
||||
Name = "Crateria East"
|
||||
Area = "Crateria"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 1, 0x8F81E8, LocationType.Visible, "Missile (outside Wrecked Ship bottom)",
|
||||
lambda items: items.Morph and (
|
||||
items.SpeedBooster or items.Grapple or items.SpaceJump or
|
||||
items.Gravity and (items.CanIbj() or items.HiJump) or
|
||||
self.world.CanEnter("Wrecked Ship", items)) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.Morph),
|
||||
Location(self, 2, 0x8F81EE, LocationType.Hidden, "Missile (outside Wrecked Ship top)",
|
||||
lambda items: self.world.CanEnter("Wrecked Ship", items) and (not self.Config.Keysanity or items.CardWreckedShipBoss) and items.CanPassBombPassages()),
|
||||
Location(self, 3, 0x8F81F4, LocationType.Visible, "Missile (outside Wrecked Ship middle)",
|
||||
lambda items: self.world.CanEnter("Wrecked Ship", items) and (not self.Config.Keysanity or items.CardWreckedShipBoss) and items.CanPassBombPassages()),
|
||||
Location(self, 4, 0x8F8248, LocationType.Visible, "Missile (Crateria moat)",
|
||||
lambda items: True)
|
||||
]
|
||||
|
||||
def CanEnter(self, items:Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
# /* Ship -> Moat */
|
||||
return (items.CardCrateriaL2 if self.Config.Keysanity else items.CanUsePowerBombs()) and items.Super or (
|
||||
# /* UN Portal -> Red Tower -> Moat
|
||||
items.CardCrateriaL2 if self.Config.Keysanity else items.CanUsePowerBombs()) and items.CanAccessNorfairUpperPortal() and (
|
||||
items.Ice or items.HiJump or items.SpaceJump) or (
|
||||
# /*Through Maridia From Portal*/
|
||||
items.CanAccessMaridiaPortal(self.world)) and items.Gravity and items.Super and (
|
||||
# /* Oasis -> Forgotten Highway */
|
||||
items.CardMaridiaL2 and items.CanDestroyBombWalls() or (
|
||||
# /* Draygon -> Cactus Alley -> Forgotten Highway */
|
||||
self.world.GetLocation("Space Jump").Available(items))) or (
|
||||
# /*Through Maridia from Pipe*/
|
||||
items.CanUsePowerBombs()) and items.Super and items.Gravity
|
||||
else:
|
||||
# /* Ship -> Moat */
|
||||
return (items.CardCrateriaL2 if self.Config.Keysanity else items.CanUsePowerBombs()) and items.Super or (
|
||||
# /* UN Portal -> Red Tower -> Moat */
|
||||
items.CardCrateriaL2 if self.Config.Keysanity else items.CanUsePowerBombs()) and items.CanAccessNorfairUpperPortal() and (
|
||||
items.Ice or items.HiJump or items.CanFly() or items.CanSpringBallJump()) or (
|
||||
# /*Through Maridia From Portal*/
|
||||
items.CanAccessMaridiaPortal(self.world)) and (
|
||||
# /* Oasis -> Forgotten Highway */
|
||||
items.CardMaridiaL2 and items.Super and (
|
||||
items.HiJump and items.CanPassBombPassages() or
|
||||
items.Gravity and items.CanDestroyBombWalls()
|
||||
) or
|
||||
# /* Draygon -> Cactus Alley -> Forgotten Highway */
|
||||
items.Gravity and self.world.GetLocation("Space Jump").Available(items)) or (
|
||||
# /*Through Maridia from Pipe*/
|
||||
items.CanUsePowerBombs()) and items.Super and (items.Gravity or items.HiJump and (items.Ice or items.CanSpringBallJump())
|
||||
and items.Grapple and items.CardMaridiaL1)
|
|
@ -0,0 +1,38 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class West(SMRegion):
|
||||
Name = "Crateria West"
|
||||
Area = "Crateria"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 8, 0x8F8432, LocationType.Visible, "Energy Tank, Terminator"),
|
||||
Location(self, 5, 0x8F8264, LocationType.Visible, "Energy Tank, Gauntlet",
|
||||
lambda items: self.CanEnterAndLeaveGauntlet(items) and items.HasEnergyReserves(1) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanEnterAndLeaveGauntlet(items)),
|
||||
Location(self, 9, 0x8F8464, LocationType.Visible, "Missile (Crateria gauntlet right)",
|
||||
lambda items: self.CanEnterAndLeaveGauntlet(items) and items.CanPassBombPassages() and items.HasEnergyReserves(2) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanEnterAndLeaveGauntlet(items) and items.CanPassBombPassages()),
|
||||
Location(self, 10, 0x8F846A, LocationType.Visible, "Missile (Crateria gauntlet left)",
|
||||
lambda items: self.CanEnterAndLeaveGauntlet(items) and items.CanPassBombPassages() and items.HasEnergyReserves(2) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanEnterAndLeaveGauntlet(items) and items.CanPassBombPassages())
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.CanDestroyBombWalls() or items.SpeedBooster
|
||||
|
||||
def CanEnterAndLeaveGauntlet(self, items: Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return items.CardCrateriaL1 and items.Morph and (items.CanFly() or items.SpeedBooster) and (
|
||||
items.CanIbj() or
|
||||
items.CanUsePowerBombs() and items.TwoPowerBombs or
|
||||
items.ScrewAttack)
|
||||
else:
|
||||
return items.CardCrateriaL1 and (
|
||||
items.Morph and (items.Bombs or items.TwoPowerBombs) or
|
||||
items.ScrewAttack or
|
||||
items.SpeedBooster and items.CanUsePowerBombs() and items.HasEnergyReserves(2))
|
|
@ -0,0 +1,110 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion, IReward, RewardType
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class Inner(SMRegion, IReward):
|
||||
Name = "Maridia Inner"
|
||||
Area = "Maridia"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Reward = RewardType.GoldenFourBoss
|
||||
self.Locations = [
|
||||
Location(self, 140, 0x8FC4AF, LocationType.Visible, "Super Missile (yellow Maridia)",
|
||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() and
|
||||
(items.Gravity or items.Ice or items.HiJump and items.CanSpringBallJump())),
|
||||
Location(self, 141, 0x8FC4B5, LocationType.Visible, "Missile (yellow Maridia super missile)",
|
||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() and
|
||||
(items.Gravity or items.Ice or items.HiJump and items.CanSpringBallJump())),
|
||||
Location(self, 142, 0x8FC533, LocationType.Visible, "Missile (yellow Maridia false wall)",
|
||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CardMaridiaL1 and items.CanPassBombPassages() and
|
||||
(items.Gravity or items.Ice or items.HiJump and items.CanSpringBallJump())),
|
||||
Location(self, 143, 0x8FC559, LocationType.Chozo, "Plasma Beam",
|
||||
lambda items: self.CanDefeatDraygon(items) and (items.ScrewAttack or items.Plasma) and (items.HiJump or items.CanFly()) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanDefeatDraygon(items) and
|
||||
(items.Charge and items.HasEnergyReserves(3) or items.ScrewAttack or items.Plasma or items.SpeedBooster) and
|
||||
(items.HiJump or items.CanSpringBallJump() or items.CanFly() or items.SpeedBooster)),
|
||||
Location(self, 144, 0x8FC5DD, LocationType.Visible, "Missile (left Maridia sand pit room)",
|
||||
lambda items: self.CanReachAqueduct(items) and items.Super and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanReachAqueduct(items) and items.Super and
|
||||
(items.HiJump and (items.SpaceJump or items.CanSpringBallJump()) or items.Gravity)),
|
||||
Location(self, 145, 0x8FC5E3, LocationType.Chozo, "Reserve Tank, Maridia",
|
||||
lambda items: self.CanReachAqueduct(items) and items.Super and items.CanPassBombPassages() if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanReachAqueduct(items) and items.Super and
|
||||
(items.HiJump and (items.SpaceJump or items.CanSpringBallJump()) or items.Gravity)),
|
||||
Location(self, 146, 0x8FC5EB, LocationType.Visible, "Missile (right Maridia sand pit room)",
|
||||
lambda items: self.CanReachAqueduct(items) and items.Super) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanReachAqueduct(items) and items.Super and (items.HiJump or items.Gravity),
|
||||
Location(self, 147, 0x8FC5F1, LocationType.Visible, "Power Bomb (right Maridia sand pit room)",
|
||||
lambda items: self.CanReachAqueduct(items) and items.Super) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanReachAqueduct(items) and items.Super and (items.HiJump and items.CanSpringBallJump() or items.Gravity),
|
||||
Location(self, 148, 0x8FC603, LocationType.Visible, "Missile (pink Maridia)",
|
||||
lambda items: self.CanReachAqueduct(items) and items.SpeedBooster if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanReachAqueduct(items) and items.Gravity),
|
||||
Location(self, 149, 0x8FC609, LocationType.Visible, "Super Missile (pink Maridia)",
|
||||
lambda items: self.CanReachAqueduct(items) and items.SpeedBooster if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanReachAqueduct(items) and items.Gravity),
|
||||
Location(self, 150, 0x8FC6E5, LocationType.Chozo, "Spring Ball",
|
||||
lambda items: items.Super and items.Grapple and items.CanUsePowerBombs() and (items.SpaceJump or items.HiJump) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.Super and items.Grapple and items.CanUsePowerBombs() and (
|
||||
items.Gravity and (items.CanFly() or items.HiJump) or
|
||||
items.Ice and items.HiJump and items.CanSpringBallJump() and items.SpaceJump)),
|
||||
Location(self, 151, 0x8FC74D, LocationType.Hidden, "Missile (Draygon)",
|
||||
lambda items:
|
||||
items.CardMaridiaL1 and items.CardMaridiaL2 and self.CanDefeatBotwoon(items) or
|
||||
items.CanAccessMaridiaPortal(self.world) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: (
|
||||
items.CardMaridiaL1 and items.CardMaridiaL2 and self.CanDefeatBotwoon(items) or
|
||||
items.CanAccessMaridiaPortal(self.world)
|
||||
) and items.Gravity),
|
||||
Location(self, 152, 0x8FC755, LocationType.Visible, "Energy Tank, Botwoon",
|
||||
lambda items:
|
||||
items.CardMaridiaL1 and items.CardMaridiaL2 and self.CanDefeatBotwoon(items) or
|
||||
items.CanAccessMaridiaPortal(self.world) and items.CardMaridiaL2),
|
||||
Location(self, 154, 0x8FC7A7, LocationType.Chozo, "Space Jump",
|
||||
lambda items: self.CanDefeatDraygon(items))
|
||||
]
|
||||
|
||||
def CanReachAqueduct(self, items: Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return items.CardMaridiaL1 and (items.CanFly() or items.SpeedBooster or items.Grapple) \
|
||||
or items.CardMaridiaL2 and items.CanAccessMaridiaPortal(self.world)
|
||||
else:
|
||||
return items.CardMaridiaL1 and (items.Gravity or items.HiJump and (items.Ice or items.CanSpringBallJump()) and items.Grapple) \
|
||||
or items.CardMaridiaL2 and items.CanAccessMaridiaPortal(self.world)
|
||||
|
||||
def CanDefeatDraygon(self, items: Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return (items.CardMaridiaL1 and items.CardMaridiaL2 and self.CanDefeatBotwoon(items) or
|
||||
items.CanAccessMaridiaPortal(self.world)
|
||||
) and items.CardMaridiaBoss and items.Gravity and (items.SpeedBooster and items.HiJump or items.CanFly())
|
||||
else:
|
||||
return (items.CardMaridiaL1 and items.CardMaridiaL2 and self.CanDefeatBotwoon(items) or
|
||||
items.CanAccessMaridiaPortal(self.world)
|
||||
) and items.CardMaridiaBoss and items.Gravity
|
||||
|
||||
|
||||
def CanDefeatBotwoon(self, items: Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return items.SpeedBooster or items.CanAccessMaridiaPortal(self.world)
|
||||
else:
|
||||
return items.Ice or items.SpeedBooster and items.Gravity or items.CanAccessMaridiaPortal(self.world)
|
||||
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return items.Gravity and (
|
||||
self.world.CanEnter("Norfair Upper West", items) and items.Super and items.CanUsePowerBombs() and
|
||||
(items.CanFly() or items.SpeedBooster or items.Grapple) or
|
||||
items.CanAccessMaridiaPortal(self.world))
|
||||
else:
|
||||
return items.Super and self.world.CanEnter("Norfair Upper West", items) and items.CanUsePowerBombs() and (
|
||||
items.Gravity or items.HiJump and (items.Ice or items.CanSpringBallJump()) and items.Grapple) or \
|
||||
items.CanAccessMaridiaPortal(self.world)
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.GetLocation("Space Jump").Available(items)
|
|
@ -0,0 +1,37 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class Outer(SMRegion):
|
||||
Name = "Maridia Outer"
|
||||
Area = "Maridia"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 136, 0x8FC437, LocationType.Visible, "Missile (green Maridia shinespark)",
|
||||
lambda items: items.SpeedBooster if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.Gravity and items.SpeedBooster),
|
||||
Location(self, 137, 0x8FC43D, LocationType.Visible, "Super Missile (green Maridia)"),
|
||||
Location(self, 138, 0x8FC47D, LocationType.Visible, "Energy Tank, Mama turtle",
|
||||
lambda items: items.CanOpenRedDoors() and (items.CanFly() or items.SpeedBooster or items.Grapple) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CanOpenRedDoors() and (
|
||||
items.CanFly() or items.SpeedBooster or items.Grapple or
|
||||
items.CanSpringBallJump() and (items.Gravity or items.HiJump))),
|
||||
Location(self, 139, 0x8FC483, LocationType.Hidden, "Missile (green Maridia tatori)",
|
||||
lambda items: items.CanOpenRedDoors())
|
||||
]
|
||||
|
||||
def CanEnter(self, items:Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return items.Gravity and (
|
||||
self.world.CanEnter("Norfair Upper West", items) and items.CanUsePowerBombs() or
|
||||
items.CanAccessMaridiaPortal(self.world) and items.CardMaridiaL1 and items.CardMaridiaL2 and (items.CanPassBombPassages() or items.ScrewAttack))
|
||||
else:
|
||||
return self.world.CanEnter("Norfair Upper West", items) and items.CanUsePowerBombs() and (
|
||||
items.Gravity or items.HiJump and (items.CanSpringBallJump() or items.Ice)) or (
|
||||
items.CanAccessMaridiaPortal(self.world)) and items.CardMaridiaL1 and items.CardMaridiaL2 and (
|
||||
items.CanPassBombPassages() or
|
||||
items.Gravity and items.ScrewAttack or
|
||||
items.Super and (items.Gravity or items.HiJump and (items.CanSpringBallJump() or items.Ice)))
|
|
@ -0,0 +1,60 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion, IReward, RewardType
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class East(SMRegion, IReward):
|
||||
Name = "Norfair Lower East"
|
||||
Area = "Norfair Lower"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Reward = RewardType.GoldenFourBoss
|
||||
self.Locations = [
|
||||
Location(self, 74, 0x8F8FCA, LocationType.Visible, "Missile (lower Norfair above fire flea room)",
|
||||
lambda items: self.CanExit(items)),
|
||||
Location(self, 75, 0x8F8FD2, LocationType.Visible, "Power Bomb (lower Norfair above fire flea room)",
|
||||
lambda items: self.CanExit(items)) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanExit(items) and items.CanPassBombPassages(),
|
||||
Location(self, 76, 0x8F90C0, LocationType.Visible, "Power Bomb (Power Bombs of shame)",
|
||||
lambda items: self.CanExit(items) and items.CanUsePowerBombs()),
|
||||
Location(self, 77, 0x8F9100, LocationType.Visible, "Missile (lower Norfair near Wave Beam)",
|
||||
lambda items: self.CanExit(items)) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanExit(items) and items.Morph and items.CanDestroyBombWalls(),
|
||||
Location(self, 78, 0x8F9108, LocationType.Hidden, "Energy Tank, Ridley",
|
||||
lambda items: self.CanExit(items) and items.CardLowerNorfairBoss and items.CanUsePowerBombs() and items.Super),
|
||||
Location(self, 80, 0x8F9184, LocationType.Visible, "Energy Tank, Firefleas",
|
||||
lambda items: self.CanExit(items))
|
||||
]
|
||||
|
||||
def CanExit(self, items:Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
# /*Bubble Mountain*/
|
||||
return items.CardNorfairL2 or (
|
||||
# /* Volcano Room and Blue Gate */
|
||||
items.Gravity) and items.Wave and (
|
||||
# /*Spikey Acid Snakes and Croc Escape*/
|
||||
items.Grapple or items.SpaceJump)
|
||||
else:
|
||||
# /*Vanilla LN Escape*/
|
||||
return (items.Morph and (items.CardNorfairL2 or # /*Bubble Mountain*/
|
||||
(items.Missile or items.Super or items.Wave) and # /* Blue Gate */
|
||||
(items.SpeedBooster or items.CanFly() or items.Grapple or items.HiJump and
|
||||
(items.CanSpringBallJump() or items.Ice))) or # /*Frog Speedway or Croc Escape*/
|
||||
# /*Reverse Amphitheater*/
|
||||
items.HasEnergyReserves(5))
|
||||
|
||||
def CanEnter(self, items:Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return items.Varia and items.CardLowerNorfairL1 and (
|
||||
self.world.CanEnter("Norfair Upper East", items) and items.CanUsePowerBombs() and items.SpaceJump and items.Gravity or
|
||||
items.CanAccessNorfairLowerPortal() and items.CanDestroyBombWalls() and items.Super and items.CanUsePowerBombs() and items.CanFly())
|
||||
else:
|
||||
return items.Varia and items.CardLowerNorfairL1 and (
|
||||
self.world.CanEnter("Norfair Upper East", items) and items.CanUsePowerBombs() and (items.HiJump or items.Gravity) or
|
||||
items.CanAccessNorfairLowerPortal() and items.CanDestroyBombWalls() and items.Super and (items.CanFly() or items.CanSpringBallJump() or items.SpeedBooster)) and (
|
||||
items.CanFly() or items.HiJump or items.CanSpringBallJump() or items.Ice and items.Charge) and (
|
||||
items.CanPassBombPassages() or items.ScrewAttack and items.SpaceJump)
|
||||
|
||||
def CanComplete(self, items:Progression):
|
||||
return self.GetLocation("Energy Tank, Ridley").Available(items)
|
|
@ -0,0 +1,67 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class West(SMRegion):
|
||||
Name = "Norfair Lower West"
|
||||
Area = "Norfair Lower"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 70, 0x8F8E6E, LocationType.Visible, "Missile (Gold Torizo)",
|
||||
lambda items: items.CanUsePowerBombs() and items.SpaceJump and items.Super if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CanUsePowerBombs() and items.SpaceJump and items.Varia and (
|
||||
items.HiJump or items.Gravity or
|
||||
items.CanAccessNorfairLowerPortal() and (items.CanFly() or items.CanSpringBallJump() or items.SpeedBooster) and items.Super)),
|
||||
Location(self, 71, 0x8F8E74, LocationType.Hidden, "Super Missile (Gold Torizo)",
|
||||
lambda items: items.CanDestroyBombWalls() and (items.Super or items.Charge) and
|
||||
(items.CanAccessNorfairLowerPortal() or items.SpaceJump and items.CanUsePowerBombs()) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CanDestroyBombWalls() and items.Varia and (items.Super or items.Charge)),
|
||||
Location(self, 79, 0x8F9110, LocationType.Chozo, "Screw Attack",
|
||||
lambda items: items.CanDestroyBombWalls() and (items.SpaceJump and items.CanUsePowerBombs() or items.CanAccessNorfairLowerPortal()) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CanDestroyBombWalls() and (items.Varia or items.CanAccessNorfairLowerPortal())),
|
||||
Location(self, 73, 0x8F8F30, LocationType.Visible, "Missile (Mickey Mouse room)",
|
||||
lambda items: items.CanFly() and items.Morph and items.Super and (
|
||||
# /*Exit to Upper Norfair*/
|
||||
(items.CardLowerNorfairL1 or
|
||||
# /*Vanilla or Reverse Lava Dive*/
|
||||
items.Gravity) and
|
||||
# /*Bubble Mountain*/
|
||||
items.CardNorfairL2 or
|
||||
# /* Volcano Room and Blue Gate */
|
||||
items.Gravity and items.Wave and
|
||||
# /*Spikey Acid Snakes and Croc Escape*/
|
||||
(items.Grapple or items.SpaceJump) or
|
||||
# /*Exit via GT fight and Portal*/
|
||||
(items.CanUsePowerBombs() and items.SpaceJump and (items.Super or items.Charge))) if self.Logic == SMLogic.Normal else \
|
||||
lambda items:
|
||||
items.Morph and items.Varia and items.Super and ((items.CanFly() or items.CanSpringBallJump() or
|
||||
items.SpeedBooster and (items.HiJump and items.CanUsePowerBombs() or items.Charge and items.Ice)) and
|
||||
# /*Exit to Upper Norfair*/
|
||||
(items.CardNorfairL2 or (items.SpeedBooster or items.CanFly() or items.Grapple or items.HiJump and
|
||||
(items.CanSpringBallJump() or items.Ice))) or
|
||||
# /*Return to Portal*/
|
||||
items.CanUsePowerBombs()))
|
||||
]
|
||||
|
||||
# // Todo: account for Croc Speedway once Norfair Upper East also do so, otherwise it would be inconsistent to do so here
|
||||
def CanEnter(self, items:Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return items.Varia and (
|
||||
self.world.CanEnter("Norfair Upper East", items) and items.CanUsePowerBombs() and items.SpaceJump and items.Gravity and (
|
||||
# /* Trivial case, Bubble Mountain access */
|
||||
items.CardNorfairL2 or
|
||||
# /* Frog Speedway -> UN Farming Room gate */
|
||||
items.SpeedBooster and items.Wave
|
||||
) or
|
||||
items.CanAccessNorfairLowerPortal() and items.CanDestroyBombWalls()
|
||||
)
|
||||
else:
|
||||
return self.world.CanEnter("Norfair Upper East", items) and items.CanUsePowerBombs() and items.Varia and (items.HiJump or items.Gravity) and (
|
||||
# /* Trivial case, Bubble Mountain access */
|
||||
items.CardNorfairL2 or
|
||||
# /* Frog Speedway -> UN Farming Room gate */
|
||||
items.SpeedBooster and (items.Missile or items.Super or items.Wave) # /* Blue Gate */
|
||||
) or items.CanAccessNorfairLowerPortal() and items.CanDestroyBombWalls()
|
|
@ -0,0 +1,69 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class Crocomire(SMRegion):
|
||||
Name = "Norfair Upper Crocomire"
|
||||
Area = "Norfair Upper"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 52, 0x8F8BA4, LocationType.Visible, "Energy Tank, Crocomire",
|
||||
lambda items: self.CanAccessCrocomire(items) and (items.HasEnergyReserves(1) or items.SpaceJump or items.Grapple) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanAccessCrocomire(items)),
|
||||
Location(self, 54, 0x8F8BC0, LocationType.Visible, "Missile (above Crocomire)",
|
||||
lambda items: items.CanFly() or items.Grapple or items.HiJump and items.SpeedBooster if self.Logic == SMLogic.Normal else \
|
||||
lambda items: (items.CanFly() or items.Grapple or items.HiJump and
|
||||
(items.SpeedBooster or items.CanSpringBallJump() or items.Varia and items.Ice)) and items.CanHellRun()),
|
||||
Location(self, 57, 0x8F8C04, LocationType.Visible, "Power Bomb (Crocomire)",
|
||||
lambda items: self.CanAccessCrocomire(items) and (items.CanFly() or items.HiJump or items.Grapple) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanAccessCrocomire(items)),
|
||||
Location(self, 58, 0x8F8C14, LocationType.Visible, "Missile (below Crocomire)",
|
||||
lambda items: self.CanAccessCrocomire(items) and items.Morph),
|
||||
Location(self, 59, 0x8F8C2A, LocationType.Visible, "Missile (Grappling Beam)",
|
||||
lambda items: self.CanAccessCrocomire(items) and items.Morph and (items.CanFly() or items.SpeedBooster and items.CanUsePowerBombs()) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanAccessCrocomire(items) and (items.SpeedBooster or items.Morph and (items.CanFly() or items.Grapple))),
|
||||
Location(self, 60, 0x8F8C36, LocationType.Chozo, "Grappling Beam",
|
||||
lambda items: self.CanAccessCrocomire(items) and items.Morph and (items.CanFly() or items.SpeedBooster and items.CanUsePowerBombs()) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanAccessCrocomire(items) and (items.SpaceJump or items.Morph or items.Grapple or
|
||||
items.HiJump and items.SpeedBooster))
|
||||
]
|
||||
|
||||
def CanAccessCrocomire(self, items:Progression):
|
||||
return items.CardNorfairBoss if self.Config.Keysanity else items.Super
|
||||
|
||||
def CanEnter(self, items:Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return ((items.CanDestroyBombWalls() or items.SpeedBooster) and items.Super and items.Morph or items.CanAccessNorfairUpperPortal()) and (
|
||||
items.Varia) and (
|
||||
# /* Ice Beam -> Croc Speedway */
|
||||
(items.CardNorfairL1 if self.Config.Keysanity else items.Super) and items.CanUsePowerBombs() and items.SpeedBooster or
|
||||
# /* Frog Speedway */
|
||||
items.SpeedBooster and items.Wave or
|
||||
# /* Cathedral -> through the floor or Vulcano */
|
||||
items.CanOpenRedDoors() and (items.CardNorfairL2 if self.Config.Keysanity else items.Super) and
|
||||
(items.CanFly() or items.HiJump or items.SpeedBooster) and
|
||||
(items.CanPassBombPassages() or items.Gravity and items.Morph) and items.Wave
|
||||
or
|
||||
# /* Reverse Lava Dive */
|
||||
items.CanAccessNorfairLowerPortal() and items.ScrewAttack and items.SpaceJump and items.Super and
|
||||
items.Gravity and items.Wave and (items.CardNorfairL2 or items.Morph))
|
||||
else:
|
||||
return ((items.CanDestroyBombWalls() or items.SpeedBooster) and items.Super and items.Morph or items.CanAccessNorfairUpperPortal()) and (
|
||||
# /* Ice Beam -> Croc Speedway */
|
||||
(items.CardNorfairL1 if self.Config.Keysanity else items.Super) and items.CanUsePowerBombs() and
|
||||
items.SpeedBooster and (items.HasEnergyReserves(3) or items.Varia) or
|
||||
# /* Frog Speedway */
|
||||
items.SpeedBooster and (items.HasEnergyReserves(2) or items.Varia) and
|
||||
(items.Missile or items.Super or items.Wave) or ( # /* Blue Gate */
|
||||
# /* Cathedral -> through the floor or Vulcano */
|
||||
items.CanHellRun()) and items.CanOpenRedDoors() and (items.CardNorfairL2 if self.Config.Keysanity else items.Super) and
|
||||
(items.CanFly() or items.HiJump or items.SpeedBooster or items.CanSpringBallJump() or items.Varia and items.Ice) and
|
||||
(items.CanPassBombPassages() or items.Varia and items.Morph) and
|
||||
(items.Missile or items.Super or items.Wave) # /* Blue Gate */
|
||||
) or (
|
||||
# /* Reverse Lava Dive */
|
||||
items.CanAccessNorfairLowerPortal()) and items.ScrewAttack and items.SpaceJump and items.Varia and items.Super and (
|
||||
items.HasEnergyReserves(2)) and (items.CardNorfairL2 or items.Morph)
|
|
@ -0,0 +1,94 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class East(SMRegion):
|
||||
Name = "Norfair Upper East"
|
||||
Area = "Norfair Upper"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 61, 0x8F8C3E, LocationType.Chozo, "Reserve Tank, Norfair",
|
||||
lambda items: items.CardNorfairL2 and items.Morph and (
|
||||
items.CanFly() or
|
||||
items.Grapple and (items.SpeedBooster or items.CanPassBombPassages()) or
|
||||
items.HiJump or items.Ice
|
||||
) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CardNorfairL2 and items.Morph and items.Super),
|
||||
Location(self, 62, 0x8F8C44, LocationType.Hidden, "Missile (Norfair Reserve Tank)",
|
||||
lambda items: items.CardNorfairL2 and items.Morph and (
|
||||
items.CanFly() or
|
||||
items.Grapple and (items.SpeedBooster or items.CanPassBombPassages()) or
|
||||
items.HiJump or items.Ice
|
||||
) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CardNorfairL2 and items.Morph and items.Super),
|
||||
Location(self, 63, 0x8F8C52, LocationType.Visible, "Missile (bubble Norfair green door)",
|
||||
lambda items: items.CardNorfairL2 and (
|
||||
items.CanFly() or
|
||||
items.Grapple and items.Morph and (items.SpeedBooster or items.CanPassBombPassages()) or
|
||||
items.HiJump or items.Ice
|
||||
) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CardNorfairL2 and items.Super),
|
||||
Location(self, 64, 0x8F8C66, LocationType.Visible, "Missile (bubble Norfair)",
|
||||
lambda items: items.CardNorfairL2),
|
||||
Location(self, 65, 0x8F8C74, LocationType.Hidden, "Missile (Speed Booster)",
|
||||
lambda items: items.CardNorfairL2 and (
|
||||
items.CanFly() or
|
||||
items.Morph and (items.SpeedBooster or items.CanPassBombPassages()) or
|
||||
items.HiJump or items.Ice
|
||||
) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CardNorfairL2 and items.Super),
|
||||
Location(self, 66, 0x8F8C82, LocationType.Chozo, "Speed Booster",
|
||||
lambda items: items.CardNorfairL2 and (
|
||||
items.CanFly() or
|
||||
items.Morph and (items.SpeedBooster or items.CanPassBombPassages()) or
|
||||
items.HiJump or items.Ice
|
||||
) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CardNorfairL2 and items.Super),
|
||||
Location(self, 67, 0x8F8CBC, LocationType.Visible, "Missile (Wave Beam)",
|
||||
lambda items: items.CardNorfairL2 and (
|
||||
items.CanFly() or
|
||||
items.Morph and (items.SpeedBooster or items.CanPassBombPassages()) or
|
||||
items.HiJump or items.Ice
|
||||
) or
|
||||
items.SpeedBooster and items.Wave and items.Morph and items.Super if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CardNorfairL2 or items.Varia),
|
||||
Location(self, 68, 0x8F8CCA, LocationType.Chozo, "Wave Beam",
|
||||
lambda items: items.Morph and (
|
||||
items.CardNorfairL2 and (
|
||||
items.CanFly() or
|
||||
items.Morph and (items.SpeedBooster or items.CanPassBombPassages()) or
|
||||
items.HiJump or items.Ice
|
||||
) or
|
||||
items.SpeedBooster and items.Wave and items.Morph and items.Super
|
||||
) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CanOpenRedDoors() and (items.CardNorfairL2 or items.Varia) and
|
||||
(items.Morph or items.Grapple or items.HiJump and items.Varia or items.SpaceJump))
|
||||
]
|
||||
|
||||
# // Todo: Super is not actually needed for Frog Speedway, but changing this will affect locations
|
||||
# // Todo: Ice Beam -> Croc Speedway is not considered
|
||||
def CanEnter(self, items:Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return ((items.CanDestroyBombWalls() or items.SpeedBooster) and items.Super and items.Morph or
|
||||
items.CanAccessNorfairUpperPortal()
|
||||
) and items.Varia and items.Super and (
|
||||
# /* Cathedral */
|
||||
items.CanOpenRedDoors() and (items.CardNorfairL2 if self.Config.Keysanity else items.Super) and
|
||||
(items.CanFly() or items.HiJump or items.SpeedBooster) or
|
||||
# /* Frog Speedway */
|
||||
items.SpeedBooster and (items.CardNorfairL2 or items.Wave) and items.CanUsePowerBombs()
|
||||
)
|
||||
else:
|
||||
return ((items.CanDestroyBombWalls() or items.SpeedBooster) and items.Super and items.Morph or
|
||||
items.CanAccessNorfairUpperPortal()) and (
|
||||
items.CanHellRun()) and (
|
||||
# /* Cathedral */
|
||||
items.CanOpenRedDoors() and (items.CardNorfairL2 if self.Config.Keysanity else items.Super) and (
|
||||
items.CanFly() or items.HiJump or items.SpeedBooster or
|
||||
items.CanSpringBallJump() or items.Varia and items.Ice
|
||||
) or
|
||||
# /* Frog Speedway */
|
||||
items.SpeedBooster and (items.CardNorfairL2 or items.Missile or items.Super or items.Wave) and items.CanUsePowerBombs())
|
|
@ -0,0 +1,44 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class West(SMRegion):
|
||||
Name = "Norfair Upper West"
|
||||
Area = "Norfair Upper"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 49, 0x8F8AE4, LocationType.Hidden, "Missile (lava room)",
|
||||
lambda items: items.Varia and (
|
||||
items.CanOpenRedDoors() and (items.CanFly() or items.HiJump or items.SpeedBooster) or
|
||||
self.world.CanEnter("Norfair Upper East", items) and items.CardNorfairL2
|
||||
) and items.Morph if self.Logic == SMLogic.Normal else \
|
||||
lambda items: items.CanHellRun() and (
|
||||
items.CanOpenRedDoors() and (
|
||||
items.CanFly() or items.HiJump or items.SpeedBooster or
|
||||
items.CanSpringBallJump() or items.Varia and items.Ice
|
||||
) or
|
||||
self.world.CanEnter("Norfair Upper East", items) and items.CardNorfairL2
|
||||
) and items.Morph),
|
||||
Location(self, 50, 0x8F8B24, LocationType.Chozo, "Ice Beam",
|
||||
lambda items: (items.CardNorfairL1 if config.Keysanity else items.Super) and items.CanPassBombPassages() and items.Varia and items.SpeedBooster if self.Logic == SMLogic.Normal else \
|
||||
lambda items: (items.CardNorfairL1 if config.Keysanity else items.Super) and items.Morph and (items.Varia or items.HasEnergyReserves(3))),
|
||||
Location(self, 51, 0x8F8B46, LocationType.Hidden, "Missile (below Ice Beam)",
|
||||
lambda items: (items.CardNorfairL1 if config.Keysanity else items.Super) and items.CanUsePowerBombs() and items.Varia and items.SpeedBooster if self.Logic == SMLogic.Normal else \
|
||||
lambda items:
|
||||
(items.CardNorfairL1 if config.Keysanity else items.Super) and items.CanUsePowerBombs() and (items.Varia or items.HasEnergyReserves(3)) or
|
||||
(items.Missile or items.Super or items.Wave) and items.Varia and items.SpeedBooster and # /* Blue Gate */
|
||||
# /* Access to Croc's room to get spark */
|
||||
(items.CardNorfairBoss if config.Keysanity else items.Super) and items.CardNorfairL1),
|
||||
Location(self, 53, 0x8F8BAC, LocationType.Chozo, "Hi-Jump Boots",
|
||||
lambda items: items.CanOpenRedDoors() and items.CanPassBombPassages()),
|
||||
Location(self, 55, 0x8F8BE6, LocationType.Visible, "Missile (Hi-Jump Boots)",
|
||||
lambda items: items.CanOpenRedDoors() and items.Morph),
|
||||
Location(self, 56, 0x8F8BEC, LocationType.Visible, "Energy Tank (Hi-Jump Boots)",
|
||||
lambda items: items.CanOpenRedDoors())
|
||||
]
|
||||
|
||||
def CanEnter(self, items:Progression):
|
||||
return (items.CanDestroyBombWalls() or items.SpeedBooster) and items.Super and items.Morph or items.CanAccessNorfairUpperPortal()
|
|
@ -0,0 +1,77 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import SMRegion, IReward, RewardType
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, SMLogic
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class WreckedShip(SMRegion, IReward):
|
||||
Name = "Wrecked Ship"
|
||||
Area = "Wrecked Ship"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Reward = RewardType.GoldenFourBoss
|
||||
self.Locations = [
|
||||
Location(self, 128, 0x8FC265, LocationType.Visible, "Missile (Wrecked Ship middle)",
|
||||
lambda items: items.CanPassBombPassages()),
|
||||
Location(self, 129, 0x8FC2E9, LocationType.Chozo, "Reserve Tank, Wrecked Ship",
|
||||
lambda items: self.CanUnlockShip(items) and items.CardWreckedShipL1 and items.SpeedBooster and items.CanUsePowerBombs() and
|
||||
(items.Grapple or items.SpaceJump or items.Varia and items.HasEnergyReserves(2) or items.HasEnergyReserves(3)) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanUnlockShip(items) and items.CardWreckedShipL1 and items.CanUsePowerBombs() and items.SpeedBooster and
|
||||
(items.Varia or items.HasEnergyReserves(2))),
|
||||
Location(self, 130, 0x8FC2EF, LocationType.Visible, "Missile (Gravity Suit)",
|
||||
lambda items: self.CanUnlockShip(items) and items.CardWreckedShipL1 and
|
||||
(items.Grapple or items.SpaceJump or items.Varia and items.HasEnergyReserves(2) or items.HasEnergyReserves(3)) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanUnlockShip(items) and items.CardWreckedShipL1 and (items.Varia or items.HasEnergyReserves(1))),
|
||||
Location(self, 131, 0x8FC319, LocationType.Visible, "Missile (Wrecked Ship top)",
|
||||
lambda items: self.CanUnlockShip(items)),
|
||||
Location(self, 132, 0x8FC337, LocationType.Visible, "Energy Tank, Wrecked Ship",
|
||||
lambda items: self.CanUnlockShip(items) and
|
||||
(items.HiJump or items.SpaceJump or items.SpeedBooster or items.Gravity) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanUnlockShip(items) and (items.Bombs or items.PowerBomb or items.CanSpringBallJump() or
|
||||
items.HiJump or items.SpaceJump or items.SpeedBooster or items.Gravity)),
|
||||
Location(self, 133, 0x8FC357, LocationType.Visible, "Super Missile (Wrecked Ship left)",
|
||||
lambda items: self.CanUnlockShip(items)),
|
||||
Location(self, 134, 0x8FC365, LocationType.Visible, "Right Super, Wrecked Ship",
|
||||
lambda items: self.CanUnlockShip(items)),
|
||||
Location(self, 135, 0x8FC36D, LocationType.Chozo, "Gravity Suit",
|
||||
lambda items: self.CanUnlockShip(items) and items.CardWreckedShipL1 and
|
||||
(items.Grapple or items.SpaceJump or items.Varia and items.HasEnergyReserves(2) or items.HasEnergyReserves(3)) if self.Logic == SMLogic.Normal else \
|
||||
lambda items: self.CanUnlockShip(items) and items.CardWreckedShipL1 and (items.Varia or items.HasEnergyReserves(1)))
|
||||
]
|
||||
|
||||
def CanUnlockShip(self, items:Progression):
|
||||
return items.CardWreckedShipBoss and items.CanPassBombPassages()
|
||||
|
||||
def CanEnter(self, items:Progression):
|
||||
if self.Logic == SMLogic.Normal:
|
||||
return items.Super and (
|
||||
# /* Over the Moat */
|
||||
(items.CardCrateriaL2 if self.Config.Keysanity else items.CanUsePowerBombs()) and (
|
||||
items.SpeedBooster or items.Grapple or items.SpaceJump or
|
||||
items.Gravity and (items.CanIbj() or items.HiJump)
|
||||
) or
|
||||
# /* Through Maridia -> Forgotten Highway */
|
||||
items.CanUsePowerBombs() and items.Gravity or
|
||||
# /* From Maridia portal -> Forgotten Highway */
|
||||
items.CanAccessMaridiaPortal(self.world) and items.Gravity and (
|
||||
items.CanDestroyBombWalls() and items.CardMaridiaL2 or
|
||||
self.world.GetLocation("Space Jump").Available(items)))
|
||||
else:
|
||||
return items.Super and (
|
||||
# /* Over the Moat */
|
||||
(items.CardCrateriaL2 if self.Config.Keysanity else items.CanUsePowerBombs()) or
|
||||
# /* Through Maridia -> Forgotten Highway */
|
||||
items.CanUsePowerBombs() and (
|
||||
items.Gravity or
|
||||
# /* Climb Mt. Everest */
|
||||
items.HiJump and (items.Ice or items.CanSpringBallJump()) and items.Grapple and items.CardMaridiaL1
|
||||
) or
|
||||
# /* From Maridia portal -> Forgotten Highway */
|
||||
items.CanAccessMaridiaPortal(self.world) and (
|
||||
items.HiJump and items.CanPassBombPassages() and items.CardMaridiaL2 or
|
||||
items.Gravity and (
|
||||
items.CanDestroyBombWalls() and items.CardMaridiaL2 or
|
||||
self.world.GetLocation("Space Jump").Available(items))))
|
||||
|
||||
def CanComplete(self, items:Progression):
|
||||
return self.CanEnter(items) and self.CanUnlockShip(items)
|
|
@ -0,0 +1,25 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType, IReward
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression, ItemType
|
||||
|
||||
class CastleTower(Z3Region, IReward):
|
||||
Name = "Castle Tower"
|
||||
Area = "Castle Tower"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Reward = RewardType.Agahnim
|
||||
self.RegionItems = [ItemType.KeyCT]
|
||||
self.Locations = [
|
||||
Location(self, 256+101, 0x1EAB5, LocationType.Regular, "Castle Tower - Foyer"),
|
||||
Location(self, 256+102, 0x1EAB2, LocationType.Regular, "Castle Tower - Dark Maze",
|
||||
lambda items: items.Lamp and items.KeyCT >= 1)
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.CanKillManyEnemies() and (items.Cape or items.MasterSword)
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.CanEnter(items) and items.Lamp and items.KeyCT >= 2 and items.Sword
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class East(Z3Region):
|
||||
Name = "Dark World Death Mountain East"
|
||||
Area = "Dark World"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 256+65, 0x1EB51, LocationType.Regular, "Hookshot Cave - Top Right",
|
||||
lambda items: items.MoonPearl and items.Hookshot),
|
||||
Location(self, 256+66, 0x1EB54, LocationType.Regular, "Hookshot Cave - Top Left",
|
||||
lambda items: items.MoonPearl and items.Hookshot),
|
||||
Location(self, 256+67, 0x1EB57, LocationType.Regular, "Hookshot Cave - Bottom Left",
|
||||
lambda items: items.MoonPearl and items.Hookshot),
|
||||
Location(self, 256+68, 0x1EB5A, LocationType.Regular, "Hookshot Cave - Bottom Right",
|
||||
lambda items: items.MoonPearl and (items.Hookshot or items.Boots)),
|
||||
Location(self, 256+69, 0x1EA7C, LocationType.Regular, "Superbunny Cave - Top",
|
||||
lambda items: items.MoonPearl),
|
||||
Location(self, 256+70, 0x1EA7F, LocationType.Regular, "Superbunny Cave - Bottom",
|
||||
lambda items: items.MoonPearl)
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.CanLiftHeavy() and self.world.CanEnter("Light World Death Mountain East", items)
|
|
@ -0,0 +1,16 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
|
||||
class West(Z3Region):
|
||||
Name = "Dark World Death Mountain West"
|
||||
Area = "Dark World"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 256+64, 0x1EA8B, LocationType.Regular, "Spike Cave",
|
||||
lambda items: items.MoonPearl and items.Hammer and items.CanLiftLight() and
|
||||
(items.CanExtendMagic() and items.Cape or items.Byrna) and
|
||||
self.world.CanEnter("Light World Death Mountain West", items))
|
||||
]
|
|
@ -0,0 +1,20 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class Mire(Z3Region):
|
||||
Name = "Dark World Mire"
|
||||
Area = "Dark World"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 256+89, 0x1EA73, LocationType.Regular, "Mire Shed - Left",
|
||||
lambda items: items.MoonPearl),
|
||||
Location(self, 256+90, 0x1EA76, LocationType.Regular, "Mire Shed - Right",
|
||||
lambda items: items.MoonPearl)
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.Flute and items.CanLiftHeavy() or items.CanAccessMiseryMirePortal(Config)
|
|
@ -0,0 +1,28 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class NorthEast(Z3Region):
|
||||
Name = "Dark World North East"
|
||||
Area = "Dark World"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 256+78, 0x1DE185, LocationType.Regular, "Catfish",
|
||||
lambda items: items.MoonPearl and items.CanLiftLight()),
|
||||
Location(self, 256+79, 0x308147, LocationType.Regular, "Pyramid"),
|
||||
Location(self, 256+80, 0x1E980, LocationType.Regular, "Pyramid Fairy - Left",
|
||||
lambda items: self.world.CanAquireAll(items, RewardType.CrystalRed) and items.MoonPearl and self.world.CanEnter("Dark World South", items) and
|
||||
(items.Hammer or items.Mirror and self.world.CanAquire(items, RewardType.Agahnim))),
|
||||
Location(self, 256+81, 0x1E983, LocationType.Regular, "Pyramid Fairy - Right",
|
||||
lambda items: self.world.CanAquireAll(items, RewardType.CrystalRed) and items.MoonPearl and self.world.CanEnter("Dark World South", items) and
|
||||
(items.Hammer or items.Mirror and self.world.CanAquire(items, RewardType.Agahnim)))
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return self.world.CanAquire(items, RewardType.Agahnim) or items.MoonPearl and (
|
||||
items.Hammer and items.CanLiftLight() or
|
||||
items.CanLiftHeavy() and items.Flippers or
|
||||
items.CanAccessDarkWorldPortal(self.Config) and items.Flippers)
|
|
@ -0,0 +1,32 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class NorthWest(Z3Region):
|
||||
Name = "Dark World North West"
|
||||
Area = "Dark World"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 256+71, 0x308146, LocationType.Regular, "Bumper Cave",
|
||||
lambda items: items.CanLiftLight() and items.Cape),
|
||||
Location(self, 256+72, 0x1EDA8, LocationType.Regular, "Chest Game"),
|
||||
Location(self, 256+73, 0x1E9EF, LocationType.Regular, "C-Shaped House"),
|
||||
Location(self, 256+74, 0x1E9EC, LocationType.Regular, "Brewery"),
|
||||
Location(self, 256+75, 0x308006, LocationType.Regular, "Hammer Pegs",
|
||||
lambda items: items.CanLiftHeavy() and items.Hammer),
|
||||
Location(self, 256+76, 0x30802A, LocationType.Regular, "Blacksmith",
|
||||
lambda items: items.CanLiftHeavy()),
|
||||
Location(self, 256+77, 0x6BD68, LocationType.Regular, "Purple Chest",
|
||||
lambda items: items.CanLiftHeavy())
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.MoonPearl and ((
|
||||
self.world.CanAquire(items, RewardType.Agahnim) or
|
||||
items.CanAccessDarkWorldPortal(self.Config) and items.Flippers
|
||||
) and items.Hookshot and (items.Flippers or items.CanLiftLight() or items.Hammer) or
|
||||
items.Hammer and items.CanLiftLight() or
|
||||
items.CanLiftHeavy())
|
|
@ -0,0 +1,28 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class South(Z3Region):
|
||||
Name = "Dark World South"
|
||||
Area = "Dark World"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 256+82, 0x308148, LocationType.Regular, "Digging Game"),
|
||||
Location(self, 256+83, 0x6B0C7, LocationType.Regular, "Stumpy"),
|
||||
Location(self, 256+84, 0x1EB1E, LocationType.Regular, "Hype Cave - Top"),
|
||||
Location(self, 256+85, 0x1EB21, LocationType.Regular, "Hype Cave - Middle Right"),
|
||||
Location(self, 256+86, 0x1EB24, LocationType.Regular, "Hype Cave - Middle Left"),
|
||||
Location(self, 256+87, 0x1EB27, LocationType.Regular, "Hype Cave - Bottom"),
|
||||
Location(self, 256+88, 0x308011, LocationType.Regular, "Hype Cave - NPC")
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.MoonPearl and ((
|
||||
self.world.CanAquire(items, RewardType.Agahnim) or
|
||||
items.CanAccessDarkWorldPortal(self.Config) and items.Flippers
|
||||
) and (items.Hammer or items.Hookshot and (items.Flippers or items.CanLiftLight())) or
|
||||
items.Hammer and items.CanLiftLight() or
|
||||
items.CanLiftHeavy())
|
|
@ -0,0 +1,42 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType, IReward
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import ItemType, Progression
|
||||
|
||||
class DesertPalace(Z3Region, IReward):
|
||||
Name = "Desert Palace"
|
||||
Area = "Desert Palace"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Reward = RewardType.Null
|
||||
self.RegionItems = [ ItemType.KeyDP, ItemType.BigKeyDP, ItemType.MapDP, ItemType.CompassDP ]
|
||||
self.Locations = [
|
||||
Location(self, 256+109, 0x1E98F, LocationType.Regular, "Desert Palace - Big Chest",
|
||||
lambda items: items.BigKeyDP),
|
||||
Location(self, 256+110, 0x308160, LocationType.Regular, "Desert Palace - Torch",
|
||||
lambda items: items.Boots),
|
||||
Location(self, 256+111, 0x1E9B6, LocationType.Regular, "Desert Palace - Map Chest"),
|
||||
Location(self, 256+112, 0x1E9C2, LocationType.Regular, "Desert Palace - Big Key Chest",
|
||||
lambda items: items.KeyDP),
|
||||
Location(self, 256+113, 0x1E9CB, LocationType.Regular, "Desert Palace - Compass Chest",
|
||||
lambda items: items.KeyDP),
|
||||
Location(self, 256+114, 0x308151, LocationType.Regular, "Desert Palace - Lanmolas",
|
||||
lambda items: (
|
||||
items.CanLiftLight() or
|
||||
items.CanAccessMiseryMirePortal(self.Config) and items.Mirror
|
||||
) and items.BigKeyDP and items.KeyDP and items.CanLightTorches() and self.CanBeatBoss(items))
|
||||
]
|
||||
|
||||
def CanBeatBoss(self, items: Progression):
|
||||
return items.Sword or items.Hammer or items.Bow or \
|
||||
items.Firerod or items.Icerod or \
|
||||
items.Byrna or items.Somaria
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.Book or \
|
||||
items.Mirror and items.CanLiftHeavy() and items.Flute or \
|
||||
items.CanAccessMiseryMirePortal(self.Config) and items.Mirror
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.GetLocation("Desert Palace - Lanmolas").Available(items)
|
|
@ -0,0 +1,27 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType, IReward
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression, ItemType
|
||||
|
||||
class EasternPalace(Z3Region, IReward):
|
||||
Name = "Eastern Palace"
|
||||
Area = "Eastern Palace"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Reward = RewardType.Null
|
||||
self.RegionItems = [ ItemType.BigKeyEP, ItemType.MapEP, ItemType.CompassEP ]
|
||||
self.Locations = [
|
||||
Location(self, 256+103, 0x1E9B3, LocationType.Regular, "Eastern Palace - Cannonball Chest"),
|
||||
Location(self, 256+104, 0x1E9F5, LocationType.Regular, "Eastern Palace - Map Chest"),
|
||||
Location(self, 256+105, 0x1E977, LocationType.Regular, "Eastern Palace - Compass Chest"),
|
||||
Location(self, 256+106, 0x1E97D, LocationType.Regular, "Eastern Palace - Big Chest",
|
||||
lambda items: items.BigKeyEP),
|
||||
Location(self, 256+107, 0x1E9B9, LocationType.Regular, "Eastern Palace - Big Key Chest",
|
||||
lambda items: items.Lamp),
|
||||
Location(self, 256+108, 0x308150, LocationType.Regular, "Eastern Palace - Armos Knights",
|
||||
lambda items: items.BigKeyEP and items.Bow and items.Lamp)
|
||||
]
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.GetLocation("Eastern Palace - Armos Knights").Available(items)
|
|
@ -0,0 +1,146 @@
|
|||
from typing import List
|
||||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, GameMode, KeyShuffle
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Item, Progression, ItemType
|
||||
|
||||
class GanonsTower(Z3Region):
|
||||
Name = "Ganon's Tower"
|
||||
Area = "Ganon's Tower"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Reward = RewardType.Null
|
||||
self.RegionItems = [ ItemType.KeyGT, ItemType.BigKeyGT, ItemType.MapGT , ItemType.CompassGT]
|
||||
self.Locations = [
|
||||
Location(self, 256+189, 0x308161, LocationType.Regular, "Ganon's Tower - Bob's Torch",
|
||||
lambda items: items.Boots),
|
||||
Location(self, 256+190, 0x1EAB8, LocationType.Regular, "Ganon's Tower - DMs Room - Top Left",
|
||||
lambda items: items.Hammer and items.Hookshot),
|
||||
Location(self, 256+191, 0x1EABB, LocationType.Regular, "Ganon's Tower - DMs Room - Top Right",
|
||||
lambda items: items.Hammer and items.Hookshot),
|
||||
Location(self, 256+192, 0x1EABE, LocationType.Regular, "Ganon's Tower - DMs Room - Bottom Left",
|
||||
lambda items: items.Hammer and items.Hookshot),
|
||||
Location(self, 256+193, 0x1EAC1, LocationType.Regular, "Ganon's Tower - DMs Room - Bottom Right",
|
||||
lambda items: items.Hammer and items.Hookshot),
|
||||
Location(self, 256+194, 0x1EAD3, LocationType.Regular, "Ganon's Tower - Map Chest",
|
||||
lambda items: items.Hammer and (items.Hookshot or items.Boots) and items.KeyGT >=
|
||||
(3 if any(self.GetLocation("Ganon's Tower - Map Chest").ItemIs(type, self.world) for type in [ItemType.BigKeyGT, ItemType.KeyGT]) else 4))
|
||||
.AlwaysAllow(lambda item, items: item.Is(ItemType.KeyGT, self.world) and items.KeyGT >= 3),
|
||||
Location(self, 256+195, 0x1EAD0, LocationType.Regular, "Ganon's Tower - Firesnake Room",
|
||||
lambda items: items.Hammer and items.Hookshot and items.KeyGT >= (2 if any(l.ItemIs(ItemType.BigKeyGT, self.world) for l in [
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Right"),
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Left"),
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"),
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
||||
]) or self.GetLocation("Ganon's Tower - Firesnake Room").ItemIs(ItemType.KeyGT, self.world) else 3)),
|
||||
Location(self, 256+196, 0x1EAC4, LocationType.Regular, "Ganon's Tower - Randomizer Room - Top Left",
|
||||
lambda items: self.LeftSide(items, [
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Right"),
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"),
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
||||
])),
|
||||
Location(self, 256+197, 0x1EAC7, LocationType.Regular, "Ganon's Tower - Randomizer Room - Top Right",
|
||||
lambda items: self.LeftSide(items, [
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Left"),
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"),
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
||||
])),
|
||||
Location(self, 256+198, 0x1EACA, LocationType.Regular, "Ganon's Tower - Randomizer Room - Bottom Left",
|
||||
lambda items: self.LeftSide(items, [
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Right"),
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Left"),
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Right")
|
||||
])),
|
||||
Location(self, 256+199, 0x1EACD, LocationType.Regular, "Ganon's Tower - Randomizer Room - Bottom Right",
|
||||
lambda items: self.LeftSide(items, [
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Right"),
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Top Left"),
|
||||
self.GetLocation("Ganon's Tower - Randomizer Room - Bottom Left")
|
||||
])),
|
||||
Location(self, 256+200, 0x1EAD9, LocationType.Regular, "Ganon's Tower - Hope Room - Left"),
|
||||
Location(self, 256+201, 0x1EADC, LocationType.Regular, "Ganon's Tower - Hope Room - Right"),
|
||||
Location(self, 256+202, 0x1EAE2, LocationType.Regular, "Ganon's Tower - Tile Room",
|
||||
lambda items: items.Somaria),
|
||||
Location(self, 256+203, 0x1EAE5, LocationType.Regular, "Ganon's Tower - Compass Room - Top Left",
|
||||
lambda items: self.RightSide(items, [
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Top Right"),
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Bottom Left"),
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Bottom Right")
|
||||
])),
|
||||
Location(self, 256+204, 0x1EAE8, LocationType.Regular, "Ganon's Tower - Compass Room - Top Right",
|
||||
lambda items: self.RightSide(items, [
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Top Left"),
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Bottom Left"),
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Bottom Right")
|
||||
])),
|
||||
Location(self, 256+205, 0x1EAEB, LocationType.Regular, "Ganon's Tower - Compass Room - Bottom Left",
|
||||
lambda items: self.RightSide(items, [
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Top Right"),
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Top Left"),
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Bottom Right")
|
||||
])),
|
||||
Location(self, 256+206, 0x1EAEE, LocationType.Regular, "Ganon's Tower - Compass Room - Bottom Right",
|
||||
lambda items: self.RightSide(items, [
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Top Right"),
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Top Left"),
|
||||
self.GetLocation("Ganon's Tower - Compass Room - Bottom Left")
|
||||
])),
|
||||
Location(self, 256+207, 0x1EADF, LocationType.Regular, "Ganon's Tower - Bob's Chest",
|
||||
lambda items: items.KeyGT >= 3 and (
|
||||
items.Hammer and items.Hookshot or
|
||||
items.Somaria and items.Firerod)),
|
||||
Location(self, 256+208, 0x1EAD6, LocationType.Regular, "Ganon's Tower - Big Chest",
|
||||
lambda items: items.BigKeyGT and items.KeyGT >= 3 and (
|
||||
items.Hammer and items.Hookshot or
|
||||
items.Somaria and items.Firerod))
|
||||
.Allow(lambda item, items: item.IsNot(ItemType.BigKeyGT, self.world)),
|
||||
Location(self, 256+209, 0x1EAF1, LocationType.Regular, "Ganon's Tower - Big Key Chest", self.BigKeyRoom),
|
||||
Location(self, 256+210, 0x1EAF4, LocationType.Regular, "Ganon's Tower - Big Key Room - Left", self.BigKeyRoom),
|
||||
Location(self, 256+211, 0x1EAF7, LocationType.Regular, "Ganon's Tower - Big Key Room - Right", self.BigKeyRoom),
|
||||
Location(self, 256+212, 0x1EAFD, LocationType.Regular, "Ganon's Tower - Mini Helmasaur Room - Left", self.TowerAscend)
|
||||
.Allow(lambda item, items: item.IsNot(ItemType.BigKeyGT, self.world)),
|
||||
Location(self, 256+213, 0x1EB00, LocationType.Regular, "Ganon's Tower - Mini Helmasaur Room - Right", self.TowerAscend)
|
||||
.Allow(lambda item, items: item.IsNot(ItemType.BigKeyGT, self.world)),
|
||||
Location(self, 256+214, 0x1EB03, LocationType.Regular, "Ganon's Tower - Pre-Moldorm Chest", self.TowerAscend)
|
||||
.Allow(lambda item, items: item.IsNot(ItemType.BigKeyGT, self.world)),
|
||||
Location(self, 256+215, 0x1EB06, LocationType.Regular, "Ganon's Tower - Moldorm Chest",
|
||||
lambda items: items.BigKeyGT and items.KeyGT >= 4 and
|
||||
items.Bow and items.CanLightTorches() and
|
||||
self.CanBeatMoldorm(items) and items.Hookshot)
|
||||
.Allow(lambda item, items: all(item.IsNot(type, self.world) for type in [ ItemType.KeyGT, ItemType.BigKeyGT ]))
|
||||
]
|
||||
|
||||
def LeftSide(self, items: Progression, locations: List[Location]):
|
||||
return items.Hammer and items.Hookshot and items.KeyGT >= (3 if any(l.ItemIs(ItemType.BigKeyGT, self.world) for l in locations) else 4)
|
||||
|
||||
def RightSide(self, items: Progression, locations: List[Location]):
|
||||
return items.Somaria and items.Firerod and items.KeyGT >= (3 if any(l.ItemIs(ItemType.BigKeyGT, self.world) for l in locations) else 4)
|
||||
|
||||
def BigKeyRoom(self, items: Progression):
|
||||
return items.KeyGT >= 3 and self.CanBeatArmos(items) \
|
||||
and (items.Hammer and items.Hookshot or items.Firerod and items.Somaria)
|
||||
|
||||
def TowerAscend(self, items: Progression):
|
||||
return items.BigKeyGT and items.KeyGT >= 3 and items.Bow and items.CanLightTorches()
|
||||
|
||||
def CanBeatArmos(self, items: Progression):
|
||||
return items.Sword or items.Hammer or items.Bow or \
|
||||
items.CanExtendMagic(2) and (items.Somaria or items.Byrna) or \
|
||||
items.CanExtendMagic(4) and (items.Firerod or items.Icerod)
|
||||
|
||||
def CanBeatMoldorm(self, items: Progression):
|
||||
return items.Sword or items.Hammer
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.MoonPearl and self.world.CanEnter("Dark World Death Mountain East", items) and \
|
||||
self.world.CanAquireAll(items, RewardType.CrystalBlue, RewardType.CrystalRed, RewardType.GoldenFourBoss)
|
||||
|
||||
def CanFill(self, item: Item):
|
||||
if (self.Config.GameMode == GameMode.Multiworld):
|
||||
if (item.World != self.world or item.Progression):
|
||||
return False
|
||||
if (self.Config.KeyShuffle == KeyShuffle.Keysanity and not ((item.Type == ItemType.BigKeyGT or item.Type == ItemType.KeyGT) and item.World == self.world) and (item.IsKey() or item.IsBigKey() or item.IsKeycard())):
|
||||
return False
|
||||
return super().CanFill(item)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
from typing import List
|
||||
from worlds.smz3.TotalSMZ3.Region import Z3Region
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import ItemType
|
||||
|
||||
class HyruleCastle(Z3Region):
|
||||
Name = "Hyrule Castle"
|
||||
Area = "Hyrule Castle"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.RegionItems = [ ItemType.KeyHC, ItemType.MapHC]
|
||||
sphereOne = -10
|
||||
self.Locations = [
|
||||
Location(self, 256+91, 0x1EA79, LocationType.Regular, "Sanctuary").Weighted(sphereOne),
|
||||
Location(self, 256+92, 0x1EB5D, LocationType.Regular, "Sewers - Secret Room - Left",
|
||||
lambda items: items.CanLiftLight() or items.Lamp and items.KeyHC),
|
||||
Location(self, 256+93, 0x1EB60, LocationType.Regular, "Sewers - Secret Room - Middle",
|
||||
lambda items: items.CanLiftLight() or items.Lamp and items.KeyHC),
|
||||
Location(self, 256+94, 0x1EB63, LocationType.Regular, "Sewers - Secret Room - Right",
|
||||
lambda items: items.CanLiftLight() or items.Lamp and items.KeyHC),
|
||||
Location(self, 256+95, 0x1E96E, LocationType.Regular, "Sewers - Dark Cross",
|
||||
lambda items: items.Lamp),
|
||||
Location(self, 256+96, 0x1EB0C, LocationType.Regular, "Hyrule Castle - Map Chest").Weighted(sphereOne),
|
||||
Location(self, 256+97, 0x1E974, LocationType.Regular, "Hyrule Castle - Boomerang Chest",
|
||||
lambda items: items.KeyHC).Weighted(sphereOne),
|
||||
Location(self, 256+98, 0x1EB09, LocationType.Regular, "Hyrule Castle - Zelda's Cell",
|
||||
lambda items: items.KeyHC).Weighted(sphereOne),
|
||||
Location(self, 256+99, 0x5DF45, LocationType.NotInDungeon, "Link's Uncle")
|
||||
.Allow(lambda item, items: self.Config.Keysanity or not item.IsDungeonItem()).Weighted(sphereOne),
|
||||
Location(self, 256+100, 0x1E971, LocationType.NotInDungeon, "Secret Passage")
|
||||
.Allow(lambda item, items: self.Config.Keysanity or not item.IsDungeonItem()).Weighted(sphereOne),
|
||||
]
|
|
@ -0,0 +1,52 @@
|
|||
from typing import List
|
||||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType, IReward
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression, ItemType
|
||||
|
||||
class IcePalace(Z3Region, IReward):
|
||||
Name = "Ice Palace"
|
||||
Area = "Ice Palace"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.RegionItems = [ ItemType.KeyIP, ItemType.BigKeyIP, ItemType.MapIP, ItemType.CompassIP]
|
||||
self.Reward = RewardType.Null
|
||||
self.Locations = [
|
||||
Location(self, 256+161, 0x1E9D4, LocationType.Regular, "Ice Palace - Compass Chest"),
|
||||
Location(self, 256+162, 0x1E9E0, LocationType.Regular, "Ice Palace - Spike Room",
|
||||
lambda items: items.Hookshot or items.KeyIP >= 1 and self.CanNotWasteKeysBeforeAccessible(items, [
|
||||
self.GetLocation("Ice Palace - Map Chest"),
|
||||
self.GetLocation("Ice Palace - Big Key Chest")
|
||||
])),
|
||||
Location(self, 256+163, 0x1E9DD, LocationType.Regular, "Ice Palace - Map Chest",
|
||||
lambda items: items.Hammer and items.CanLiftLight() and (
|
||||
items.Hookshot or items.KeyIP >= 1 and self.CanNotWasteKeysBeforeAccessible(items, [
|
||||
self.GetLocation("Ice Palace - Spike Room"),
|
||||
self.GetLocation("Ice Palace - Big Key Chest")
|
||||
])
|
||||
)),
|
||||
Location(self, 256+164, 0x1E9A4, LocationType.Regular, "Ice Palace - Big Key Chest",
|
||||
lambda items: items.Hammer and items.CanLiftLight() and (
|
||||
items.Hookshot or items.KeyIP >= 1 and self.CanNotWasteKeysBeforeAccessible(items, [
|
||||
self.GetLocation("Ice Palace - Spike Room"),
|
||||
self.GetLocation("Ice Palace - Map Chest")
|
||||
])
|
||||
)),
|
||||
Location(self, 256+165, 0x1E9E3, LocationType.Regular, "Ice Palace - Iced T Room"),
|
||||
Location(self, 256+166, 0x1E995, LocationType.Regular, "Ice Palace - Freezor Chest"),
|
||||
Location(self, 256+167, 0x1E9AA, LocationType.Regular, "Ice Palace - Big Chest",
|
||||
lambda items: items.BigKeyIP),
|
||||
Location(self, 256+168, 0x308157, LocationType.Regular, "Ice Palace - Kholdstare",
|
||||
lambda items: items.BigKeyIP and items.Hammer and items.CanLiftLight() and
|
||||
items.KeyIP >= (1 if items.Somaria else 2))
|
||||
]
|
||||
|
||||
def CanNotWasteKeysBeforeAccessible(self, items: Progression, locations: List[Location]):
|
||||
return not items.BigKeyIP or any(l.ItemIs(ItemType.BigKeyIP, self.world) for l in locations)
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.MoonPearl and items.Flippers and items.CanLiftHeavy() and items.CanMeltFreezors()
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.GetLocation("Ice Palace - Kholdstare").Available(items)
|
|
@ -0,0 +1,30 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class East(Z3Region):
|
||||
Name = "Light World Death Mountain East"
|
||||
Area = "Death Mountain"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 256+4, 0x308141, LocationType.Regular, "Floating Island",
|
||||
lambda items: items.Mirror and items.MoonPearl and items.CanLiftHeavy()),
|
||||
Location(self, 256+5, 0x1E9BF, LocationType.Regular, "Spiral Cave"),
|
||||
Location(self, 256+6, 0x1EB39, LocationType.Regular, "Paradox Cave Upper - Left"),
|
||||
Location(self, 256+7, 0x1EB3C, LocationType.Regular, "Paradox Cave Upper - Right"),
|
||||
Location(self, 256+8, 0x1EB2A, LocationType.Regular, "Paradox Cave Lower - Far Left"),
|
||||
Location(self, 256+9, 0x1EB2D, LocationType.Regular, "Paradox Cave Lower - Left"),
|
||||
Location(self, 256+10, 0x1EB36, LocationType.Regular, "Paradox Cave Lower - Middle"),
|
||||
Location(self, 256+11, 0x1EB30, LocationType.Regular, "Paradox Cave Lower - Right"),
|
||||
Location(self, 256+12, 0x1EB33, LocationType.Regular, "Paradox Cave Lower - Far Right"),
|
||||
Location(self, 256+13, 0x1E9C5, LocationType.Regular, "Mimic Cave",
|
||||
lambda items: items.Mirror and items.KeyTR >= 2 and self.world.CanEnter("Turtle Rock", items))
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return self.world.CanEnter("Light World Death Mountain West", items) and (
|
||||
items.Hammer and items.Mirror or
|
||||
items.Hookshot)
|
|
@ -0,0 +1,23 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression
|
||||
|
||||
class West(Z3Region):
|
||||
Name = "Light World Death Mountain West"
|
||||
Area = "Death Mountain"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.Locations = [
|
||||
Location(self, 256+0, 0x308016, LocationType.Ether, "Ether Tablet",
|
||||
lambda items: items.Book and items.MasterSword and (items.Mirror or items.Hammer and items.Hookshot)),
|
||||
Location(self, 256+1, 0x308140, LocationType.Regular, "Spectacle Rock",
|
||||
lambda items: items.Mirror),
|
||||
Location(self, 256+2, 0x308002, LocationType.Regular, "Spectacle Rock Cave"),
|
||||
Location(self, 256+3, 0x1EE9FA, LocationType.Regular, "Old Man",
|
||||
lambda items: items.Lamp)
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.Flute or items.CanLiftLight() and items.Lamp or items.CanAccessDeathMountainPortal()
|
|
@ -0,0 +1,28 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
|
||||
class NorthEast(Z3Region):
|
||||
Name = "Light World North East"
|
||||
Area = "Light World"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
sphereOne = -10
|
||||
self.Locations = [
|
||||
Location(self, 256+36, 0x1DE1C3, LocationType.Regular, "King Zora",
|
||||
lambda items: items.CanLiftLight() or items.Flippers),
|
||||
Location(self, 256+37, 0x308149, LocationType.Regular, "Zora's Ledge",
|
||||
lambda items: items.Flippers),
|
||||
Location(self, 256+254, 0x1E9B0, LocationType.Regular, "Waterfall Fairy - Left",
|
||||
lambda items: items.Flippers),
|
||||
Location(self, 256+39, 0x1E9D1, LocationType.Regular, "Waterfall Fairy - Right",
|
||||
lambda items: items.Flippers),
|
||||
Location(self, 256+40, 0x308014, LocationType.Regular, "Potion Shop",
|
||||
lambda items: items.Mushroom),
|
||||
Location(self, 256+41, 0x1EA82, LocationType.Regular, "Sahasrahla's Hut - Left").Weighted(sphereOne),
|
||||
Location(self, 256+42, 0x1EA85, LocationType.Regular, "Sahasrahla's Hut - Middle").Weighted(sphereOne),
|
||||
Location(self, 256+43, 0x1EA88, LocationType.Regular, "Sahasrahla's Hut - Right").Weighted(sphereOne),
|
||||
Location(self, 256+44, 0x5F1FC, LocationType.Regular, "Sahasrahla",
|
||||
lambda items: self.world.CanAquire(items, RewardType.PendantGreen))
|
||||
]
|
|
@ -0,0 +1,44 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
|
||||
class NorthWest(Z3Region):
|
||||
Name = "Light World North West"
|
||||
Area = "Light World"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
sphereOne = -14
|
||||
self.Locations = [
|
||||
Location(self, 256+14, 0x589B0, LocationType.Pedestal, "Master Sword Pedestal",
|
||||
lambda items: self.world.CanAquireAll(items, RewardType.PendantGreen, RewardType.PendantNonGreen)),
|
||||
Location(self, 256+15, 0x308013, LocationType.Regular, "Mushroom").Weighted(sphereOne),
|
||||
Location(self, 256+16, 0x308000, LocationType.Regular, "Lost Woods Hideout").Weighted(sphereOne),
|
||||
Location(self, 256+17, 0x308001, LocationType.Regular, "Lumberjack Tree",
|
||||
lambda items: self.world.CanAquire(items, RewardType.Agahnim) and items.Boots),
|
||||
Location(self, 256+18, 0x1EB3F, LocationType.Regular, "Pegasus Rocks",
|
||||
lambda items: items.Boots),
|
||||
Location(self, 256+19, 0x308004, LocationType.Regular, "Graveyard Ledge",
|
||||
lambda items: items.Mirror and items.MoonPearl and self.world.CanEnter("Dark World North West", items)),
|
||||
Location(self, 256+20, 0x1E97A, LocationType.Regular, "King's Tomb",
|
||||
lambda items: items.Boots and (
|
||||
items.CanLiftHeavy() or
|
||||
items.Mirror and items.MoonPearl and self.world.CanEnter("Dark World North West", items))),
|
||||
Location(self, 256+21, 0x1EA8E, LocationType.Regular, "Kakariko Well - Top").Weighted(sphereOne),
|
||||
Location(self, 256+22, 0x1EA91, LocationType.Regular, "Kakariko Well - Left").Weighted(sphereOne),
|
||||
Location(self, 256+23, 0x1EA94, LocationType.Regular, "Kakariko Well - Middle").Weighted(sphereOne),
|
||||
Location(self, 256+24, 0x1EA97, LocationType.Regular, "Kakariko Well - Right").Weighted(sphereOne),
|
||||
Location(self, 256+25, 0x1EA9A, LocationType.Regular, "Kakariko Well - Bottom").Weighted(sphereOne),
|
||||
Location(self, 256+26, 0x1EB0F, LocationType.Regular, "Blind's Hideout - Top").Weighted(sphereOne),
|
||||
Location(self, 256+27, 0x1EB18, LocationType.Regular, "Blind's Hideout - Far Left").Weighted(sphereOne),
|
||||
Location(self, 256+28, 0x1EB12, LocationType.Regular, "Blind's Hideout - Left").Weighted(sphereOne),
|
||||
Location(self, 256+29, 0x1EB15, LocationType.Regular, "Blind's Hideout - Right").Weighted(sphereOne),
|
||||
Location(self, 256+30, 0x1EB1B, LocationType.Regular, "Blind's Hideout - Far Right").Weighted(sphereOne),
|
||||
Location(self, 256+31, 0x5EB18, LocationType.Regular, "Bottle Merchant").Weighted(sphereOne),
|
||||
Location(self, 256+250, 0x1E9E9, LocationType.Regular, "Chicken House").Weighted(sphereOne),
|
||||
Location(self, 256+33, 0x6B9CF, LocationType.Regular, "Sick Kid",
|
||||
lambda items: items.Bottle),
|
||||
Location(self, 256+34, 0x1E9CE, LocationType.Regular, "Kakariko Tavern").Weighted(sphereOne),
|
||||
Location(self, 256+35, 0x308015, LocationType.Regular, "Magic Bat",
|
||||
lambda items: items.Powder and (items.Hammer or items.MoonPearl and items.Mirror and items.CanLiftHeavy()))
|
||||
]
|
|
@ -0,0 +1,45 @@
|
|||
from worlds.smz3.TotalSMZ3.Region import Z3Region
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
|
||||
class South(Z3Region):
|
||||
Name = "Light World South"
|
||||
Area = "Light World"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
sphereOne = -10
|
||||
self.Locations = [
|
||||
Location(self, 256+45, 0x308142, LocationType.Regular, "Maze Race").Weighted(sphereOne),
|
||||
Location(self, 256+240, 0x308012, LocationType.Regular, "Library",
|
||||
lambda items: items.Boots),
|
||||
Location(self, 256+241, 0x30814A, LocationType.Regular, "Flute Spot",
|
||||
lambda items: items.Shovel),
|
||||
Location(self, 256+242, 0x308003, LocationType.Regular, "South of Grove",
|
||||
lambda items: items.Mirror and self.world.CanEnter("Dark World South", items)),
|
||||
Location(self, 256+243, 0x1E9BC, LocationType.Regular, "Link's House").Weighted(sphereOne),
|
||||
Location(self, 256+244, 0x1E9F2, LocationType.Regular, "Aginah's Cave").Weighted(sphereOne),
|
||||
Location(self, 256+51, 0x1EB42, LocationType.Regular, "Mini Moldorm Cave - Far Left").Weighted(sphereOne),
|
||||
Location(self, 256+52, 0x1EB45, LocationType.Regular, "Mini Moldorm Cave - Left").Weighted(sphereOne),
|
||||
Location(self, 256+53, 0x308010, LocationType.Regular, "Mini Moldorm Cave - NPC").Weighted(sphereOne),
|
||||
Location(self, 256+54, 0x1EB48, LocationType.Regular, "Mini Moldorm Cave - Right").Weighted(sphereOne),
|
||||
Location(self, 256+251, 0x1EB4B, LocationType.Regular, "Mini Moldorm Cave - Far Right").Weighted(sphereOne),
|
||||
Location(self, 256+252, 0x308143, LocationType.Regular, "Desert Ledge",
|
||||
lambda items: self.world.CanEnter("Desert Palace", items)),
|
||||
Location(self, 256+253, 0x308005, LocationType.Regular, "Checkerboard Cave",
|
||||
lambda items: items.Mirror and (
|
||||
items.Flute and items.CanLiftHeavy() or
|
||||
items.CanAccessMiseryMirePortal(Config)
|
||||
) and items.CanLiftLight()),
|
||||
Location(self, 256+58, 0x308017, LocationType.Bombos, "Bombos Tablet",
|
||||
lambda items: items.Book and items.MasterSword and items.Mirror and self.world.CanEnter("Dark World South", items)),
|
||||
Location(self, 256+59, 0x1E98C, LocationType.Regular, "Floodgate Chest").Weighted(sphereOne),
|
||||
Location(self, 256+60, 0x308145, LocationType.Regular, "Sunken Treasure").Weighted(sphereOne),
|
||||
Location(self, 256+61, 0x308144, LocationType.Regular, "Lake Hylia Island",
|
||||
lambda items: items.Flippers and items.MoonPearl and items.Mirror and (
|
||||
self.world.CanEnter("Dark World South", items) or
|
||||
self.world.CanEnter("Dark World North East", items))),
|
||||
Location(self, 256+62, 0x6BE7D, LocationType.Regular, "Hobo",
|
||||
lambda items: items.Flippers),
|
||||
Location(self, 256+63, 0x1EB4E, LocationType.Regular, "Ice Rod Cave").Weighted(sphereOne)
|
||||
]
|
|
@ -0,0 +1,43 @@
|
|||
from typing import List
|
||||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType, IReward, IMedallionAccess
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression, ItemType
|
||||
|
||||
class MiseryMire(Z3Region, IReward, IMedallionAccess):
|
||||
Name = "Misery Mire"
|
||||
Area = "Misery Mire"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.RegionItems = [ ItemType.KeyMM, ItemType.BigKeyMM, ItemType.MapMM, ItemType.CompassMM]
|
||||
self.Reward = RewardType.Null
|
||||
self.Medallion = ItemType.Nothing
|
||||
self.Locations = [
|
||||
Location(self, 256+169, 0x1EA5E, LocationType.Regular, "Misery Mire - Main Lobby",
|
||||
lambda items: items.BigKeyMM or items.KeyMM >= 1),
|
||||
Location(self, 256+170, 0x1EA6A, LocationType.Regular, "Misery Mire - Map Chest",
|
||||
lambda items: items.BigKeyMM or items.KeyMM >= 1),
|
||||
Location(self, 256+171, 0x1EA61, LocationType.Regular, "Misery Mire - Bridge Chest"),
|
||||
Location(self, 256+172, 0x1E9DA, LocationType.Regular, "Misery Mire - Spike Chest"),
|
||||
Location(self, 256+173, 0x1EA64, LocationType.Regular, "Misery Mire - Compass Chest",
|
||||
lambda items: items.CanLightTorches() and
|
||||
items.KeyMM >= (2 if self.GetLocation("Misery Mire - Big Key Chest").ItemIs(ItemType.BigKeyMM, self.world) else 3)),
|
||||
Location(self, 256+174, 0x1EA6D, LocationType.Regular, "Misery Mire - Big Key Chest",
|
||||
lambda items: items.CanLightTorches() and
|
||||
items.KeyMM >= (2 if self.GetLocation("Misery Mire - Compass Chest").ItemIs(ItemType.BigKeyMM, self.world) else 3)),
|
||||
Location(self, 256+175, 0x1EA67, LocationType.Regular, "Misery Mire - Big Chest",
|
||||
lambda items: items.BigKeyMM),
|
||||
Location(self, 256+176, 0x308158, LocationType.Regular, "Misery Mire - Vitreous",
|
||||
lambda items: items.BigKeyMM and items.Lamp and items.Somaria)
|
||||
]
|
||||
|
||||
# // Need "CanKillManyEnemies" if implementing swordless
|
||||
def CanEnter(self, items: Progression):
|
||||
return (items.Bombos if self.Medallion == ItemType.Bombos else (
|
||||
items.Ether if self.Medallion == ItemType.Ether else items.Quake)) and items.Sword and \
|
||||
items.MoonPearl and (items.Boots or items.Hookshot) and \
|
||||
self.world.CanEnter("Dark World Mire", items)
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.GetLocation("Misery Mire - Vitreous").Available(items)
|
|
@ -0,0 +1,54 @@
|
|||
from typing import List
|
||||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType, IReward
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression, ItemType
|
||||
|
||||
class PalaceOfDarkness(Z3Region, IReward):
|
||||
Name = "Palace of Darkness"
|
||||
Area = "Dark Palace"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.RegionItems = [ ItemType.KeyPD, ItemType.BigKeyPD, ItemType.MapPD, ItemType.CompassPD]
|
||||
self.Reward = RewardType.Null
|
||||
self.Locations = [
|
||||
Location(self, 256+121, 0x1EA5B, LocationType.Regular, "Palace of Darkness - Shooter Room"),
|
||||
Location(self, 256+122, 0x1EA37, LocationType.Regular, "Palace of Darkness - Big Key Chest",
|
||||
lambda items: items.KeyPD >= (1 if self.GetLocation("Palace of Darkness - Big Key Chest").ItemIs(ItemType.KeyPD, self.world) else
|
||||
6 if (items.Hammer and items.Bow and items.Lamp) or config.Keysanity else 5))
|
||||
.AlwaysAllow(lambda item, items: item.Is(ItemType.KeyPD, self.world) and items.KeyPD >= 5),
|
||||
Location(self, 256+123, 0x1EA49, LocationType.Regular, "Palace of Darkness - Stalfos Basement",
|
||||
lambda items: items.KeyPD >= 1 or items.Bow and items.Hammer),
|
||||
Location(self, 256+124, 0x1EA3D, LocationType.Regular, "Palace of Darkness - The Arena - Bridge",
|
||||
lambda items: items.KeyPD >= 1 or items.Bow and items.Hammer),
|
||||
Location(self, 256+125, 0x1EA3A, LocationType.Regular, "Palace of Darkness - The Arena - Ledge",
|
||||
lambda items: items.Bow),
|
||||
Location(self, 256+126, 0x1EA52, LocationType.Regular, "Palace of Darkness - Map Chest",
|
||||
lambda items: items.Bow),
|
||||
Location(self, 256+127, 0x1EA43, LocationType.Regular, "Palace of Darkness - Compass Chest",
|
||||
lambda items: items.KeyPD >= (4 if (items.Hammer and items.Bow and items.Lamp) or config.Keysanity else 3)),
|
||||
Location(self, 256+128, 0x1EA46, LocationType.Regular, "Palace of Darkness - Harmless Hellway",
|
||||
lambda items: items.KeyPD >= (4 if (items.Hammer and items.Bow and items.Lamp) or config.Keysanity else 3 if
|
||||
self.GetLocation("Palace of Darkness - Harmless Hellway").ItemIs(ItemType.KeyPD, self.world) else
|
||||
6 if (items.Hammer and items.Bow and items.Lamp) or config.Keysanity else 5))
|
||||
.AlwaysAllow(lambda item, items: item.Is(ItemType.KeyPD, self.world) and items.KeyPD >= 5),
|
||||
Location(self, 256+129, 0x1EA4C, LocationType.Regular, "Palace of Darkness - Dark Basement - Left",
|
||||
lambda items: items.Lamp and items.KeyPD >= (4 if (items.Hammer and items.Bow) or config.Keysanity else 3)),
|
||||
Location(self, 256+130, 0x1EA4F, LocationType.Regular, "Palace of Darkness - Dark Basement - Right",
|
||||
lambda items: items.Lamp and items.KeyPD >= (4 if (items.Hammer and items.Bow) or config.Keysanity else 3)),
|
||||
Location(self, 256+131, 0x1EA55, LocationType.Regular, "Palace of Darkness - Dark Maze - Top",
|
||||
lambda items: items.Lamp and items.KeyPD >= (6 if (items.Hammer and items.Bow) or config.Keysanity else 5)),
|
||||
Location(self, 256+132, 0x1EA58, LocationType.Regular, "Palace of Darkness - Dark Maze - Bottom",
|
||||
lambda items: items.Lamp and items.KeyPD >= (6 if(items.Hammer and items.Bow) or config.Keysanity else 5)),
|
||||
Location(self, 256+133, 0x1EA40, LocationType.Regular, "Palace of Darkness - Big Chest",
|
||||
lambda items: items.BigKeyPD and items.Lamp and items.KeyPD >= (6 if (items.Hammer and items.Bow) or config.Keysanity else 5)),
|
||||
Location(self, 256+134, 0x308153, LocationType.Regular, "Palace of Darkness - Helmasaur King",
|
||||
lambda items: items.Lamp and items.Hammer and items.Bow and items.BigKeyPD and items.KeyPD >= 6),
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.MoonPearl and self.world.CanEnter("Dark World North East", items)
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.GetLocation("Palace of Darkness - Helmasaur King").Available(items)
|
|
@ -0,0 +1,35 @@
|
|||
from typing import List
|
||||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType, IReward
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression, ItemType
|
||||
|
||||
class SkullWoods(Z3Region, IReward):
|
||||
Name = "Skull Woods"
|
||||
Area = "Skull Woods"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.RegionItems = [ ItemType.KeySW, ItemType.BigKeySW, ItemType.MapSW, ItemType.CompassSW]
|
||||
self.Reward = RewardType.Null
|
||||
self.Locations = [
|
||||
Location(self, 256+145, 0x1E9A1, LocationType.Regular, "Skull Woods - Pot Prison"),
|
||||
Location(self, 256+146, 0x1E992, LocationType.Regular, "Skull Woods - Compass Chest"),
|
||||
Location(self, 256+147, 0x1E998, LocationType.Regular, "Skull Woods - Big Chest",
|
||||
lambda items: items.BigKeySW)
|
||||
.AlwaysAllow(lambda item, items: item.Is(ItemType.BigKeySW, self.world)),
|
||||
Location(self, 256+148, 0x1E99B, LocationType.Regular, "Skull Woods - Map Chest"),
|
||||
Location(self, 256+149, 0x1E9C8, LocationType.Regular, "Skull Woods - Pinball Room")
|
||||
.Allow(lambda item, items: item.Is(ItemType.KeySW, self.world)),
|
||||
Location(self, 256+150, 0x1E99E, LocationType.Regular, "Skull Woods - Big Key Chest"),
|
||||
Location(self, 256+151, 0x1E9FE, LocationType.Regular, "Skull Woods - Bridge Room",
|
||||
lambda items: items.Firerod),
|
||||
Location(self, 256+152, 0x308155, LocationType.Regular, "Skull Woods - Mothula",
|
||||
lambda items: items.Firerod and items.Sword and items.KeySW >= 3),
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.MoonPearl and self.world.CanEnter("Dark World North West", items)
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.GetLocation("Skull Woods - Mothula").Available(items)
|
|
@ -0,0 +1,43 @@
|
|||
from typing import List
|
||||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType, IReward
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression, ItemType
|
||||
|
||||
class SwampPalace(Z3Region, IReward):
|
||||
Name = "Swamp Palace"
|
||||
Area = "Swamp Palace"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.RegionItems = [ ItemType.KeySP, ItemType.BigKeySP, ItemType.MapSP, ItemType.CompassSP]
|
||||
self.Reward = RewardType.Null
|
||||
self.Locations = [
|
||||
Location(self, 256+135, 0x1EA9D, LocationType.Regular, "Swamp Palace - Entrance")
|
||||
.Allow(lambda item, items: self.Config.Keysanity or item.Is(ItemType.KeySP, self.world)),
|
||||
Location(self, 256+136, 0x1E986, LocationType.Regular, "Swamp Palace - Map Chest",
|
||||
lambda items: items.KeySP),
|
||||
Location(self, 256+137, 0x1E989, LocationType.Regular, "Swamp Palace - Big Chest",
|
||||
lambda items: items.BigKeySP and items.KeySP and items.Hammer)
|
||||
.AlwaysAllow(lambda item, items: item.Is(ItemType.BigKeySP, self.world)),
|
||||
Location(self, 256+138, 0x1EAA0, LocationType.Regular, "Swamp Palace - Compass Chest",
|
||||
lambda items: items.KeySP and items.Hammer),
|
||||
Location(self, 256+139, 0x1EAA3, LocationType.Regular, "Swamp Palace - West Chest",
|
||||
lambda items: items.KeySP and items.Hammer),
|
||||
Location(self, 256+140, 0x1EAA6, LocationType.Regular, "Swamp Palace - Big Key Chest",
|
||||
lambda items: items.KeySP and items.Hammer),
|
||||
Location(self, 256+141, 0x1EAA9, LocationType.Regular, "Swamp Palace - Flooded Room - Left",
|
||||
lambda items: items.KeySP and items.Hammer and items.Hookshot),
|
||||
Location(self, 256+142, 0x1EAAC, LocationType.Regular, "Swamp Palace - Flooded Room - Right",
|
||||
lambda items: items.KeySP and items.Hammer and items.Hookshot),
|
||||
Location(self, 256+143, 0x1EAAF, LocationType.Regular, "Swamp Palace - Waterfall Room",
|
||||
lambda items: items.KeySP and items.Hammer and items.Hookshot),
|
||||
Location(self, 256+144, 0x308154, LocationType.Regular, "Swamp Palace - Arrghus",
|
||||
lambda items: items.KeySP and items.Hammer and items.Hookshot)
|
||||
]
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.MoonPearl and items.Mirror and items.Flippers and self.world.CanEnter("Dark World South", items)
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.GetLocation("Swamp Palace - Arrghus").Available(items)
|
|
@ -0,0 +1,39 @@
|
|||
from typing import List
|
||||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType, IReward
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression, ItemType
|
||||
|
||||
class ThievesTown(Z3Region, IReward):
|
||||
Name = "Thieves' Town"
|
||||
Area = "Thieves' Town"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.RegionItems = [ ItemType.KeyTT, ItemType.BigKeyTT, ItemType.MapTT, ItemType.CompassTT]
|
||||
self.Reward = RewardType.Null
|
||||
self.Locations = [
|
||||
Location(self, 256+153, 0x1EA01, LocationType.Regular, "Thieves' Town - Map Chest"),
|
||||
Location(self, 256+154, 0x1EA0A, LocationType.Regular, "Thieves' Town - Ambush Chest"),
|
||||
Location(self, 256+155, 0x1EA07, LocationType.Regular, "Thieves' Town - Compass Chest"),
|
||||
Location(self, 256+156, 0x1EA04, LocationType.Regular, "Thieves' Town - Big Key Chest"),
|
||||
Location(self, 256+157, 0x1EA0D, LocationType.Regular, "Thieves' Town - Attic",
|
||||
lambda items: items.BigKeyTT and items.KeyTT),
|
||||
Location(self, 256+158, 0x1EA13, LocationType.Regular, "Thieves' Town - Blind's Cell",
|
||||
lambda items: items.BigKeyTT),
|
||||
Location(self, 256+159, 0x1EA10, LocationType.Regular, "Thieves' Town - Big Chest",
|
||||
lambda items: items.BigKeyTT and items.Hammer and
|
||||
(self.GetLocation("Thieves' Town - Big Chest").ItemIs(ItemType.KeyTT, self.world) or items.KeyTT))
|
||||
.AlwaysAllow(lambda item, items: item.Is(ItemType.KeyTT, self.world) and items.Hammer),
|
||||
Location(self, 256+160, 0x308156, LocationType.Regular, "Thieves' Town - Blind",
|
||||
lambda items: items.BigKeyTT and items.KeyTT and self.CanBeatBoss(items)),
|
||||
]
|
||||
|
||||
def CanBeatBoss(self, items: Progression):
|
||||
return items.Sword or items.Hammer or items.Somaria or items.Byrna
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return items.MoonPearl and self.world.CanEnter("Dark World North West", items)
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.GetLocation("Thieves' Town - Blind").Available(items)
|
|
@ -0,0 +1,36 @@
|
|||
from typing import List
|
||||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType, IReward
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression, ItemType
|
||||
|
||||
class TowerOfHera(Z3Region, IReward):
|
||||
Name = "Tower of Hera"
|
||||
Area = "Tower of Hera"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.RegionItems = [ ItemType.KeyTH, ItemType.BigKeyTH, ItemType.MapTH, ItemType.CompassTH]
|
||||
self.Reward = RewardType.Null
|
||||
self.Locations = [
|
||||
Location(self, 256+115, 0x308162, LocationType.HeraStandingKey, "Tower of Hera - Basement Cage"),
|
||||
Location(self, 256+116, 0x1E9AD, LocationType.Regular, "Tower of Hera - Map Chest"),
|
||||
Location(self, 256+117, 0x1E9E6, LocationType.Regular, "Tower of Hera - Big Key Chest",
|
||||
lambda items: items.KeyTH and items.CanLightTorches())
|
||||
.AlwaysAllow(lambda item, items: item.Is(ItemType.KeyTH, self.world)),
|
||||
Location(self, 256+118, 0x1E9FB, LocationType.Regular, "Tower of Hera - Compass Chest",
|
||||
lambda items: items.BigKeyTH),
|
||||
Location(self, 256+119, 0x1E9F8, LocationType.Regular, "Tower of Hera - Big Chest",
|
||||
lambda items: items.BigKeyTH),
|
||||
Location(self, 256+120, 0x308152, LocationType.Regular, "Tower of Hera - Moldorm",
|
||||
lambda items: items.BigKeyTH and self.CanBeatBoss(items)),
|
||||
]
|
||||
|
||||
def CanBeatBoss(self, items: Progression):
|
||||
return items.Sword or items.Hammer
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return (items.Mirror or items.Hookshot and items.Hammer) and self.world.CanEnter("Light World Death Mountain West", items)
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.GetLocation("Tower of Hera - Moldorm").Available(items)
|
|
@ -0,0 +1,55 @@
|
|||
from typing import List
|
||||
from worlds.smz3.TotalSMZ3.Region import Z3Region, RewardType, IReward, IMedallionAccess
|
||||
from worlds.smz3.TotalSMZ3.Config import Config
|
||||
from worlds.smz3.TotalSMZ3.Location import Location, LocationType
|
||||
from worlds.smz3.TotalSMZ3.Item import Progression, ItemType
|
||||
|
||||
class TurtleRock(Z3Region, IReward, IMedallionAccess):
|
||||
Name = "Turtle Rock"
|
||||
Area = "Turtle Rock"
|
||||
|
||||
def __init__(self, world, config: Config):
|
||||
super().__init__(world, config)
|
||||
self.RegionItems = [ ItemType.KeyTR, ItemType.BigKeyTR, ItemType.MapTR, ItemType.CompassTR]
|
||||
self.Reward = RewardType.Null
|
||||
self.Medallion = ItemType.Nothing
|
||||
self.Locations = [
|
||||
Location(self, 256+177, 0x1EA22, LocationType.Regular, "Turtle Rock - Compass Chest"),
|
||||
Location(self, 256+178, 0x1EA1C, LocationType.Regular, "Turtle Rock - Roller Room - Left",
|
||||
lambda items: items.Firerod),
|
||||
Location(self, 256+179, 0x1EA1F, LocationType.Regular, "Turtle Rock - Roller Room - Right",
|
||||
lambda items: items.Firerod),
|
||||
Location(self, 256+180, 0x1EA16, LocationType.Regular, "Turtle Rock - Chain Chomps",
|
||||
lambda items: items.KeyTR >= 1),
|
||||
Location(self, 256+181, 0x1EA25, LocationType.Regular, "Turtle Rock - Big Key Chest",
|
||||
lambda items: items.KeyTR >=
|
||||
(2 if not self.Config.Keysanity or self.GetLocation("Turtle Rock - Big Key Chest").ItemIs(ItemType.BigKeyTR, self.world) else
|
||||
3 if self.GetLocation("Turtle Rock - Big Key Chest").ItemIs(ItemType.KeyTR, self.world) else 4))
|
||||
.AlwaysAllow(lambda item, items: item.Is(ItemType.KeyTR, self.world) and items.KeyTR >= 3),
|
||||
Location(self, 256+182, 0x1EA19, LocationType.Regular, "Turtle Rock - Big Chest",
|
||||
lambda items: items.BigKeyTR and items.KeyTR >= 2)
|
||||
.Allow(lambda item, items: item.IsNot(ItemType.BigKeyTR, self.world)),
|
||||
Location(self, 256+183, 0x1EA34, LocationType.Regular, "Turtle Rock - Crystaroller Room",
|
||||
lambda items: items.BigKeyTR and items.KeyTR >= 2),
|
||||
Location(self, 256+184, 0x1EA28, LocationType.Regular, "Turtle Rock - Eye Bridge - Top Right", self.LaserBridge),
|
||||
Location(self, 256+185, 0x1EA2B, LocationType.Regular, "Turtle Rock - Eye Bridge - Top Left", self.LaserBridge),
|
||||
Location(self, 256+186, 0x1EA2E, LocationType.Regular, "Turtle Rock - Eye Bridge - Bottom Right", self.LaserBridge),
|
||||
Location(self, 256+187, 0x1EA31, LocationType.Regular, "Turtle Rock - Eye Bridge - Bottom Left", self.LaserBridge),
|
||||
Location(self, 256+188, 0x308159, LocationType.Regular, "Turtle Rock - Trinexx",
|
||||
lambda items: items.BigKeyTR and items.KeyTR >= 4 and items.Lamp and self.CanBeatBoss(items)),
|
||||
]
|
||||
|
||||
def LaserBridge(self, items: Progression):
|
||||
return items.BigKeyTR and items.KeyTR >= 3 and items.Lamp and (items.Cape or items.Byrna or items.CanBlockLasers)
|
||||
|
||||
def CanBeatBoss(self, items: Progression):
|
||||
return items.Firerod and items.Icerod
|
||||
|
||||
def CanEnter(self, items: Progression):
|
||||
return (items.Bombos if self.Medallion == ItemType.Bombos else (
|
||||
items.Ether if self.Medallion == ItemType.Ether else items.Quake)) and items.Sword and \
|
||||
items.MoonPearl and items.CanLiftHeavy() and items.Hammer and items.Somaria and \
|
||||
self.world.CanEnter("Light World Death Mountain East", items)
|
||||
|
||||
def CanComplete(self, items: Progression):
|
||||
return self.GetLocation("Turtle Rock - Trinexx").Available(items)
|
|
@ -0,0 +1,323 @@
|
|||
import re
|
||||
|
||||
class Dialog:
|
||||
|
||||
command = re.compile(r"^\{[^}]*\}")
|
||||
invalid = re.compile(r"(?<!^)\{[^}]*\}(?!$)", re.MULTILINE)
|
||||
digit = re.compile(r"\d")
|
||||
uppercaseLetter = re.compile(r"[A-Z]")
|
||||
lowercaseLetter = re.compile(r"[a-z]")
|
||||
|
||||
@staticmethod
|
||||
def Simple(text: str):
|
||||
maxBytes = 256
|
||||
wrap = 19
|
||||
|
||||
bytes = []
|
||||
lines = text.split('\n')
|
||||
lineIndex = 0
|
||||
for line in lines:
|
||||
bytes.append(0x74 if 0 else 0x75 if 1 else 0x76)
|
||||
letters = line[:wrap] if len(line) > wrap else line
|
||||
for letter in letters:
|
||||
write = Dialog.LetterToBytes(letter)
|
||||
if (write[0] == 0xFD):
|
||||
bytes += write
|
||||
else:
|
||||
for b in write:
|
||||
bytes += [ 0x00, b ]
|
||||
|
||||
lineIndex += 1
|
||||
|
||||
if (lineIndex % 3 == 0 and lineIndex < len(lines)):
|
||||
bytes.append(0x7E)
|
||||
if (lineIndex >= 3 and lineIndex < len(lines)):
|
||||
bytes.append(0x73)
|
||||
|
||||
bytes.append(0x7F)
|
||||
if (len(bytes) > maxBytes):
|
||||
return bytes[:maxBytes - 1].append(0x7F)
|
||||
|
||||
return bytes
|
||||
|
||||
@staticmethod
|
||||
def Compiled(text: str, pause = True):
|
||||
maxBytes = 2046
|
||||
wrap = 19
|
||||
|
||||
if (Dialog.invalid.match(text)):
|
||||
raise Exception("Dialog commands must be placed on separate lines", text)
|
||||
|
||||
padOut = False
|
||||
|
||||
bytes = [ 0xFB ]
|
||||
lines = Dialog.Wordwrap(text, wrap)
|
||||
lineCount = len([l for l in lines if not Dialog.command.match(l)])
|
||||
lineIndex = 0
|
||||
for line in lines:
|
||||
match = Dialog.command.match(line)
|
||||
if (match is not None):
|
||||
if (match.string == "{NOTEXT}"):
|
||||
return [ 0xFB, 0xFE, 0x6E, 0x00, 0xFE, 0x6B, 0x04 ]
|
||||
if (match.string == "{INTRO}"):
|
||||
padOut = True
|
||||
|
||||
bytesMap = {
|
||||
"{SPEED0}" : [ 0xFC, 0x00 ],
|
||||
"{SPEED2}" : [ 0xFC, 0x02 ],
|
||||
"{SPEED6}" : [ 0xFC, 0x06 ],
|
||||
"{PAUSE1}" : [ 0xFE, 0x78, 0x01 ],
|
||||
"{PAUSE3}" : [ 0xFE, 0x78, 0x03 ],
|
||||
"{PAUSE5}" : [ 0xFE, 0x78, 0x05 ],
|
||||
"{PAUSE7}" : [ 0xFE, 0x78, 0x07 ],
|
||||
"{PAUSE9}" : [ 0xFE, 0x78, 0x09 ],
|
||||
"{INPUT}" : [ 0xFA ],
|
||||
"{CHOICE}" : [ 0xFE, 0x68 ],
|
||||
"{ITEMSELECT}" : [ 0xFE, 0x69 ],
|
||||
"{CHOICE2}" : [ 0xFE, 0x71 ],
|
||||
"{CHOICE3}" : [ 0xFE, 0x72 ],
|
||||
"{C:GREEN}" : [ 0xFE, 0x77, 0x07 ],
|
||||
"{C:YELLOW}" : [ 0xFE, 0x77, 0x02 ],
|
||||
"{HARP}" : [ 0xFE, 0x79, 0x2D ],
|
||||
"{MENU}" : [ 0xFE, 0x6D, 0x00 ],
|
||||
"{BOTTOM}" : [ 0xFE, 0x6D, 0x01 ],
|
||||
"{NOBORDER}" : [ 0xFE, 0x6B, 0x02 ],
|
||||
"{CHANGEPIC}" : [ 0xFE, 0x67, 0xFE, 0x67 ],
|
||||
"{CHANGEMUSIC}" : [ 0xFE, 0x67 ],
|
||||
"{INTRO}" : [ 0xFE, 0x6E, 0x00, 0xFE, 0x77, 0x07, 0xFC, 0x03, 0xFE, 0x6B, 0x02, 0xFE, 0x67 ],
|
||||
"{IBOX}" : [ 0xFE, 0x6B, 0x02, 0xFE, 0x77, 0x07, 0xFC, 0x03, 0xF7 ],
|
||||
}
|
||||
result = bytesMap.get(match.string, None)
|
||||
if (result is None):
|
||||
raise Exception(f"Dialog text contained unknown command {match.string}", text)
|
||||
else:
|
||||
bytes += result
|
||||
|
||||
if (len(bytes) > maxBytes):
|
||||
raise Exception("Command overflowed maximum byte length", text)
|
||||
|
||||
continue
|
||||
|
||||
if (lineIndex == 1):
|
||||
bytes.append(0xF8); #// row 2
|
||||
elif (lineIndex >= 3 and lineIndex < lineCount):
|
||||
bytes.append(0xF6); #// scroll
|
||||
elif (lineIndex >= 2):
|
||||
bytes.append(0xF9); #// row 3
|
||||
|
||||
#// The first box needs to fill the full width with spaces as the palette is loaded weird.
|
||||
letters = line + (" " * wrap) if padOut and lineIndex < 3 else line
|
||||
for letter in letters:
|
||||
bytes += Dialog.LetterToBytes(letter)
|
||||
|
||||
lineIndex += 1
|
||||
|
||||
if (pause and lineIndex % 3 == 0 and lineIndex < lineCount):
|
||||
bytes.append(0xFA) #// wait for input
|
||||
|
||||
return bytes[:maxBytes]
|
||||
|
||||
@staticmethod
|
||||
def Wordwrap(text: str, width: int):
|
||||
result = []
|
||||
for line in text.split('\n'):
|
||||
line = line.rstrip()
|
||||
if (len(line) <= width):
|
||||
result.append(line)
|
||||
else:
|
||||
words = line.split(' ')
|
||||
lines = [ "" ]
|
||||
for word in words:
|
||||
line = lines.pop()
|
||||
if (len(line) + len(word) <= width):
|
||||
line = f"{line}{word} "
|
||||
else:
|
||||
if (len(line) > 0):
|
||||
lines.append(line)
|
||||
line = word
|
||||
while (len(line) > width):
|
||||
lines.append(line[:width])
|
||||
line = line[width:]
|
||||
line = f"{line} "
|
||||
lines.append(line)
|
||||
#lines.reverse()
|
||||
result += [l.strip() for l in lines]
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def LetterToBytes(c: str):
|
||||
if Dialog.digit.match(c): return [(ord(c) - ord('0') + 0xA0) ]
|
||||
elif Dialog.uppercaseLetter.match(c): return [ (ord(c) - ord('A') + 0xAA) ]
|
||||
elif Dialog.lowercaseLetter.match(c): return [ (ord(c) - ord('a') + 0x30) ]
|
||||
else:
|
||||
value = Dialog.letters.get(c, None)
|
||||
return value if value else [ 0xFF ]
|
||||
|
||||
#region letter bytes lookup
|
||||
|
||||
letters = {
|
||||
' ' : [ 0x4F ],
|
||||
'?' : [ 0xC6 ],
|
||||
'!' : [ 0xC7 ],
|
||||
',' : [ 0xC8 ],
|
||||
'-' : [ 0xC9 ],
|
||||
'…' : [ 0xCC ],
|
||||
'.' : [ 0xCD ],
|
||||
'~' : [ 0xCE ],
|
||||
'~' : [ 0xCE ],
|
||||
'\'' : [ 0xD8 ],
|
||||
'’' : [ 0xD8 ],
|
||||
'"' : [ 0xD8 ],
|
||||
':' : [ 0x4A ],
|
||||
'@' : [ 0x4B ],
|
||||
'#' : [ 0x4C ],
|
||||
'¤' : [ 0x4D, 0x4E ], #// Morphing ball
|
||||
'_' : [ 0xFF ], #// Full width space
|
||||
'£' : [ 0xFE, 0x6A ], #// link's name compressed
|
||||
'>' : [ 0xD2, 0xD3 ], #// link face
|
||||
'%' : [ 0xDD ], #// Hylian Bird
|
||||
'^' : [ 0xDE ], #// Hylian Ankh
|
||||
'=' : [ 0xDF ], #// Hylian Wavy lines
|
||||
'↑' : [ 0xE0 ],
|
||||
'↓' : [ 0xE1 ],
|
||||
'→' : [ 0xE2 ],
|
||||
'←' : [ 0xE3 ],
|
||||
'≥' : [ 0xE4 ], #// cursor
|
||||
'¼' : [ 0xE5, 0xE7 ], #// 1/4 heart
|
||||
'½' : [ 0xE6, 0xE7 ], #// 1/2 heart
|
||||
'¾' : [ 0xE8, 0xE9 ], #// 3/4 heart
|
||||
'♥' : [ 0xEA, 0xEB ], #// full heart
|
||||
'ᚋ' : [ 0xFE, 0x6C, 0x00 ], #// var 0
|
||||
'ᚌ' : [ 0xFE, 0x6C, 0x01 ], #// var 1
|
||||
'ᚍ' : [ 0xFE, 0x6C, 0x02 ], #// var 2
|
||||
'ᚎ' : [ 0xFE, 0x6C, 0x03 ], #// var 3
|
||||
|
||||
'あ' : [ 0x00 ],
|
||||
'い' : [ 0x01 ],
|
||||
'う' : [ 0x02 ],
|
||||
'え' : [ 0x03 ],
|
||||
'お' : [ 0x04 ],
|
||||
'や' : [ 0x05 ],
|
||||
'ゆ' : [ 0x06 ],
|
||||
'よ' : [ 0x07 ],
|
||||
'か' : [ 0x08 ],
|
||||
'き' : [ 0x09 ],
|
||||
'く' : [ 0x0A ],
|
||||
'け' : [ 0x0B ],
|
||||
'こ' : [ 0x0C ],
|
||||
'わ' : [ 0x0D ],
|
||||
'を' : [ 0x0E ],
|
||||
'ん' : [ 0x0F ],
|
||||
'さ' : [ 0x10 ],
|
||||
'し' : [ 0x11 ],
|
||||
'す' : [ 0x12 ],
|
||||
'せ' : [ 0x13 ],
|
||||
'そ' : [ 0x14 ],
|
||||
'が' : [ 0x15 ],
|
||||
'ぎ' : [ 0x16 ],
|
||||
'ぐ' : [ 0x17 ],
|
||||
'た' : [ 0x18 ],
|
||||
'ち' : [ 0x19 ],
|
||||
'つ' : [ 0x1A ],
|
||||
'て' : [ 0x1B ],
|
||||
'と' : [ 0x1C ],
|
||||
'げ' : [ 0x1D ],
|
||||
'ご' : [ 0x1E ],
|
||||
'ざ' : [ 0x1F ],
|
||||
'な' : [ 0x20 ],
|
||||
'に' : [ 0x21 ],
|
||||
'ぬ' : [ 0x22 ],
|
||||
'ね' : [ 0x23 ],
|
||||
'の' : [ 0x24 ],
|
||||
'じ' : [ 0x25 ],
|
||||
'ず' : [ 0x26 ],
|
||||
'ぜ' : [ 0x27 ],
|
||||
'は' : [ 0x28 ],
|
||||
'ひ' : [ 0x29 ],
|
||||
'ふ' : [ 0x2A ],
|
||||
'へ' : [ 0x2B ],
|
||||
'ほ' : [ 0x2C ],
|
||||
'ぞ' : [ 0x2D ],
|
||||
'だ' : [ 0x2E ],
|
||||
'ぢ' : [ 0x2F ],
|
||||
|
||||
'ア' : [ 0x50 ],
|
||||
'イ' : [ 0x51 ],
|
||||
'ウ' : [ 0x52 ],
|
||||
'エ' : [ 0x53 ],
|
||||
'オ' : [ 0x54 ],
|
||||
'ヤ' : [ 0x55 ],
|
||||
'ユ' : [ 0x56 ],
|
||||
'ヨ' : [ 0x57 ],
|
||||
'カ' : [ 0x58 ],
|
||||
'キ' : [ 0x59 ],
|
||||
'ク' : [ 0x5A ],
|
||||
'ケ' : [ 0x5B ],
|
||||
'コ' : [ 0x5C ],
|
||||
'ワ' : [ 0x5D ],
|
||||
'ヲ' : [ 0x5E ],
|
||||
'ン' : [ 0x5F ],
|
||||
'サ' : [ 0x60 ],
|
||||
'シ' : [ 0x61 ],
|
||||
'ス' : [ 0x62 ],
|
||||
'セ' : [ 0x63 ],
|
||||
'ソ' : [ 0x64 ],
|
||||
'ガ' : [ 0x65 ],
|
||||
'ギ' : [ 0x66 ],
|
||||
'グ' : [ 0x67 ],
|
||||
'タ' : [ 0x68 ],
|
||||
'チ' : [ 0x69 ],
|
||||
'ツ' : [ 0x6A ],
|
||||
'テ' : [ 0x6B ],
|
||||
'ト' : [ 0x6C ],
|
||||
'ゲ' : [ 0x6D ],
|
||||
'ゴ' : [ 0x6E ],
|
||||
'ザ' : [ 0x6F ],
|
||||
'ナ' : [ 0x70 ],
|
||||
'ニ' : [ 0x71 ],
|
||||
'ヌ' : [ 0x72 ],
|
||||
'ネ' : [ 0x73 ],
|
||||
'ノ' : [ 0x74 ],
|
||||
'ジ' : [ 0x75 ],
|
||||
'ズ' : [ 0x76 ],
|
||||
'ゼ' : [ 0x77 ],
|
||||
'ハ' : [ 0x78 ],
|
||||
'ヒ' : [ 0x79 ],
|
||||
'フ' : [ 0x7A ],
|
||||
'ヘ' : [ 0x7B ],
|
||||
'ホ' : [ 0x7C ],
|
||||
'ゾ' : [ 0x7D ],
|
||||
'ダ' : [ 0x7E ],
|
||||
'マ' : [ 0x80 ],
|
||||
'ミ' : [ 0x81 ],
|
||||
'ム' : [ 0x82 ],
|
||||
'メ' : [ 0x83 ],
|
||||
'モ' : [ 0x84 ],
|
||||
'ヅ' : [ 0x85 ],
|
||||
'デ' : [ 0x86 ],
|
||||
'ド' : [ 0x87 ],
|
||||
'ラ' : [ 0x88 ],
|
||||
'リ' : [ 0x89 ],
|
||||
'ル' : [ 0x8A ],
|
||||
'レ' : [ 0x8B ],
|
||||
'ロ' : [ 0x8C ],
|
||||
'バ' : [ 0x8D ],
|
||||
'ビ' : [ 0x8E ],
|
||||
'ブ' : [ 0x8F ],
|
||||
'ベ' : [ 0x90 ],
|
||||
'ボ' : [ 0x91 ],
|
||||
'パ' : [ 0x92 ],
|
||||
'ピ' : [ 0x93 ],
|
||||
'プ' : [ 0x94 ],
|
||||
'ペ' : [ 0x95 ],
|
||||
'ポ' : [ 0x96 ],
|
||||
'ャ' : [ 0x97 ],
|
||||
'ュ' : [ 0x98 ],
|
||||
'ョ' : [ 0x99 ],
|
||||
'ッ' : [ 0x9A ],
|
||||
'ァ' : [ 0x9B ],
|
||||
'ィ' : [ 0x9C ],
|
||||
'ゥ' : [ 0x9D ],
|
||||
'ェ' : [ 0x9E ],
|
||||
'ォ' : [ 0x9F ],
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
I hate insect
|
||||
puns, they
|
||||
really bug me.
|
||||
---
|
||||
I haven't seen
|
||||
the eye doctor
|
||||
in years.
|
||||
---
|
||||
I don't see
|
||||
you having a
|
||||
bright future
|
||||
---
|
||||
Are you doing
|
||||
a blind run
|
||||
of this game?
|
||||
---
|
||||
Pizza joke? No
|
||||
I think it's a
|
||||
bit too cheesy
|
||||
---
|
||||
A novice skier
|
||||
often jumps to
|
||||
contusions.
|
||||
---
|
||||
The beach?
|
||||
I'm not shore
|
||||
I can make it.
|
||||
---
|
||||
Rental agents
|
||||
offer quarters
|
||||
for dollars.
|
||||
---
|
||||
I got my tires
|
||||
fixed for a
|
||||
flat rate.
|
||||
---
|
||||
New lightbulb
|
||||
invented?
|
||||
Enlighten me.
|
||||
---
|
||||
A baker's job
|
||||
is a piece of
|
||||
cake.
|
||||
---
|
||||
My optometrist
|
||||
said I have
|
||||
vision!
|
||||
---
|
||||
When you're a
|
||||
baker, don't
|
||||
loaf around
|
||||
---
|
||||
Mire requires
|
||||
ether quake,
|
||||
or bombos
|
||||
---
|
||||
Broken pencils
|
||||
are pointless.
|
||||
---
|
||||
The food they
|
||||
serve guards
|
||||
lasts sentries
|
||||
---
|
||||
Being crushed
|
||||
by big objects
|
||||
is depressing.
|
||||
---
|
||||
A tap dancer's
|
||||
routine runs
|
||||
hot and cold.
|
||||
---
|
||||
A weeknight
|
||||
is a tiny
|
||||
nobleman
|
||||
---
|
||||
The chimney
|
||||
sweep wore a
|
||||
soot and tye.
|
||||
---
|
||||
Gardeners like
|
||||
to spring into
|
||||
action.
|
||||
---
|
||||
Bad at nuclear
|
||||
physics. I Got
|
||||
no fission.
|
||||
---
|
|
@ -0,0 +1,189 @@
|
|||
Start your day
|
||||
smiling with a
|
||||
delicious
|
||||
wholegrain
|
||||
breakfast
|
||||
created for
|
||||
your
|
||||
incredible
|
||||
insides.
|
||||
---
|
||||
You drove
|
||||
away my other
|
||||
self, Agahnim
|
||||
two times…
|
||||
But, I won't
|
||||
give you the
|
||||
Triforce.
|
||||
I'll defeat
|
||||
you!
|
||||
---
|
||||
Impa says that
|
||||
the mark on
|
||||
your hand
|
||||
means that you
|
||||
are the hero
|
||||
chosen to
|
||||
awaken Zelda.
|
||||
your blood can
|
||||
resurrect me.
|
||||
---
|
||||
Don't stand,
|
||||
|
||||
don't stand so
|
||||
Don't stand so
|
||||
|
||||
close to me
|
||||
Don't stand so
|
||||
close to me
|
||||
back off buddy
|
||||
---
|
||||
So ya
|
||||
Thought ya
|
||||
Might like to
|
||||
go to the show
|
||||
To feel the
|
||||
warm thrill of
|
||||
confusion
|
||||
That space
|
||||
cadet glow.
|
||||
---
|
||||
Like other
|
||||
pulmonate land
|
||||
gastropods,
|
||||
the majority
|
||||
of land slugs
|
||||
have two pairs
|
||||
of 'feelers'
|
||||
or tentacles
|
||||
on their head.
|
||||
---
|
||||
If you were a
|
||||
burrito, what
|
||||
kind of a
|
||||
burrito would
|
||||
you be?
|
||||
Me, I fancy I
|
||||
would be a
|
||||
spicy barbacoa
|
||||
burrito.
|
||||
---
|
||||
I am your
|
||||
father's
|
||||
brother's
|
||||
nephew's
|
||||
cousin's
|
||||
former
|
||||
roommate. What
|
||||
does that make
|
||||
us, you ask?
|
||||
---
|
||||
I'll be more
|
||||
eager about
|
||||
encouraging
|
||||
thinking
|
||||
outside the
|
||||
box when there
|
||||
is evidence of
|
||||
any thinking
|
||||
inside it.
|
||||
---
|
||||
If we're not
|
||||
meant to have
|
||||
midnight
|
||||
snacks, then
|
||||
why is there
|
||||
a light in the
|
||||
fridge?
|
||||
|
||||
---
|
||||
I feel like we
|
||||
keep ending up
|
||||
here.
|
||||
|
||||
Don't you?
|
||||
|
||||
It's like
|
||||
deja vu
|
||||
all over again
|
||||
---
|
||||
Did you know?
|
||||
The biggest
|
||||
and heaviest
|
||||
cheese ever
|
||||
produced
|
||||
weighed
|
||||
57,518 pounds
|
||||
and was 32
|
||||
feet long.
|
||||
---
|
||||
Now there was
|
||||
a time, When
|
||||
you loved me
|
||||
so. I couldn't
|
||||
do wrong,
|
||||
And now you
|
||||
need to know.
|
||||
So How you
|
||||
like me now?
|
||||
---
|
||||
Did you know?
|
||||
Nutrition
|
||||
experts
|
||||
recommend that
|
||||
at least half
|
||||
of our daily
|
||||
grains come
|
||||
from whole
|
||||
grain products
|
||||
---
|
||||
The Hemiptera
|
||||
or true bugs
|
||||
are an order
|
||||
of insects
|
||||
covering 50k
|
||||
to 80k species
|
||||
like aphids,
|
||||
cicadas, and
|
||||
shield bugs.
|
||||
---
|
||||
Thanks for
|
||||
dropping in,
|
||||
the first
|
||||
passengers
|
||||
in a hot
|
||||
air balloon.
|
||||
were a duck,
|
||||
a sheep,
|
||||
and a rooster.
|
||||
---
|
||||
You think you
|
||||
are so smart?
|
||||
|
||||
I bet you
|
||||
didn't know
|
||||
You can't hum
|
||||
while holding
|
||||
your nose
|
||||
closed.
|
||||
---
|
||||
Grumble,
|
||||
|
||||
grumble…
|
||||
grumble,
|
||||
|
||||
grumble…
|
||||
Seriously you
|
||||
were supposed
|
||||
to bring food
|
||||
---
|
||||
Join me hero,
|
||||
and I shall
|
||||
make your face
|
||||
the greatest
|
||||
in the dark
|
||||
world!
|
||||
|
||||
Or else you
|
||||
will die!
|
||||
---
|
|
@ -0,0 +1,385 @@
|
|||
SahasrahlaReveal: |-
|
||||
Want something
|
||||
for free? Go
|
||||
earn the green
|
||||
pendant in
|
||||
<dungeon>
|
||||
and I'll give
|
||||
you something.
|
||||
|
||||
BombShopReveal: |-
|
||||
Bring me the
|
||||
crystals from
|
||||
<first>
|
||||
and
|
||||
<second>
|
||||
so I can make
|
||||
a big bomb!
|
||||
|
||||
GanonSilversReveal:
|
||||
single:
|
||||
local: |-
|
||||
Did you find
|
||||
the arrows in
|
||||
my tower?
|
||||
remote: |-
|
||||
Did you find
|
||||
the arrows in
|
||||
<region>
|
||||
multi:
|
||||
local: |-
|
||||
Seek the
|
||||
arrows in
|
||||
this world
|
||||
remote: |-
|
||||
Seek the sage
|
||||
<player>
|
||||
for the arrows
|
||||
|
||||
Items:
|
||||
|
||||
BigKeyEP: |-
|
||||
The big key
|
||||
of the east
|
||||
BigKeyDP: |-
|
||||
Sand spills
|
||||
out of this
|
||||
big key
|
||||
BigKeyTH: |-
|
||||
The big key
|
||||
to moldorm's
|
||||
heart
|
||||
BigKeyPD: |-
|
||||
Hammeryump
|
||||
with this
|
||||
big key
|
||||
BigKeySP: |-
|
||||
The big key
|
||||
to the swamp
|
||||
BigKeySW: |-
|
||||
The big key
|
||||
of the dark
|
||||
forest
|
||||
BigKeyTT: |-
|
||||
The big key
|
||||
of rogues
|
||||
BigKeyIP: |-
|
||||
A frozen
|
||||
big key
|
||||
rests here
|
||||
BigKeyMM: |-
|
||||
The big key
|
||||
to Vitreous
|
||||
BigKeyTR: |-
|
||||
The big key
|
||||
of terrapins
|
||||
BigKeyGT: |-
|
||||
The big key
|
||||
of evil's bane
|
||||
|
||||
KeyHC: |-
|
||||
The key to
|
||||
the castle
|
||||
KeyCT: |-
|
||||
Agahanim
|
||||
halfway
|
||||
unlocked
|
||||
KeyDP: |-
|
||||
Sand spills
|
||||
out of this
|
||||
small key
|
||||
KeyTH: |-
|
||||
The key
|
||||
to moldorm's
|
||||
basement
|
||||
KeyPD: |-
|
||||
A small key
|
||||
that steals
|
||||
light
|
||||
KeySP: |-
|
||||
Access to
|
||||
the swamp
|
||||
is granted
|
||||
KeySW: |-
|
||||
The small key
|
||||
of the dark
|
||||
forest
|
||||
KeyTT: |-
|
||||
The small key
|
||||
of rogues
|
||||
KeyIP: |-
|
||||
A frozen
|
||||
small key
|
||||
rests here
|
||||
KeyMM: |-
|
||||
The small key
|
||||
to Vitreous
|
||||
KeyTR: |-
|
||||
The small key
|
||||
of terrapins
|
||||
KeyGT: |-
|
||||
The small key
|
||||
of evil's bane
|
||||
|
||||
Map: |-
|
||||
You can now
|
||||
find your way
|
||||
home!
|
||||
Compass: |-
|
||||
Now you know
|
||||
where the boss
|
||||
hides!
|
||||
|
||||
ProgressiveTunic: |-
|
||||
Time for a
|
||||
change of
|
||||
clothes?
|
||||
ProgressiveShield: |-
|
||||
Have a better
|
||||
defense in
|
||||
front of you
|
||||
ProgressiveSword: |-
|
||||
A better copy
|
||||
of your sword
|
||||
for your time
|
||||
Bow: |-
|
||||
You have
|
||||
chosen the
|
||||
archer class.
|
||||
SilverArrows: |-
|
||||
Do you fancy
|
||||
silver tipped
|
||||
arrows?
|
||||
BlueBoomerang: |-
|
||||
No matter what
|
||||
you do, blue
|
||||
returns to you
|
||||
RedBoomerang: |-
|
||||
No matter what
|
||||
you do, red
|
||||
returns to you
|
||||
Hookshot: |-
|
||||
BOING!!!
|
||||
BOING!!!
|
||||
BOING!!!
|
||||
Mushroom: |-
|
||||
I'm a fun guy!
|
||||
|
||||
I'm a funghi!
|
||||
Powder: |-
|
||||
You can turn
|
||||
anti-faeries
|
||||
into faeries
|
||||
Firerod: |-
|
||||
I'm the hot
|
||||
rod. I make
|
||||
things burn!
|
||||
Icerod: |-
|
||||
I'm the cold
|
||||
rod. I make
|
||||
things freeze!
|
||||
Bombos: |-
|
||||
Burn, baby,
|
||||
burn! Fear my
|
||||
ring of fire!
|
||||
Ether: |-
|
||||
This magic
|
||||
coin freezes
|
||||
everything!
|
||||
Quake: |-
|
||||
Maxing out the
|
||||
Richter scale
|
||||
is what I do!
|
||||
Lamp: |-
|
||||
Baby, baby,
|
||||
baby.
|
||||
Light my way!
|
||||
Hammer: |-
|
||||
Stop!
|
||||
Hammer time!
|
||||
Shovel: |-
|
||||
Can
|
||||
You
|
||||
Dig it?
|
||||
Flute: |-
|
||||
Save the duck
|
||||
and fly to
|
||||
freedom!
|
||||
Bugnet: |-
|
||||
Let's catch
|
||||
some bees and
|
||||
faeries!
|
||||
Book: |-
|
||||
This is a
|
||||
paradox?!
|
||||
Bottle: |-
|
||||
Now you can
|
||||
store potions
|
||||
and stuff!
|
||||
BottleWithRedPotion: |-
|
||||
You see red
|
||||
goo in a
|
||||
bottle?
|
||||
BottleWithGreenPotion: |-
|
||||
You see green
|
||||
goo in a
|
||||
bottle?
|
||||
BottleWithBluePotion: |-
|
||||
You see blue
|
||||
goo in a
|
||||
bottle?
|
||||
BottleWithBee: |-
|
||||
Release me
|
||||
so I can go
|
||||
bzzzzz!
|
||||
BottleWithFairy: |-
|
||||
If you die
|
||||
I will revive
|
||||
you!
|
||||
Somaria: |-
|
||||
I make blocks
|
||||
to hold down
|
||||
switches!
|
||||
Byrna: |-
|
||||
Use this to
|
||||
become
|
||||
invincible!
|
||||
Cape: |-
|
||||
Wear this to
|
||||
become
|
||||
invisible!
|
||||
Mirror: |-
|
||||
Isn't your
|
||||
reflection so
|
||||
pretty?
|
||||
Boots: |-
|
||||
Gotta go fast!
|
||||
ProgressiveGlove: |-
|
||||
A way to lift
|
||||
heavier things
|
||||
Flippers: |-
|
||||
Fancy a swim?
|
||||
MoonPearl: |-2
|
||||
Bunny Link
|
||||
be
|
||||
gone!
|
||||
HalfMagic: |-
|
||||
Your magic
|
||||
power has been
|
||||
doubled!
|
||||
HeartPiece: |-
|
||||
Just a little
|
||||
piece of love!
|
||||
HeartContainer: |-
|
||||
Maximum health
|
||||
increased!
|
||||
Yeah!
|
||||
ThreeBombs: |-
|
||||
I make things
|
||||
go triple
|
||||
BOOM!!!
|
||||
Arrow: |-
|
||||
A lonely arrow
|
||||
sits here.
|
||||
TenArrows: |-
|
||||
This will give
|
||||
you ten shots
|
||||
with your bow!
|
||||
PocketRupees: |-
|
||||
Just pocket
|
||||
change. Move
|
||||
right along.
|
||||
CouchRupees: |-
|
||||
Just couch
|
||||
cash. Move
|
||||
right along.
|
||||
OneHundredRupees: |-
|
||||
A rupee stash!
|
||||
Hell yeah!
|
||||
ThreeHundredRupees: |-
|
||||
A rupee hoard!
|
||||
Hell yeah!
|
||||
BombUpgrade: |-
|
||||
Increase bomb
|
||||
storage, low
|
||||
low price
|
||||
ArrowUpgrade: |-
|
||||
Increase arrow
|
||||
storage, low
|
||||
low price
|
||||
|
||||
Missile: |-
|
||||
Some kind of
|
||||
flying bomb?
|
||||
Super: |-
|
||||
A really big
|
||||
flying bomb!
|
||||
PowerBomb: |-
|
||||
Big bada boom!
|
||||
Grapple: |-
|
||||
Some kind of
|
||||
futuristic
|
||||
hookshot?
|
||||
XRay: |-
|
||||
THIS LENS OF
|
||||
TRUTH IS MADE
|
||||
IN ZEBES!
|
||||
ETank: |-
|
||||
A heart from
|
||||
the future?
|
||||
ReserveTank: |-
|
||||
A fairy from
|
||||
the future?
|
||||
Charge: |-
|
||||
IM'A CHARGIN
|
||||
MA LAZER!
|
||||
Ice: |-
|
||||
Some kind of
|
||||
ice rod for
|
||||
aliens?
|
||||
Wave: |-
|
||||
Trigonometry gun.
|
||||
Spazer: |-
|
||||
Even space
|
||||
lasers can
|
||||
be sucky.
|
||||
Plasma: |-
|
||||
Some kind of
|
||||
fire rod for
|
||||
aliens?
|
||||
Varia: |-
|
||||
Alien armor?
|
||||
Gravity: |-
|
||||
No more water
|
||||
physics.
|
||||
Morph: |-
|
||||
Why can't
|
||||
Metroid crawl?
|
||||
Bombs: |-
|
||||
Bombs from
|
||||
the future.
|
||||
SpringBall: |-
|
||||
Bouncy bouncy
|
||||
bouncy bouncy
|
||||
bounce.
|
||||
ScrewAttack: |-
|
||||
U spin me right
|
||||
round baby
|
||||
right round
|
||||
HiJump: |-
|
||||
This would be
|
||||
great if I
|
||||
could jump.
|
||||
SpaceJump: |-
|
||||
I believe
|
||||
I can fly.
|
||||
SpeedBooster: |-
|
||||
THE GREEN
|
||||
BOOMERANG IS
|
||||
THE FASTEST!
|
||||
Keycard: |-
|
||||
A key from
|
||||
the future?
|
||||
default: |-
|
||||
Don't waste
|
||||
your time!
|
|
@ -0,0 +1,433 @@
|
|||
# Numbers in comments refer to US text numbers
|
||||
# Except for the first few entries, JP1.0 text numbers are smaller by 2
|
||||
|
||||
# The order of the dialog entries is significant
|
||||
- set_cursor: [0xFB, 0xFC, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xE4, 0xFE, 0x68]
|
||||
- set_cursor2: [0xFB, 0xFC, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xE4, 0xFE, 0x68]
|
||||
- game_over_menu: { NoPause: "{SPEED0}\nSave and Continue\nSave and Quit\nContinue" }
|
||||
- var_test: { NoPause: "0= ᚋ, 1= ᚌ\n2= ᚍ, 3= ᚎ" }
|
||||
- follower_no_enter: "Can't you take me some place nice."
|
||||
- choice_1_3: [0xFB, 0xFC, 0x00, 0xF7, 0xE4, 0xF8, 0xFF, 0xF9, 0xFF, 0xFE, 0x71]
|
||||
- choice_2_3: [0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xE4, 0xF9, 0xFF, 0xFE, 0x71]
|
||||
- choice_3_3: [0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xE4, 0xFE, 0x71]
|
||||
- choice_1_2: [0xFB, 0xFC, 0x00, 0xF7, 0xE4, 0xF8, 0xFF, 0xFE, 0x72]
|
||||
- choice_2_2: [0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xE4, 0xFE, 0x72]
|
||||
- uncle_leaving_text: "I'm just going out for a pack of smokes."
|
||||
- uncle_dying_sewer: "I've fallen and I can't get up, take this."
|
||||
# $10
|
||||
- tutorial_guard_1: "Only adults should travel at night."
|
||||
- tutorial_guard_2: "You can press X to see the Map."
|
||||
- tutorial_guard_3: "Press the A button to lift things by you."
|
||||
- tutorial_guard_4: "When you has a sword, press B to slash it."
|
||||
- tutorial_guard_5: "このメッセージはニホンゴでそのまま" # on purpose
|
||||
- tutorial_guard_6: "Are we really still reading these?"
|
||||
- tutorial_guard_7: "Jeeze! There really are a lot of things."
|
||||
- priest_sanctuary_before_leave: "Go be a hero!"
|
||||
- sanctuary_enter: "YAY!\nYou saved Zelda!"
|
||||
- zelda_sanctuary_story: "Do you want to hear me say this again?\n{HARP}\n ≥ No\n _Yes\n{CHOICE}"
|
||||
- priest_sanctuary_before_pendants: "Go'on and get them pendants so you can beat up Agahnim."
|
||||
- priest_sanctuary_after_pendants_before_master_sword: "Kudos! But seriously, you should be getting the master sword, not having a kegger in here."
|
||||
- priest_sanctuary_dying: "They took her to the castle! Take your sword and save her!"
|
||||
- zelda_save_sewers: "You saved me!"
|
||||
- priest_info: "So, I'm the dude that will protect Zelda. Don't worry, I got this covered."
|
||||
- zelda_sanctuary_before_leave: "Be careful!"
|
||||
- telepathic_intro: "{NOBORDER}\n{SPEED6}\nHey, come find me and help me!"
|
||||
# $20
|
||||
- telepathic_reminder: "{NOBORDER}\n{SPEED6}\nI'm in the castle basement."
|
||||
- zelda_go_to_throne: "Go north to the throne."
|
||||
- zelda_push_throne: "Let's push it from the left!"
|
||||
- zelda_switch_room_pull: "Pull this lever using A."
|
||||
- zelda_save_lets_go: "Let's get out of here!"
|
||||
- zelda_save_repeat: "I like talking, do you?\n ≥ No\n _Yes\n{CHOICE}"
|
||||
- zelda_before_pendants: "You need to find all the pendants…\n\n\nNumpty."
|
||||
- zelda_after_pendants_before_master_sword: "Very pretty pendants, but really you should be getting that sword in the forest!"
|
||||
- telepathic_zelda_right_after_master_sword: "{NOBORDER}\n{SPEED6}\nHi £,\nHave you been thinking about me?\narrrrrgghh…\n… … …"
|
||||
- zelda_sewers: "Just a little further to the Sanctuary."
|
||||
- zelda_switch_room: "The Sanctuary!\n\nPull my finger"
|
||||
- kakariko_sahasrahla_wife: "Heya, £!\nLong time no see.\nYou want a master sword?\n\nWell good luck with that."
|
||||
- kakariko_sahasrahla_wife_sword_story: "It occurs to me that I like toast and jam, but cheese and crackers is better.\nYou like?\n__≥ Cheese\n___ Jam\n{CHOICE}"
|
||||
- kakariko_sahasrahla_wife_closing: "Anywho, I have things to do. You see those 2 ovens?\n\nYeah, 2!\nWho has 2 ovens nowadays?!"
|
||||
- kakariko_sahasrahla_after_master_sword: "Cool sword!\n\n\n…\n\n\n…\n\n\nPlease save us"
|
||||
- kakariko_alert_guards: "GUARDS! HELP!\nThe creeper\n£ is here!"
|
||||
# $30
|
||||
- sahasrahla_quest_have_pendants: "{BOTTOM}\nCool beans, but I think you should mosey on over to the lost woods."
|
||||
- sahasrahla_quest_have_master_sword: "{BOTTOM}\nThat's a pretty sword, but I'm old, forgetful, and old. Why don't you go do all the hard work while I hang out in this hut."
|
||||
- sahasrahla_quest_information: "{BOTTOM}\nSahasrahla, I am. You would do well to find the 3 pendants from the 3 dungeons in the Light World.\nUnderstand?\n ≥ Yes\n No\n{CHOICE}"
|
||||
- sahasrahla_bring_courage: "{BOTTOM}\nWhile you're here, could you do me a solid and get the green pendant from that dungeon?\n{HARP}\nI'll give you a present if you do."
|
||||
- sahasrahla_have_ice_rod: "{BOTTOM}\nLike, I sit here, and tell you what to do?\n\n\nAlright, go and find all the maidens, there are, like, maybe 7 of them. I dunno anymore. I'm old."
|
||||
- telepathic_sahasrahla_beat_agahnim: "{NOBORDER}\n{SPEED6}\nNice, so you beat Agahnim. Now you must beat Ganon. Good Luck!"
|
||||
- telepathic_sahasrahla_beat_agahnim_no_pearl: "{NOBORDER}\n{SPEED6}\nOh, also you forgot the Moon Pearl, dingus. Go back and find it!"
|
||||
- sahasrahla_have_boots_no_icerod: "{BOTTOM}\nCave in South East has a cool item."
|
||||
- sahasrahla_have_courage: "{BOTTOM}\nLook, you have the green pendant! I'll give you something. Go kill the other two bosses for more pendant fun!"
|
||||
- sahasrahla_found: "{BOTTOM}\nYup!\n\nI'm the old man you are looking for. I'll keep it short and sweet: Go into that dungeon, then bring me the green pendant and talk to me again."
|
||||
- sign_rain_north_of_links_house: "↑ Dying Uncle\n This way…"
|
||||
- sign_north_of_links_house: "> Randomizer Don't read me, go beat Ganon!"
|
||||
- sign_path_to_death_mountain: "Cave to lost, old man.\nGood luck."
|
||||
- sign_lost_woods: "\n↑ Lost Woods"
|
||||
- sign_zoras: "Danger!\nDeep water!\nZoras!"
|
||||
- sign_outside_magic_shop: "Welcome to the Magic Shoppe"
|
||||
# $40
|
||||
- sign_death_mountain_cave_back: "Cave away from sky cabbages"
|
||||
- sign_east_of_links_house: "↓ Lake Hylia\n\n Also, a shop"
|
||||
- sign_south_of_lumberjacks: "← Kakariko\n Village"
|
||||
- sign_east_of_desert: "← Desert\n\n It's hot."
|
||||
- sign_east_of_sanctuary: "↑→ Potions!\n\nWish Waterfall"
|
||||
- sign_east_of_castle: "→ East Palace\n\n← Castle"
|
||||
- sign_north_of_lake: "\n Lake Hiriah"
|
||||
- sign_desert_thief: "Don't talk to me or touch my sign!"
|
||||
- sign_lumberjacks_house: "Lumberjacks, Inc.\nYou see 'em, we saw 'em."
|
||||
- sign_north_kakariko: "↓ Kakariko\n Village"
|
||||
- witch_bring_mushroom: "Double, double toil and trouble!\nBring me a mushroom!"
|
||||
- witch_brewing_the_item: "This mushroom is busy brewing. Come back later."
|
||||
- witch_assistant_no_bottle: "You got to give me the mushroom, Numpty."
|
||||
- witch_assistant_no_empty_bottle: "Gotta use your stuff before you can get more."
|
||||
- witch_assistant_informational: "Red is life\nGreen is magic\nBlue is both\nI'll heal you for free, though."
|
||||
- witch_assistant_no_bottle_buying: "If only you had something to put that in, like a bottle…"
|
||||
# $50
|
||||
- potion_shop_no_empty_bottles: "Whoa, bucko!\nNo empty bottles."
|
||||
- item_get_lamp: "Lamp! You can see in the dark, and light torches."
|
||||
- item_get_boomerang: "Boomerang! Press START to select it."
|
||||
- item_get_bow: "You're in bow mode now!"
|
||||
- item_get_shovel: "This is my new mop. My friend George, he gave me this mop. It's a pretty good mop. It's not as good as my old mop. I miss my old mop. But it's still a good mop."
|
||||
- item_get_magic_cape: "Finally! We get to play Invisible Man!"
|
||||
- item_get_powder: "It's the powder. Let's cause some mischief!"
|
||||
- item_get_flippers: "Splish! Splash! Let's go take a bath!"
|
||||
- item_get_power_gloves: "Feel the power! You can now lift light rocks! Rock on!"
|
||||
- item_get_pendant_courage: "We have the Pendant of Courage! How brave!"
|
||||
- item_get_pendant_power: "We have the Pendant of Power! How robust!"
|
||||
- item_get_pendant_wisdom: "We have the Pendant of Wisdom! How astute!"
|
||||
- item_get_mushroom: "A Mushroom! Don't eat it. Find a witch."
|
||||
- item_get_book: "It book! U R now litterit!"
|
||||
- item_get_moonpearl: "I found a shiny marble! No more hops!"
|
||||
- item_get_compass: "A compass! I can now find the boss."
|
||||
# $60
|
||||
- item_get_map: "Yo! You found a MAP! Press X to see it."
|
||||
- item_get_ice_rod: "It's the Ice Rod! Freeze Ray time."
|
||||
- item_get_fire_rod: "A Rod that shoots fire? Let's burn all the things!"
|
||||
- item_get_ether: "We can chill out with this!"
|
||||
- item_get_bombos: "Let's set everything on fire, and melt things!"
|
||||
- item_get_quake: "Time to make the earth shake, rattle, and roll!"
|
||||
- item_get_hammer: "STOP!\n\nHammer Time!"
|
||||
- item_get_ocarina: "Finally! We can play the Song of Time!"
|
||||
- item_get_cane_of_somaria: "Make blocks!\nThrow blocks!\nSplode Blocks!"
|
||||
- item_get_hookshot: "BOING!!!\nBOING!!!\nSay no more…"
|
||||
- item_get_bombs: "BOMBS! Use A to pick 'em up, throw 'em, get hurt!"
|
||||
- item_get_bottle: "It's a terrarium. I hope we find a lizard!"
|
||||
- item_get_big_key: "Yo! You got a Big Key!"
|
||||
- item_get_titans_mitts: "So, like, you can now lift anything.\nANYTHING!"
|
||||
- item_get_magic_mirror: "We could stare at this all day or, you know, beat Ganon…"
|
||||
- item_get_fake_mastersword: "It's the Master Sword! …or not…\n\n FOOL!"
|
||||
# $70
|
||||
- post_item_get_mastersword: "{NOBORDER}\n{SPEED6}\n£, you got the sword!\n{CHANGEMUSIC}\nNow let's go beat up Agahnim!"
|
||||
- item_get_red_potion: "Red goo to go! Nice!"
|
||||
- item_get_green_potion: "Green goo to go! Nice!"
|
||||
- item_get_blue_potion: "Blue goo to go! Nice!"
|
||||
- item_get_bug_net: "Surprise Net! Let's catch stuff!"
|
||||
- item_get_blue_mail: "Blue threads? Less damage activated!"
|
||||
- item_get_red_mail: "You feel the power of the eggplant on your head."
|
||||
- item_get_temperedsword: "Nice… I now have a craving for Cheetos."
|
||||
- item_get_mirror_shield: "Pit would be proud!"
|
||||
- item_get_cane_of_byrna: "It's the Blue Cane. You can now protect yourself with lag!"
|
||||
- missing_big_key: "Something is missing…\nThe Big Key?"
|
||||
- missing_magic: "Something is missing…\nMagic meter?"
|
||||
- item_get_pegasus_boots: "Finally, it's bonking time!\nHold A to dash"
|
||||
- talking_tree_info_start: "Whoa! I can talk again!"
|
||||
- talking_tree_info_1: "Yank on the pitchfork in the center of town, ya heard it here."
|
||||
- talking_tree_info_2: "Ganon is such a dingus, no one likes him, ya heard it here."
|
||||
# $80
|
||||
- talking_tree_info_3: "There is a portal near the Lost Woods, ya heard it here."
|
||||
- talking_tree_info_4: "Use bombs to quickly kill the Hinox, ya heard it here."
|
||||
- talking_tree_other: "I can breathe!"
|
||||
- item_get_pendant_power_alt: "We have the Pendant of Power! How robust!"
|
||||
- item_get_pendant_wisdom_alt: "We have the Pendant of Wisdom! How astute!"
|
||||
- game_shooting_choice: "20 rupees.\n5 arrows.\nWin rupees!\nWant to play?\n ≥ Yes\n _No\n{CHOICE}"
|
||||
- game_shooting_yes: "Let's do this!"
|
||||
- game_shooting_no: "Where are you going? Straight up!"
|
||||
- game_shooting_continue: "Keep playing?\n ≥ Yes\n _No\n{CHOICE}"
|
||||
- pond_of_wishing: "-Wishing Pond-\n\n On Vacation"
|
||||
- pond_item_select: "Pick something\nto throw in.\n{ITEMSELECT}"
|
||||
- pond_item_test: "You toss this?\n ≥ Yup\n _Wrong\n{CHOICE}"
|
||||
- pond_will_upgrade: "You're honest, so I'll give you a present."
|
||||
- pond_item_test_no: "You sure?\n ≥ Oh yeah\n _Um\n{CHOICE}"
|
||||
- pond_item_test_no_no: "Well, I don't want it, so take it back."
|
||||
- pond_item_boomerang: "I don't much like you, so have this worse Boomerang."
|
||||
# $90
|
||||
- pond_item_shield: "I grant you the ability to block fireballs. Don't lose this to a Pikit!"
|
||||
- pond_item_silvers: "So, wouldn't it be nice to kill Ganon? These should help in the final phase."
|
||||
- pond_item_bottle_filled: "Bottle Filled!\nMoney Saved!"
|
||||
- pond_item_sword: "Thank you for the sword, here is a stick of butter."
|
||||
- pond_of_wishing_happiness: "Happiness up!\nYou are now\nᚌᚋ happy!"
|
||||
- pond_of_wishing_choice: "Your wish?\n ≥More bombs\n _More arrows\n{CHOICE}"
|
||||
- pond_of_wishing_bombs: "Woo-hoo!\nYou can now\ncarry ᚌᚋ bombs"
|
||||
- pond_of_wishing_arrows: "Woo-hoo!\nYou can now\nhold ᚌᚋ arrows"
|
||||
- pond_of_wishing_full_upgrades: "You have all I can give you, here are your rupees back."
|
||||
- mountain_old_man_first: "Look out for holes, and monsters."
|
||||
- mountain_old_man_deadend: "Oh, goody, hearts in jars! This place is creepy."
|
||||
- mountain_old_man_turn_right: "Turn right. Let's get out of this place."
|
||||
- mountain_old_man_lost_and_alone: "Hello. I can't see anything. Take me with you."
|
||||
- mountain_old_man_drop_off: "Here's a thing to help you, good luck!"
|
||||
- mountain_old_man_in_his_cave_pre_agahnim: "You need to beat the tower at the top of the mountain."
|
||||
- mountain_old_man_in_his_cave: "You can find stuff in the tower at the top of this mountain.\nCome see me if you'd like to be healed."
|
||||
# $A0
|
||||
- mountain_old_man_in_his_cave_post_agahnim: "You should be heading to the castle… you have a portal there now.\nSay hi anytime you like."
|
||||
- tavern_old_man_awake: "Life? Love? Happiness? The question you should really ask is: Was this generated by Stoops Alu or Stoops Jet?"
|
||||
- tavern_old_man_unactivated_flute: "You should play that flute for the weathervane, cause reasons."
|
||||
- tavern_old_man_know_tree_unactivated_flute: "You should play that flute for the weathervane, cause reasons."
|
||||
- tavern_old_man_have_flute: "Life? Love? Happiness? The question you should really ask is: Was this generated by Stoops Alu or Stoops Jet?"
|
||||
- chicken_hut_lady: "This is\nChristos' hut.\n\nHe's out, searching for a bow."
|
||||
- running_man: "Hi, Do you\nknow Veetorp?\n\nYou really\nshould. And\nall the other great guys who made this possible.\nGo thank them.\n\n\nIf you can catch them…"
|
||||
- game_race_sign: "Why are you reading this sign? Run!!!"
|
||||
- sign_bumper_cave: "You need Cape, but not Hookshot"
|
||||
- sign_catfish: "Toss rocks\nToss items\nToss cookies"
|
||||
- sign_north_village_of_outcasts: "↑ Skull Woods\n\n↓ Steve's Town"
|
||||
- sign_south_of_bumper_cave: "\n→ Karkats cave"
|
||||
- sign_east_of_pyramid: "\n→ Dark Palace"
|
||||
- sign_east_of_bomb_shop: "\n← Bomb Shoppe"
|
||||
- sign_east_of_mire: "\n← Misery Mire\n no way in.\n no way out."
|
||||
- sign_village_of_outcasts: "Have a Trulie Awesome Day!"
|
||||
# $B0
|
||||
- sign_before_wishing_pond: "waterfall\nup ahead\nmake wishes"
|
||||
- sign_before_catfish_area: "→↑ Have you met Woeful Ike?"
|
||||
- castle_wall_guard: "Looking for a Princess? Look downstairs."
|
||||
- gate_guard: "No Lonks Allowed!"
|
||||
- telepathic_tile_eastern_palace: "{NOBORDER}\nYou need a Bow to get past the red Eyegore. derpy"
|
||||
- telepathic_tile_tower_of_hera_floor_4: "{NOBORDER}\nIf you find a shiny ball, you can be you in the Dark World."
|
||||
- hylian_text_1: "%== %== %==\n ^ %==% ^\n%== ^%%^ ==^"
|
||||
- mastersword_pedestal_translated: "A test of strength: If you have 3 pendants, I'm yours."
|
||||
- telepathic_tile_spectacle_rock: "{NOBORDER}\nUse the Mirror, or the Hookshot and Hammer, to get to Tower of Hera!"
|
||||
- telepathic_tile_swamp_entrance: "{NOBORDER}\nDrain the floodgate to raise the water here!"
|
||||
- telepathic_tile_thieves_town_upstairs: "{NOBORDER}\nBlind hates bright light."
|
||||
- telepathic_tile_misery_mire: "{NOBORDER}\nLighting 4 torches will open your way forward!"
|
||||
- hylian_text_2: "%%^= %==%\n ^ =%^=\n==%= ^^%^"
|
||||
- desert_entry_translated: "Kneel before this stone, and magic will move around you."
|
||||
- telepathic_tile_under_ganon: "{NOBORDER}\nOnly Silver Arrows will finish off a blue Ganon, or really well timed spins in phase 4."
|
||||
- telepathic_tile_palace_of_darkness: "{NOBORDER}\nThis is a funny looking Enemizer"
|
||||
# $C0
|
||||
- telepathic_tile_desert_bonk_torch_room: "{NOBORDER}\nThings can be knocked down, if you fancy yourself a dashing dude."
|
||||
- telepathic_tile_castle_tower: "{NOBORDER}\nYou can reflect Agahnim's energy with Sword, Bug-net or Hammer."
|
||||
- telepathic_tile_ice_large_room: "{NOBORDER}\nAll right, stop collaborate and listen\nIce is back with my brand new invention."
|
||||
- telepathic_tile_turtle_rock: "{NOBORDER}\nYou Shall not pass… without the red cane."
|
||||
- telepathic_tile_ice_entrace: "{NOBORDER}\nYou can use Fire Rod or Bombos to pass."
|
||||
- telepathic_tile_ice_stalfos_knights_room: "{NOBORDER}\nKnock 'em down and then bomb them dead."
|
||||
- telepathic_tile_tower_of_hera_entrance: "{NOBORDER}\nThis is a bad place, with a guy who will make you fall…\n\n\na lot."
|
||||
- houlihan_room: "My name is Chris Houlihan, but I'm sure you only care about the money. Take it, it's not like I can stop you."
|
||||
- caught_a_bee: "Caught a Bee\n ≥ Keep\n _Release\n{CHOICE}"
|
||||
- caught_a_fairy: "Caught Fairy!\n ≥ Keep\n _Release\n{CHOICE}"
|
||||
- no_empty_bottles: "Whoa, bucko!\nNo empty bottles."
|
||||
- game_race_boy_time: "Your time was\nᚎᚍ min ᚌᚋ sec."
|
||||
- game_race_girl: "You have 15 seconds,\nGo… Go… Go…"
|
||||
- game_race_boy_success: "Nice!\nYou can have this trash!"
|
||||
- game_race_boy_failure: "Too slow!\nI keep my\nprecious!"
|
||||
- game_race_boy_already_won: "You already have your prize, dingus!"
|
||||
# $D0
|
||||
- game_race_boy_sneaky: "Thought you could sneak in, eh?"
|
||||
- bottle_vendor_choice: "I gots bottles.\nYous gots 100 rupees?\n ≥ I want\n _No way!"
|
||||
- bottle_vendor_get: "Nice! Hold it up son! Show the world what you got!"
|
||||
- bottle_vendor_no: "Fine! I didn't want your money anyway."
|
||||
- bottle_vendor_already_collected: "Dude! You already have it."
|
||||
- bottle_vendor_bee: "Cool! A bee! Here's 100 rupees."
|
||||
- bottle_vendor_fish: "Whoa! A fish! You walked this all the way here?"
|
||||
- hobo_item_get_bottle: "You think life is rough? I guess you can take my last item. Except this tent. That's MY tent!"
|
||||
- blacksmiths_what_you_want: "Nice of you to come back!\nWould you like us mess with your sword?\n ≥ Temper\n _It's fine\n{CHOICE}"
|
||||
- blacksmiths_paywall: "It's 10 rupees\n ≥ Easy\n _Hang on…\n{CHOICE}"
|
||||
- blacksmiths_extra_okay: "Are you sure you're sure?\n ≥ Ah, yup\n _Hang on…\n{CHOICE}"
|
||||
- blacksmiths_tempered_already: "Whelp… We can't make this any better."
|
||||
- blacksmiths_temper_no: "Oh, come by any time!"
|
||||
- blacksmiths_bogart_sword: "We're going to have to take it to work on it."
|
||||
- blacksmiths_get_sword: "Sword is done. Now, back to our bread!"
|
||||
- blacksmiths_shop_before_saving: "I lost my friend. Help me find him!"
|
||||
# $E0
|
||||
- blacksmiths_shop_saving: "You found him! Colour me happy! Come back right away and we will bang on your sword."
|
||||
- blacksmiths_collect_frog: "Ribbit! Ribbit! Let's find my partner. To the shop!"
|
||||
- blacksmiths_still_working: "Something this precious takes time… Come back later."
|
||||
- blacksmiths_saving_bows: "Thanks!\n\nThanks!"
|
||||
- blacksmiths_hammer_anvil: "Dernt Take Er Jerbs!"
|
||||
- dark_flute_boy_storytime: "Hi!\nI'm Stumpy!\nI've been chillin' in this world for a while now, but I miss my flute. If I gave you a shovel, would you go digging for it?\n ≥ Sure\n _Nahh\n{CHOICE}"
|
||||
- dark_flute_boy_get_shovel: "Schaweet! Here you go. Happy digging!"
|
||||
- dark_flute_boy_no_get_shovel: "Oh I see, not good enough for you… FINE!"
|
||||
- dark_flute_boy_flute_not_found: "Still haven't found the item? Dig in the Light World around here, dingus!"
|
||||
- dark_flute_boy_after_shovel_get: "So I gave you an item, and you're still here.\n\n\n\n\n\nI mean, we can sit here and stare at each other, if you like…\n\n\n\n\n\n\n\nFine, I guess you should just go."
|
||||
- shop_fortune_teller_lw_hint_0: "{BOTTOM}\nBy the black cats, the book opens the desert"
|
||||
- shop_fortune_teller_lw_hint_1: "{BOTTOM}\nBy the black cats, nothing doing"
|
||||
- shop_fortune_teller_lw_hint_2: "{BOTTOM}\nBy the black cats, I'm cheap"
|
||||
- shop_fortune_teller_lw_hint_3: "{BOTTOM}\nBy the black cats, am I cheap?"
|
||||
- shop_fortune_teller_lw_hint_4: "{BOTTOM}\nBy the black cats, Zora lives at the end of the river"
|
||||
- shop_fortune_teller_lw_hint_5: "{BOTTOM}\nBy the black cats, The Cape can pass through the barrier"
|
||||
# $F0
|
||||
- shop_fortune_teller_lw_hint_6: "{BOTTOM}\nBy the black cats, Spin, Hammer, or Net to hurt Agahnim"
|
||||
- shop_fortune_teller_lw_hint_7: "{BOTTOM}\nBy the black cats, You can jump in the well by the blacksmiths"
|
||||
- shop_fortune_teller_lw_no_rupees: "{BOTTOM}\nThe black cats are hungry, come back with rupees"
|
||||
- shop_fortune_teller_lw: "{BOTTOM}\nWelcome to the Fortune Shoppe!\nFancy a read?\n ≥I must know\n _Negative\n{CHOICE}"
|
||||
- shop_fortune_teller_lw_post_hint: "{BOTTOM}\nFor ᚋᚌ rupees\nIt is done.\nBe gone!"
|
||||
- shop_fortune_teller_lw_no: "{BOTTOM}\nWell then, why did you even come in here?"
|
||||
- shop_fortune_teller_lw_hint_8: "{BOTTOM}\nBy the black cats, why you do?"
|
||||
- shop_fortune_teller_lw_hint_9: "{BOTTOM}\nBy the black cats, panda crackers"
|
||||
- shop_fortune_teller_lw_hint_10: "{BOTTOM}\nBy the black cats, the missing blacksmith is south of the Village of Outcasts"
|
||||
- shop_fortune_teller_lw_hint_11: "{BOTTOM}\nBy the black cats, open chests to get stuff"
|
||||
- shop_fortune_teller_lw_hint_12: "{BOTTOM}\nBy the black cats, you can buy a new bomb at the Bomb Shoppe"
|
||||
- shop_fortune_teller_lw_hint_13: "{BOTTOM}\nBy the black cats, big bombs blow up cracked walls in pyramids"
|
||||
- shop_fortune_teller_lw_hint_14: "{BOTTOM}\nBy the black cats, you need all the crystals to open Ganon's Tower"
|
||||
- shop_fortune_teller_lw_hint_15: "{BOTTOM}\nBy the black cats, Silver Arrows will defeat Ganon in his final phase"
|
||||
- dark_sanctuary: "For 20 rupees I'll tell you something?\nHow about it?\n ≥ Yes\n _No\n{CHOICE}"
|
||||
- dark_sanctuary_hint_0: "I once was a tea kettle, but then I moved up in the world, and now you can see me as this. Makes you wonder. What I could be next time."
|
||||
# $100
|
||||
- dark_sanctuary_no: "Then go away!"
|
||||
- dark_sanctuary_hint_1: "There is a thief in the desert, he can open creepy chests that follow you. But now that we have that out of the way, do you like my hair? I've spent eons getting it this way."
|
||||
- dark_sanctuary_yes: "With Crystals 5&6, you can find a great fairy in the pyramid.\n\nFlomp Flomp, Whizzle Whomp"
|
||||
- dark_sanctuary_hint_2: "All I can say is that my life is pretty plain,\nI like watchin' the puddles gather rain,\nAnd all I can do is just pour some tea for two,\nAnd speak my point of view but it's not sane,\nIt's not sane"
|
||||
- sick_kid_no_bottle: "{BOTTOM}\nI'm sick! Show me a bottle, get something!"
|
||||
- sick_kid_trade: "{BOTTOM}\nCool Bottle! Here's something for you."
|
||||
- sick_kid_post_trade: "{BOTTOM}\nLeave me alone\nI'm sick. You have my item."
|
||||
- desert_thief_sitting: "………………………"
|
||||
- desert_thief_following: "why……………"
|
||||
- desert_thief_question: "I was a thief. I open purple chests!\nKeep secret?\n ≥ Sure thing\n Never!\n{CHOICE}"
|
||||
- desert_thief_question_yes: "Cool, bring me any purple chests you find."
|
||||
- desert_thief_after_item_get: "You tell anyone and I will give you such a pinch!"
|
||||
- desert_thief_reassure: "Bring chests. It's a secret to everyone."
|
||||
- hylian_text_3: "^^ ^%=^= =%=\n=%% =%%=^\n==%^= %=^^%"
|
||||
- tablet_ether_book: "Can you make things fall out of the sky? With the Master Sword, you can!"
|
||||
- tablet_bombos_book: "Can you make things fall out of the sky? With the Master Sword, you can!"
|
||||
# $110
|
||||
- magic_bat_wake: "You bum! I was sleeping! Where's my magic bolts?"
|
||||
- magic_bat_give_half_magic: "How you like me now?"
|
||||
- intro_main: { NoPause: "{INTRO}\n Episode III\n{PAUSE3}\n A Link to\n the Past\n{PAUSE3}\n Randomizer\n{PAUSE3}\nAfter mostly disregarding what happened in the first two games.\n{PAUSE3}\nLink awakens to his uncle leaving the house.\n{PAUSE3}\nHe just runs out the door,\n{PAUSE3}\ninto the rainy night.\n{PAUSE3}\n{CHANGEPIC}\nGanon has moved around all the items in Hyrule.\n{PAUSE7}\nYou will have to find all the items necessary to beat Ganon.\n{PAUSE7}\nThis is your chance to be a hero.\n{PAUSE3}\n{CHANGEPIC}\nYou must get the 7 crystals to beat Ganon.\n{PAUSE9}\n{CHANGEPIC}" }
|
||||
- intro_throne_room: { NoPause: "{IBOX}\nLook at this Stalfos on the throne." }
|
||||
- intro_zelda_cell: { NoPause: "{IBOX}\nIt is your time to shine!" }
|
||||
- intro_agahnim: { NoPause: "{IBOX}\nAlso, you need to defeat this guy!" }
|
||||
- pickup_purple_chest: "A curious box. Let's take it with us!"
|
||||
- bomb_shop: "30 bombs for 100 rupees. Good deals all day!"
|
||||
- bomb_shop_big_bomb: "30 bombs for 100 rupees, 100 rupees 1 BIG bomb. Good deals all day!"
|
||||
- bomb_shop_big_bomb_buy: "Thanks!\nBoom goes the dynamite!"
|
||||
- item_get_big_bomb: "YAY! press A to splode it!"
|
||||
- kiki_second_extortion: "For 100 more, I'll open this place.\nHow about it?\n ≥ Open\n _Nah\n{CHOICE}"
|
||||
- kiki_second_extortion_no: "Heh, good luck getting in."
|
||||
- kiki_second_extortion_yes: "Yay! Rupees!\nOkay, let's do this!"
|
||||
- kiki_first_extortion: "I'm Kiki. I like rupees, may I have 10?\nHow about it?\n ≥ Yes\n _No\n{CHOICE}"
|
||||
- kiki_first_extortion_yes: "Nice. I'll tag along with you for a bit."
|
||||
# $120
|
||||
- kiki_first_extortion_no: "Pfft. I have no reason to hang. See ya!"
|
||||
- kiki_leaving_screen: "No no no no no! We should play by my rules! Goodbye…"
|
||||
- blind_in_the_cell: "You saved me!\nPlease get me out of here!"
|
||||
- blind_by_the_light: "Aaaahhhh~!\nS-so bright~!"
|
||||
- blind_not_that_way: "No! Don't go that way!"
|
||||
- aginah_l1sword_no_book: "I once had a fish dinner. I still remember it to this day."
|
||||
- aginah_l1sword_with_pendants: "Do you remember when I was young?\n\nI sure don't."
|
||||
- aginah: "So, I've been living in this cave for years, and you think you can just come along and bomb open walls?"
|
||||
- aginah_need_better_sword: "Once, I farted in this cave so bad all the jazz hands guys ran away and hid in the sand."
|
||||
- aginah_have_better_sword: "Pandas are very vicious animals. Never forget…\n\n\n\n\nI never will…"
|
||||
- catfish: "You woke me from my nap! Take this, and get out!"
|
||||
- catfish_after_item: "I don't have anything else for you!\nTake this!"
|
||||
- lumberjack_right: "One of us always lies."
|
||||
- lumberjack_left: "One of us always tells the truth."
|
||||
- lumberjack_left_post_agahnim: "One of us likes peanut butter."
|
||||
- fighting_brothers_right: "I walled off my brother Leo\n\nWhat a dingus."
|
||||
# $130
|
||||
- fighting_brothers_right_opened: "Now I should probably talk to him…"
|
||||
- fighting_brothers_left: "Did you come from my brothers room?\n\nAre we cool?"
|
||||
- maiden_crystal_1: "{SPEED2}\n{BOTTOM}\n{NOBORDER}\nI have a pretty red dress.\n{SPEED2}\nJust thought I would tell you."
|
||||
- maiden_crystal_2: "{SPEED2}\n{BOTTOM}\n{NOBORDER}\nI have a pretty blue dress.\n{SPEED2}\nJust thought I would tell you."
|
||||
- maiden_crystal_3: "{SPEED2}\n{BOTTOM}\n{NOBORDER}\nI have a pretty gold dress.\n{SPEED2}\nJust thought I would tell you."
|
||||
- maiden_crystal_4: "{SPEED2}\n{BOTTOM}\n{NOBORDER}\nI have a pretty redder dress.\n{SPEED2}\nJust thought I would tell you."
|
||||
- maiden_crystal_5: "{SPEED2}\n{BOTTOM}\n{NOBORDER}\nI have a pretty green dress.\n{SPEED2}\nJust thought I would tell you."
|
||||
- maiden_crystal_6: "{SPEED2}\n{BOTTOM}\n{NOBORDER}\nI have a pretty green dress.\n{SPEED2}\nJust thought I would tell you."
|
||||
- maiden_crystal_7: "{SPEED2}\n{BOTTOM}\n{NOBORDER}\nIt's about friggin time.\n{SPEED2}\nDo you know how long I've been waiting?!"
|
||||
- maiden_ending: "May the way of the hero lead to the Triforce"
|
||||
- maiden_confirm_undersood: "{SPEED2}\n{BOTTOM}\n{NOBORDER}\nCapisce?\n ≥ Yes\n _No\n{CHOICE}"
|
||||
- barrier_breaking: "What did the seven crystals say to Ganon's Tower?"
|
||||
- maiden_crystal_7_again: "{SPEED2}\n{BOTTOM}\n{NOBORDER}\nIt's about friggin time.\n{SPEED2}\nDo you know how long I've been waiting?!"
|
||||
- agahnim_zelda_teleport: "I am a magician, and this is my act. Watch as I make this girl disappear"
|
||||
- agahnim_magic_running_away: "And now, the end is near\nAnd so I face the final curtain\nMy friend, I'll say it clear\nI'll state my case, of which I'm certain\nI've lived a life that's full\nI've traveled each and every highway\nBut more, much more than this\nI did it my way"
|
||||
- agahnim_hide_and_seek_found: "Peek-a-boo!"
|
||||
# $140
|
||||
- agahnim_defeated: "Arrrgggghhh. Well you're coming with me!"
|
||||
- agahnim_final_meeting: "You have done well to come this far. Now, die!"
|
||||
# $142
|
||||
- zora_meeting: "What do you want?\n ≥ Flippers\n _Nothin'\n{CHOICE}"
|
||||
- zora_tells_cost: "Fine! But they aren't cheap. You got 500 rupees?\n ≥ Duh\n _Oh carp\n{CHOICE}"
|
||||
- zora_get_flippers: "Here's some Flippers for you! Swim little fish, swim."
|
||||
- zora_no_cash: "Fine!\nGo get some more money first."
|
||||
- zora_no_buy_item: "Wah hoo! Well, whenever you want to see these gills, stop on by."
|
||||
- kakariko_sahasrahla_grandson: "My grandpa is over in the East. I'm bad with directions. I'll mark your map. Best of luck!\n{HARP}"
|
||||
- kakariko_sahasrahla_grandson_next: "Someday I'll be in a high school band!"
|
||||
- dark_palace_tree_dude: "Did you know…\n\n\nA tree typically has many secondary branches supported clear of the ground by the trunk. This trunk typically contains woody tissue for strength, and vascular tissue to carry materials from one part of the tree to another."
|
||||
- fairy_wishing_ponds: "\n-Wishing pond-\n\nThrow item in?\n ≥ Yesh\n _No\n{CHOICE}"
|
||||
- fairy_wishing_ponds_no: "\n Stop it!"
|
||||
- pond_of_wishing_no: "\n Fine then!"
|
||||
- pond_of_wishing_return_item: "Okay. Here's your item back, cause I can't use it. I'm stuck in this fountain."
|
||||
- pond_of_wishing_throw: "How many?\n ≥ᚌᚋ rupees\n _ᚎᚍ rupees\n{CHOICE}"
|
||||
- pond_pre_item_silvers: "I like you, so here's a thing you can use to beat up Ganon."
|
||||
# $150
|
||||
- pond_of_wishing_great_luck: "\n is great luck"
|
||||
- pond_of_wishing_good_luck: "\n is good luck"
|
||||
- pond_of_wishing_meh_luck: "\n is meh luck"
|
||||
# Repurposed to no items in Randomizer
|
||||
- pond_of_wishing_bad_luck: "Why you come in here and pretend like you have something this fountain wants? Come back with bottles!"
|
||||
- pond_of_wishing_fortune: "by the way, your fortune,"
|
||||
- item_get_14_heart: "3 more to go\n ¼\nYay!"
|
||||
- item_get_24_heart: "2 more to go\n ½\nWhee!"
|
||||
- item_get_34_heart: "1 more to go\n ¾\nGood job!"
|
||||
- item_get_whole_heart: "You got a whole ♥!!\nGo you!"
|
||||
- item_get_sanc_heart: "You got a whole ♥!\nGo you!"
|
||||
- fairy_fountain_refill: "Well done, lettuce have a cup of tea…"
|
||||
- death_mountain_bullied_no_pearl: "I wrote a word. Just one. On a stone and threw it into the ocean. It was my word. It was what would save me. I hope someday someone finds that word and brings it to me. The word is the beginning of my song."
|
||||
- death_mountain_bullied_with_pearl: "I wrote a song. Just one. On a guitar and threw it into the sky. It was my song. It could tame beasts and free minds. It flitters on the wind and lurks in our minds. It is the song of nature, of humanity, of dreams and dreamers."
|
||||
- death_mountain_bully_no_pearl: "Add garlic, ginger and apple and cook for 2 minutes. Add carrots, potatoes, garam masala and curry powder and stir well. Add tomato paste, stir well and slowly add red wine and bring to a boil. Add sugar, soy sauce and water, stir and bring to a boil again."
|
||||
- death_mountain_bully_with_pearl: "I think I forgot how to smile…"
|
||||
- shop_darkworld_enter: "It's dangerous outside, buy my crap for safety."
|
||||
# $160
|
||||
- game_chest_village_of_outcasts: "Pay 30 rupees, open 2 chests. Are you lucky?\nSo, Play game?\n ≥ Play\n _Never!\n{CHOICE}"
|
||||
- game_chest_no_cash: "So, like, you need 30 rupees.\nSilly!"
|
||||
- game_chest_not_played: "You want to play a game?\nTalk to me."
|
||||
- game_chest_played: "You've opened the chests!\nTime to go."
|
||||
- game_chest_village_of_outcasts_play: "Alright, brother!\nGo play!"
|
||||
- shop_first_time: "Welcome to my shop! Select stuff with A.\nDO IT NOW!"
|
||||
- shop_already_have: "So, like, you already have one of those."
|
||||
- shop_buy_shield: "Thanks! Now you can block fire balls."
|
||||
- shop_buy_red_potion: "Red goo, so good! It's like a fairy in a bottle, except you have to activate it yourself."
|
||||
- shop_buy_arrows: "Arrows! Cause you were too lazy to look under some pots!"
|
||||
- shop_buy_bombs: "You bought bombs. What, couldn't find any under bushes?"
|
||||
- shop_buy_bee: "He's my best friend. Please take care of him, and never lose him."
|
||||
- shop_buy_heart: "You really just bought this?"
|
||||
- shop_first_no_bottle_buy: "Why does no one own bottles? Go find one first!"
|
||||
- shop_buy_no_space: "You are carrying to much crap, go use some of it first!"
|
||||
- ganon_fall_in: "You drove\naway my other\nself, Agahnim,\ntwo times…\nBut, I won't\ngive you the\nTriforce.\nI'll defeat\nyou!"
|
||||
# $170
|
||||
- ganon_phase_3: "Can you beat\nmy darkness\ntechnique?"
|
||||
- lost_woods_thief: "Have you seen Andy?\n\nHe was out looking for our prized Ether medallion.\nI wonder when he will be back?"
|
||||
- blinds_hut_dude: "I'm just some dude. This is Blind's hut."
|
||||
- end_triforce: "{SPEED2}\n{MENU}\n{NOBORDER}\n G G"
|
||||
# $174
|
||||
- toppi_fallen: "Ouch!\n\nYou Jerk!"
|
||||
- kakariko_tavern_fisherman: "Don't argue\nwith a frozen\nDeadrock.\nHe'll never\nchange his\nposition!"
|
||||
- thief_money: "It's a secret to everyone."
|
||||
- thief_desert_rupee_cave: "So you, like, busted down my door, and are being a jerk by talking to me? Normally I would be angry and make you pay for it, but I bet you're just going to break all my pots and steal my 50 rupees."
|
||||
- thief_ice_rupee_cave: "I'm a rupee pot farmer. One day I will take over the world with my skillz. Have you met my brother in the desert? He's way richer than I am."
|
||||
- telepathic_tile_south_east_darkworld_cave: "~~ Dev cave ~~\n No farming\n required"
|
||||
# $17A
|
||||
- cukeman: "Did you hear that Veetorp beat ajneb174 in a 1 on 1 race at AGDQ?"
|
||||
- cukeman_2: "You found Shabadoo, huh?\nNiiiiice."
|
||||
- potion_shop_no_cash: "Yo! I'm not running a charity here."
|
||||
- kakariko_powdered_chicken: "Smallhacker…\n\n\nWas hiding, you found me!\n\n\nOkay, you can leave now."
|
||||
- game_chest_south_of_kakariko: "Pay 20 rupees, open 1 chest. Are you lucky?\nSo, Play game?\n ≥ Play\n _Never!\n{CHOICE}"
|
||||
- game_chest_play_yes: "Good luck then"
|
||||
# $180
|
||||
- game_chest_play_no: "Well fine, I didn't want your rupees."
|
||||
- game_chest_lost_woods: "Pay 100 rupees, open 1 chest. Are you lucky?\nSo, Play game?\n ≥ Play\n Never!\n{CHOICE}"
|
||||
- kakariko_flophouse_man_no_flippers: "I sure do have a lot of beds.\n\nZora is a cheapskate and will try to sell you his trash for 500 rupees…"
|
||||
- kakariko_flophouse_man: "I sure do have a lot of beds.\n\nDid you know if you played the flute in the center of town things could happen?"
|
||||
- menu_start_2: { NoPause: "{MENU}\n{SPEED0}\n≥£'s House\n_Sanctuary\n{CHOICE3}" }
|
||||
- menu_start_3: { NoPause: "{MENU}\n{SPEED0}\n≥£'s House\n_Sanctuary\n_Mountain Cave\n{CHOICE2}" }
|
||||
- menu_pause: { NoPause: "{SPEED0}\n≥Continue Game\n_Save and Quit\n{CHOICE3}" }
|
||||
- game_digging_choice: "Have 80 Rupees? Want to play digging game?\n ≥Yes\n _No\n{CHOICE}"
|
||||
- game_digging_start: "Okay, use the shovel with Y!"
|
||||
- game_digging_no_cash: "Shovel rental is 80 rupees.\nI have all day"
|
||||
- game_digging_end_time: "Time's up!\nTime for you to go."
|
||||
- game_digging_come_back_later: "Come back later, I have to bury things."
|
||||
- game_digging_no_follower: "Something is following you. I don't like."
|
||||
- menu_start_4: { NoPause: "{MENU}\n{SPEED0}\n≥£'s House\n_Mountain Cave\n{CHOICE3}" }
|
||||
- ganon_fall_in_alt: "You think you\nare ready to\nface me?\n\nI will not die\n\nunless you\ncomplete your\ngoals. Dingus!"
|
||||
- ganon_phase_3_alt: "Got wax in your ears? I cannot die!"
|
||||
# $190
|
||||
- sign_east_death_mountain_bridge: "How did you get up here?"
|
||||
- fish_money: "It's a secret to everyone."
|
||||
- end_pad_data: ""
|
|
@ -0,0 +1,354 @@
|
|||
What do you
|
||||
call a blind
|
||||
dinosaur?
|
||||
Adoyouthink-
|
||||
hesaurus
|
||||
|
||||
---
|
||||
A blind man
|
||||
walks into
|
||||
a bar.
|
||||
And a table.
|
||||
And a chair.
|
||||
|
||||
---
|
||||
What do ducks
|
||||
like to eat?
|
||||
|
||||
Quackers!
|
||||
|
||||
---
|
||||
How do you
|
||||
set up a party
|
||||
in space?
|
||||
|
||||
You planet!
|
||||
|
||||
---
|
||||
I'm glad I
|
||||
know sign
|
||||
language,
|
||||
it's pretty
|
||||
handy.
|
||||
|
||||
---
|
||||
What did Zelda
|
||||
say to Link at
|
||||
a secure door?
|
||||
|
||||
TRIFORCE!
|
||||
|
||||
---
|
||||
I am on a
|
||||
seafood diet.
|
||||
|
||||
Every time
|
||||
I see food,
|
||||
I eat it.
|
||||
---
|
||||
I've decided
|
||||
to sell my
|
||||
vacuum.
|
||||
It was just
|
||||
gathering
|
||||
dust.
|
||||
---
|
||||
Whats the best
|
||||
time to go to
|
||||
the dentist?
|
||||
|
||||
Tooth-hurtie!
|
||||
|
||||
---
|
||||
Why can't a
|
||||
bike stand on
|
||||
its own?
|
||||
|
||||
It's two-tired!
|
||||
|
||||
---
|
||||
If you haven't
|
||||
found Quake
|
||||
yet…
|
||||
it's not your
|
||||
fault.
|
||||
---
|
||||
Why is Peter
|
||||
Pan always
|
||||
flying?
|
||||
Because he
|
||||
Neverlands!
|
||||
---
|
||||
I once told a
|
||||
joke to Armos.
|
||||
|
||||
But he
|
||||
remained
|
||||
stone-faced!
|
||||
---
|
||||
Lanmola was
|
||||
late to our
|
||||
dinner party.
|
||||
He just came
|
||||
for the desert
|
||||
---
|
||||
Moldorm is
|
||||
such a
|
||||
prankster.
|
||||
And I fall for
|
||||
it every time!
|
||||
---
|
||||
Helmasaur is
|
||||
throwing a
|
||||
party.
|
||||
I hope it's
|
||||
a masquerade!
|
||||
---
|
||||
I'd like to
|
||||
know Arrghus
|
||||
better.
|
||||
But he won't
|
||||
come out of
|
||||
his shell!
|
||||
---
|
||||
Mothula didn't
|
||||
have much fun
|
||||
at the party.
|
||||
He's immune to
|
||||
spiked punch!
|
||||
---
|
||||
Don't set me
|
||||
up with that
|
||||
chick from
|
||||
Steve's Town.
|
||||
|
||||
|
||||
I'm not
|
||||
interested in
|
||||
a Blind date!
|
||||
---
|
||||
Kholdstare is
|
||||
afraid to go
|
||||
to the circus.
|
||||
Hungry kids
|
||||
thought he was
|
||||
cotton candy!
|
||||
---
|
||||
I asked who
|
||||
Vitreous' best
|
||||
friends are.
|
||||
He said,
|
||||
'Me, Myself,
|
||||
and Eye!'
|
||||
---
|
||||
Trinexx can be
|
||||
a hothead or
|
||||
he can be an
|
||||
ice guy. In
|
||||
the end, he's
|
||||
a solid
|
||||
individual!
|
||||
---
|
||||
Bari thought I
|
||||
had moved out
|
||||
of town.
|
||||
He was shocked
|
||||
to see me!
|
||||
---
|
||||
I can only get
|
||||
Weetabix
|
||||
around here.
|
||||
I have to go
|
||||
to Steve's
|
||||
Town for Count
|
||||
Chocula!
|
||||
---
|
||||
Don't argue
|
||||
with a frozen
|
||||
Deadrock.
|
||||
He'll never
|
||||
change his
|
||||
position!
|
||||
---
|
||||
I offered a
|
||||
drink to a
|
||||
self-loathing
|
||||
Ghini.
|
||||
He said he
|
||||
didn't like
|
||||
spirits!
|
||||
---
|
||||
I was supposed
|
||||
to meet Gibdo
|
||||
for lunch.
|
||||
But he got
|
||||
wrapped up in
|
||||
something!
|
||||
---
|
||||
Goriya sure
|
||||
has changed
|
||||
in this game.
|
||||
I hope he
|
||||
comes back
|
||||
around!
|
||||
---
|
||||
Hinox actually
|
||||
wants to be a
|
||||
lawyer.
|
||||
Too bad he
|
||||
bombed the
|
||||
Bar exam!
|
||||
---
|
||||
I'm surprised
|
||||
Moblin's tusks
|
||||
are so gross.
|
||||
He always has
|
||||
his Trident
|
||||
with him!
|
||||
---
|
||||
Don’t tell
|
||||
Stalfos I’m
|
||||
here.
|
||||
He has a bone
|
||||
to pick with
|
||||
me!
|
||||
---
|
||||
I got
|
||||
Wallmaster to
|
||||
help me move
|
||||
furniture.
|
||||
He was really
|
||||
handy!
|
||||
---
|
||||
Wizzrobe was
|
||||
just here.
|
||||
He always
|
||||
vanishes right
|
||||
before we get
|
||||
the check!
|
||||
---
|
||||
I shouldn't
|
||||
have picked up
|
||||
Zora's tab.
|
||||
That guy
|
||||
drinks like
|
||||
a fish!
|
||||
---
|
||||
I was sharing
|
||||
a drink with
|
||||
Poe.
|
||||
For no reason,
|
||||
he left in a
|
||||
heartbeat!
|
||||
---
|
||||
Don’t trust
|
||||
horsemen on
|
||||
Death Mountain
|
||||
They’re Lynel
|
||||
the time!
|
||||
---
|
||||
Today's
|
||||
special is
|
||||
battered bat.
|
||||
Got slapped
|
||||
for offering a
|
||||
lady a Keese!
|
||||
---
|
||||
Don’t walk
|
||||
under
|
||||
propellered
|
||||
pineapples.
|
||||
You may end up
|
||||
wearing
|
||||
a pee hat!
|
||||
---
|
||||
My girlfriend
|
||||
burrowed under
|
||||
the sand.
|
||||
So I decided
|
||||
to Leever!
|
||||
---
|
||||
Geldman wants
|
||||
to be a
|
||||
Broadway star.
|
||||
He’s always
|
||||
practicing
|
||||
Jazz Hands!
|
||||
---
|
||||
Octoballoon
|
||||
must be mad
|
||||
at me.
|
||||
He blows up
|
||||
at the sight
|
||||
of me!
|
||||
---
|
||||
Toppo is a
|
||||
total pothead.
|
||||
|
||||
He hates it
|
||||
when you take
|
||||
away his grass
|
||||
---
|
||||
I lost my
|
||||
shield by
|
||||
that house.
|
||||
Why did they
|
||||
put up a
|
||||
Pikit fence?!
|
||||
---
|
||||
Know that fox
|
||||
in Steve’s
|
||||
Town?
|
||||
He’ll Pikku
|
||||
pockets if you
|
||||
aren't careful
|
||||
---
|
||||
Dash through
|
||||
Dark World
|
||||
bushes.
|
||||
You’ll see
|
||||
Ganon is tryin
|
||||
to Stal you!
|
||||
---
|
||||
Eyegore!
|
||||
|
||||
You gore!
|
||||
We all gore
|
||||
those jerks
|
||||
with arrows!
|
||||
---
|
||||
I like my
|
||||
whiskey neat.
|
||||
|
||||
Some prefer it
|
||||
Octoroks!
|
||||
---
|
||||
I consoled
|
||||
Freezor over a
|
||||
cup of coffee.
|
||||
His problems
|
||||
just seemed to
|
||||
melt away!
|
||||
---
|
||||
Magic droplets
|
||||
of water don’t
|
||||
shut up.
|
||||
They just
|
||||
Kyameron!
|
||||
---
|
||||
I bought hot
|
||||
wings for
|
||||
Sluggula.
|
||||
They gave him
|
||||
explosive
|
||||
diarrhea!
|
||||
---
|
||||
Hardhat Beetle
|
||||
won’t
|
||||
Let It Be?
|
||||
Tell it to Get
|
||||
Back or give
|
||||
it a Ticket to
|
||||
Ride down
|
||||
a hole!
|
||||
---
|
|
@ -0,0 +1,103 @@
|
|||
|
||||
G G
|
||||
---
|
||||
All your base
|
||||
are belong
|
||||
to us.
|
||||
---
|
||||
You have ended
|
||||
the domination
|
||||
of dr. wily
|
||||
---
|
||||
thanks for
|
||||
playing!!!
|
||||
---
|
||||
|
||||
You Win!
|
||||
---
|
||||
Thank you!
|
||||
your quest
|
||||
is over.
|
||||
---
|
||||
A winner
|
||||
is
|
||||
you!
|
||||
---
|
||||
|
||||
WINNER!!
|
||||
---
|
||||
|
||||
I'm sorry
|
||||
|
||||
but your
|
||||
princess is in
|
||||
another castle
|
||||
---
|
||||
|
||||
success!
|
||||
---
|
||||
Whelp…
|
||||
that just
|
||||
happened
|
||||
---
|
||||
Oh hey…
|
||||
it's you
|
||||
---
|
||||
|
||||
Wheeeeee!!
|
||||
---
|
||||
Time for
|
||||
another one?
|
||||
---
|
||||
and
|
||||
|
||||
scene
|
||||
---
|
||||
|
||||
GOT EM!!
|
||||
---
|
||||
|
||||
THE VALUUUE!!!
|
||||
---
|
||||
Cool seed,
|
||||
|
||||
right?
|
||||
---
|
||||
|
||||
We did it!
|
||||
---
|
||||
Spam those
|
||||
emotes in
|
||||
wilds chat
|
||||
---
|
||||
|
||||
O M G
|
||||
---
|
||||
Hello. Will
|
||||
you be my
|
||||
friend?
|
||||
---
|
||||
Beetorp
|
||||
was
|
||||
here!
|
||||
---
|
||||
The Wind Fish
|
||||
will wake
|
||||
soon. Hoot!
|
||||
---
|
||||
meow meow meow
|
||||
meow meow meow
|
||||
oh my god!
|
||||
---
|
||||
Ahhhhhhhhh
|
||||
Ya ya yaaaah
|
||||
Ya ya yaaah
|
||||
---
|
||||
.done
|
||||
|
||||
.comment lol
|
||||
---
|
||||
You get to
|
||||
drink from
|
||||
the firehose
|
||||
---
|
|
@ -0,0 +1,87 @@
|
|||
|
||||
from typing import Any, List
|
||||
import copy
|
||||
from worlds.smz3.TotalSMZ3.Text.Dialog import Dialog
|
||||
from worlds.smz3.TotalSMZ3.Text.Texts import text_folder
|
||||
from yaml import load, Loader
|
||||
|
||||
class StringTable:
|
||||
|
||||
@staticmethod
|
||||
def ParseEntries(resource: str):
|
||||
with open(resource, 'rb') as f:
|
||||
yaml = str(f.read(), "utf-8")
|
||||
content = load(yaml, Loader)
|
||||
|
||||
result = []
|
||||
for entryValue in content:
|
||||
(key, value) = next(iter(entryValue.items()))
|
||||
if isinstance(value, List):
|
||||
result.append((key, value))
|
||||
elif isinstance(value, str):
|
||||
result.append((key, Dialog.Compiled(value)))
|
||||
elif isinstance(value, dict):
|
||||
result.append((key, Dialog.Compiled(value["NoPause"], False)))
|
||||
else: raise Exception(f"Did not expect an object of type {type(value)}")
|
||||
return result
|
||||
|
||||
template = ParseEntries.__func__(text_folder + "/Scripts/StringTable.yaml")
|
||||
|
||||
def __init__(self):
|
||||
self.entries = copy.deepcopy(StringTable.template)
|
||||
|
||||
def SetSahasrahlaRevealText(self, text: str):
|
||||
self.SetText("sahasrahla_quest_information", text)
|
||||
|
||||
def SetBombShopRevealText(self, text: str):
|
||||
self.SetText("bomb_shop", text)
|
||||
|
||||
def SetBlindText(self, text: str):
|
||||
self.SetText("blind_by_the_light", text)
|
||||
|
||||
def SetTavernManText(self, text: str):
|
||||
self.SetText("kakariko_tavern_fisherman", text)
|
||||
|
||||
def SetGanonFirstPhaseText(self, text: str):
|
||||
self.SetText("ganon_fall_in", text)
|
||||
|
||||
def SetGanonThirdPhaseText(self, text: str):
|
||||
self.SetText("ganon_phase_3", text)
|
||||
|
||||
def SetTriforceRoomText(self, text: str):
|
||||
self.SetText("end_triforce", "{NOBORDER}\n" + text)
|
||||
|
||||
def SetPedestalText(self, text: str):
|
||||
self.SetText("mastersword_pedestal_translated", text)
|
||||
|
||||
def SetEtherText(self, text: str):
|
||||
self.SetText("tablet_ether_book", text)
|
||||
|
||||
def SetBombosText(self, text: str):
|
||||
self.SetText("tablet_bombos_book", text)
|
||||
|
||||
def SetText(self, name: str, text: str):
|
||||
count = 0
|
||||
for key, value in self.entries:
|
||||
if (key == name):
|
||||
index = count
|
||||
break
|
||||
else:
|
||||
count += 1
|
||||
self.entries[index] = (name, Dialog.Compiled(text))
|
||||
|
||||
def GetPaddedBytes(self):
|
||||
return self.GetBytes(True)
|
||||
|
||||
def GetBytes(self, pad = False):
|
||||
maxBytes = 0x7355
|
||||
data = []
|
||||
for entry in self.entries:
|
||||
data += entry[1]
|
||||
|
||||
if (len(data) > maxBytes):
|
||||
raise Exception(f"String Table exceeds 0x{maxBytes:X} bytes")
|
||||
|
||||
if (pad and len(data) < maxBytes):
|
||||
data += [0xFF] * (maxBytes - len(data))
|
||||
return data
|
|
@ -0,0 +1,97 @@
|
|||
from typing import Any, List
|
||||
from worlds.smz3.TotalSMZ3.Region import Region
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.GanonsTower import GanonsTower
|
||||
from worlds.smz3.TotalSMZ3.Item import Item, ItemType
|
||||
from yaml import load, Loader
|
||||
import random
|
||||
import os
|
||||
|
||||
text_folder = os.path.dirname(__file__)
|
||||
|
||||
class Texts:
|
||||
@staticmethod
|
||||
def ParseYamlScripts(resource: str):
|
||||
with open(resource, 'rb') as f:
|
||||
yaml = str(f.read(), "utf-8")
|
||||
return load(yaml, Loader)
|
||||
|
||||
@staticmethod
|
||||
def ParseTextScript(resource: str):
|
||||
with open(resource, 'r') as file:
|
||||
return [text.rstrip('\n') for text in file.read().replace("\r", "").split("---\n") if text]
|
||||
|
||||
scripts: Any = ParseYamlScripts.__func__(text_folder + "/Scripts/General.yaml")
|
||||
blind: List[str] = ParseTextScript.__func__(text_folder + "/Scripts/Blind.txt")
|
||||
ganon: List[str] = ParseTextScript.__func__(text_folder + "/Scripts/Ganon.txt")
|
||||
tavernMan: List[str] = ParseTextScript.__func__(text_folder + "/Scripts/TavernMan.txt")
|
||||
triforceRoom: List[str] = ParseTextScript.__func__(text_folder + "/Scripts/TriforceRoom.txt")
|
||||
|
||||
@staticmethod
|
||||
def SahasrahlaReveal(dungeon: Region):
|
||||
text = Texts.scripts["SahasrahlaReveal"]
|
||||
return text.replace("<dungeon>", dungeon.Area)
|
||||
|
||||
@staticmethod
|
||||
def BombShopReveal(dungeons: List[Region]):
|
||||
text = Texts.scripts["BombShopReveal"]
|
||||
return text.replace("<first>", dungeons[0].Area).replace("<second>", dungeons[1].Area)
|
||||
|
||||
@staticmethod
|
||||
def GanonThirdPhaseSingle(silvers: Region):
|
||||
node = Texts.scripts["GanonSilversReveal"]["single"]
|
||||
text = node["local" if isinstance(silvers, GanonsTower) else "remote"]
|
||||
return text.replace("<region>", silvers.Area)
|
||||
|
||||
@staticmethod
|
||||
def GanonThirdPhaseMulti(silvers: Region, myWorld: int, silversWorld: int = None, silversPlayer: str = None):
|
||||
node = Texts.scripts["GanonSilversReveal"]["multi"]
|
||||
if silvers is None:
|
||||
if (silversWorld == myWorld.Id):
|
||||
return node["local"]
|
||||
player = silversPlayer
|
||||
else:
|
||||
if (silvers.world == myWorld):
|
||||
return node["local"]
|
||||
player = silvers.world.Player
|
||||
player = player.rjust(7 + len(player) // 2)
|
||||
text = node["remote"]
|
||||
return text.replace("<player>", player)
|
||||
|
||||
@staticmethod
|
||||
def ItemTextbox(item: Item):
|
||||
nameMap = {
|
||||
ItemType.BottleWithGoldBee : ItemType.BottleWithBee.name,
|
||||
ItemType.HeartContainerRefill : ItemType.HeartContainer.name,
|
||||
ItemType.OneRupee : "PocketRupees",
|
||||
ItemType.FiveRupees : "PocketRupees",
|
||||
ItemType.TwentyRupees : "CouchRupees",
|
||||
ItemType.TwentyRupees2 : "CouchRupees",
|
||||
ItemType.FiftyRupees : "CouchRupees",
|
||||
ItemType.BombUpgrade5 : "BombUpgrade",
|
||||
ItemType.BombUpgrade10 : "BombUpgrade",
|
||||
ItemType.ArrowUpgrade5 : "ArrowUpgrade",
|
||||
ItemType.ArrowUpgrade10 : "ArrowUpgrade",
|
||||
item.Type : item.Type.name,
|
||||
}
|
||||
if item.IsMap(): name = "Map"
|
||||
elif item.IsCompass(): name = "Compass"
|
||||
elif item.IsKeycard(): name = "Keycard"
|
||||
else: name = nameMap[item.Type]
|
||||
|
||||
items = Texts.scripts["Items"]
|
||||
return items.get(name, None) or items["default"]
|
||||
|
||||
@staticmethod
|
||||
def Blind(rnd: random): return Texts.RandomLine(rnd, Texts.blind)
|
||||
|
||||
@staticmethod
|
||||
def TavernMan(rnd: random): return Texts.RandomLine(rnd, Texts.tavernMan)
|
||||
|
||||
@staticmethod
|
||||
def GanonFirstPhase(rnd: random): return Texts.RandomLine(rnd, Texts.ganon)
|
||||
|
||||
@staticmethod
|
||||
def TriforceRoom(rnd: random): return Texts.RandomLine(rnd, Texts.triforceRoom)
|
||||
|
||||
@staticmethod
|
||||
def RandomLine(rnd: random, lines: List[str]): return lines[rnd.randrange(0, len(lines))]
|
|
@ -0,0 +1,166 @@
|
|||
from typing import Dict, List
|
||||
import random
|
||||
|
||||
import worlds.smz3.TotalSMZ3.Region as Region
|
||||
import worlds.smz3.TotalSMZ3.Config as Config
|
||||
import worlds.smz3.TotalSMZ3.Item as Item
|
||||
import worlds.smz3.TotalSMZ3.Location as Location
|
||||
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.CastleTower import CastleTower
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.EasternPalace import EasternPalace
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.DesertPalace import DesertPalace
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.TowerOfHera import TowerOfHera
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.PalaceOfDarkness import PalaceOfDarkness
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.SwampPalace import SwampPalace
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.SkullWoods import SkullWoods
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.ThievesTown import ThievesTown
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.IcePalace import IcePalace
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.MiseryMire import MiseryMire
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.TurtleRock import TurtleRock
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.GanonsTower import GanonsTower
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.LightWorld.DeathMountain.West import West as LightWorldDeathMountainWest
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.LightWorld.DeathMountain.East import East as LightWorldDeathMountainEast
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.LightWorld.NorthWest import NorthWest as LightWorldNorthWest
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.LightWorld.NorthEast import NorthEast as LightWorldNorthEast
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.LightWorld.South import South as LightWorldSouth
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.HyruleCastle import HyruleCastle
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.DarkWorld.DeathMountain.West import West as DarkWorldDeathMountainWest
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.DarkWorld.DeathMountain.East import East as DarkWorldDeathMountainEast
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.DarkWorld.NorthWest import NorthWest as DarkWorldNorthWest
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.DarkWorld.NorthEast import NorthEast as DarkWorldNorthEast
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.DarkWorld.South import South as DarkWorldSouth
|
||||
from worlds.smz3.TotalSMZ3.Regions.Zelda.DarkWorld.Mire import Mire as DarkWorldMire
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Crateria.Central import Central
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Crateria.West import West as CrateriaWest
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Crateria.East import East as CrateriaEast
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Brinstar.Blue import Blue
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Brinstar.Green import Green
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Brinstar.Kraid import Kraid
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Brinstar.Pink import Pink
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Brinstar.Red import Red
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Maridia.Outer import Outer
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.Maridia.Inner import Inner
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.NorfairUpper.West import West as NorfairUpperWest
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.NorfairUpper.East import East as NorfairUpperEast
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.NorfairUpper.Crocomire import Crocomire
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.NorfairLower.West import West as NorfairLowerWest
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.NorfairLower.East import East as NorfairLowerEast
|
||||
from worlds.smz3.TotalSMZ3.Regions.SuperMetroid.WreckedShip import WreckedShip
|
||||
|
||||
class World:
|
||||
Locations: List[Location.Location]
|
||||
Regions: List[Region.Region]
|
||||
Config: Config.Config
|
||||
Player: str
|
||||
Guid: str
|
||||
Id: int
|
||||
|
||||
def Items(self):
|
||||
return [l.Item for l in self.Locations if l.Item != None]
|
||||
|
||||
locationLookup: Dict[str, Location.Location]
|
||||
regionLookup: Dict[str, Region.Region]
|
||||
|
||||
def GetLocation(self, name:str): return self.locationLookup[name]
|
||||
def GetRegion(self, name:str): return self.regionLookup[name]
|
||||
|
||||
def __init__(self, config: Config, player: str, id: int, guid: str):
|
||||
self.Config = config
|
||||
self.Player = player
|
||||
self.Id = id
|
||||
self.Guid = guid
|
||||
|
||||
self.Regions = [
|
||||
CastleTower(self, self.Config),
|
||||
EasternPalace(self, self.Config),
|
||||
DesertPalace(self, self.Config),
|
||||
TowerOfHera(self, self.Config),
|
||||
PalaceOfDarkness(self, self.Config),
|
||||
SwampPalace(self, self.Config),
|
||||
SkullWoods(self, self.Config),
|
||||
ThievesTown(self, self.Config),
|
||||
IcePalace(self, self.Config),
|
||||
MiseryMire(self, self.Config),
|
||||
TurtleRock(self, self.Config),
|
||||
GanonsTower(self, self.Config),
|
||||
LightWorldDeathMountainWest(self, self.Config),
|
||||
LightWorldDeathMountainEast(self, self.Config),
|
||||
LightWorldNorthWest(self, self.Config),
|
||||
LightWorldNorthEast(self, self.Config),
|
||||
LightWorldSouth(self, self.Config),
|
||||
HyruleCastle(self, self.Config),
|
||||
DarkWorldDeathMountainWest(self, self.Config),
|
||||
DarkWorldDeathMountainEast(self, self.Config),
|
||||
DarkWorldNorthWest(self, self.Config),
|
||||
DarkWorldNorthEast(self, self.Config),
|
||||
DarkWorldSouth(self, self.Config),
|
||||
DarkWorldMire(self, self.Config),
|
||||
Central(self, self.Config),
|
||||
CrateriaWest(self, self.Config),
|
||||
CrateriaEast(self, self.Config),
|
||||
Blue(self, self.Config),
|
||||
Green(self, self.Config),
|
||||
Kraid(self, self.Config),
|
||||
Pink(self, self.Config),
|
||||
Red(self, self.Config),
|
||||
Outer(self, self.Config),
|
||||
Inner(self, self.Config),
|
||||
NorfairUpperWest(self, self.Config),
|
||||
NorfairUpperEast(self, self.Config),
|
||||
Crocomire(self, self.Config),
|
||||
NorfairLowerWest(self, self.Config),
|
||||
NorfairLowerEast(self, self.Config),
|
||||
WreckedShip(self, self.Config)
|
||||
]
|
||||
|
||||
self.Locations = []
|
||||
for r in self.Regions:
|
||||
self.Locations = self.Locations + r.Locations
|
||||
|
||||
self.regionLookup = {r.Name:r for r in self.Regions}
|
||||
self.locationLookup = {loc.Name:loc for loc in self.Locations}
|
||||
|
||||
for region in self.Regions:
|
||||
region.GenerateLocationLookup()
|
||||
|
||||
|
||||
def CanEnter(self, regionName: str, items: Item.Progression):
|
||||
region = self.regionLookup[regionName]
|
||||
if (region == None):
|
||||
raise Exception(f"World.CanEnter: Invalid region name {regionName}", f'{regionName=}'.partition('=')[0])
|
||||
return region.CanEnter(items)
|
||||
|
||||
def CanAquire(self, items: Item.Progression, reward: Region.RewardType):
|
||||
return next(iter([region for region in self.Regions if isinstance(region, Region.IReward) and region.Reward == reward])).CanComplete(items)
|
||||
|
||||
def CanAquireAll(self, items: Item.Progression, *rewards: Region.RewardType):
|
||||
for region in self.Regions:
|
||||
if issubclass(type(region), Region.IReward):
|
||||
if (region.Reward in rewards):
|
||||
if not region.CanComplete(items):
|
||||
return False
|
||||
return True
|
||||
|
||||
# return all(region.CanComplete(items) for region in self.Regions if (isinstance(region, Region.IReward) and region.Reward in rewards))
|
||||
|
||||
def Setup(self, rnd: random):
|
||||
self.SetMedallions(rnd)
|
||||
self.SetRewards(rnd)
|
||||
|
||||
def SetMedallions(self, rnd: random):
|
||||
medallionMap = {0: Item.ItemType.Bombos, 1: Item.ItemType.Ether, 2: Item.ItemType.Quake}
|
||||
regionList = [region for region in self.Regions if isinstance(region, Region.IMedallionAccess)]
|
||||
for region in regionList:
|
||||
region.Medallion = medallionMap[rnd.randint(0, 2)]
|
||||
|
||||
def SetRewards(self, rnd: random):
|
||||
rewards = [
|
||||
Region.RewardType.PendantGreen, Region.RewardType.PendantNonGreen, Region.RewardType.PendantNonGreen, Region.RewardType.CrystalRed, Region.RewardType.CrystalRed,
|
||||
Region.RewardType.CrystalBlue, Region.RewardType.CrystalBlue, Region.RewardType.CrystalBlue, Region.RewardType.CrystalBlue, Region.RewardType.CrystalBlue
|
||||
]
|
||||
rnd.shuffle(rewards)
|
||||
regionList = [region for region in self.Regions if isinstance(region, Region.IReward) and region.Reward == Region.RewardType.Null]
|
||||
for region in regionList:
|
||||
region.Reward = rewards[0]
|
||||
rewards.remove(region.Reward)
|
||||
|
|
@ -0,0 +1,438 @@
|
|||
import logging
|
||||
import copy
|
||||
import os
|
||||
import random
|
||||
import threading
|
||||
import Patch
|
||||
from typing import Dict, Set, TextIO
|
||||
|
||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, RegionType, CollectionState
|
||||
from worlds.generic.Rules import add_rule, set_rule
|
||||
import worlds.smz3.TotalSMZ3.Item as TotalSMZ3Item
|
||||
from worlds.smz3.TotalSMZ3.World import World as TotalSMZ3World
|
||||
from worlds.smz3.TotalSMZ3.Config import Config, GameMode, GanonInvincible, Goal, KeyShuffle, MorphLocation, SMLogic, SwordLocation, Z3Logic
|
||||
from worlds.smz3.TotalSMZ3.Location import LocationType, locations_start_id, Location as TotalSMZ3Location
|
||||
from worlds.smz3.TotalSMZ3.Patch import Patch as TotalSMZ3Patch, getWord, getWordArray
|
||||
from ..AutoWorld import World, AutoLogicRegister
|
||||
from .Rom import get_base_rom_bytes
|
||||
from .ips import IPS_Patch
|
||||
from .Options import smz3_options
|
||||
|
||||
world_folder = os.path.dirname(__file__)
|
||||
logger = logging.getLogger("SMZ3")
|
||||
|
||||
class SMCollectionState(metaclass=AutoLogicRegister):
|
||||
def init_mixin(self, parent: MultiWorld):
|
||||
# for unit tests where MultiWorld is instantiated before worlds
|
||||
if hasattr(parent, "state"):
|
||||
self.smz3state = {player: TotalSMZ3Item.Progression([]) for player in parent.get_game_players("SMZ3")}
|
||||
else:
|
||||
self.smz3state = {}
|
||||
|
||||
def copy_mixin(self, ret) -> CollectionState:
|
||||
ret.smz3state = {player: copy.deepcopy(self.smz3state[player]) for player in self.world.get_game_players("SMZ3")}
|
||||
return ret
|
||||
|
||||
|
||||
class SMZ3World(World):
|
||||
"""
|
||||
A python port of Super Metroid & A Link To The Past Crossover Item Randomizer based on v11.2 of Total's SMZ3.
|
||||
This is allowed as long as we keep features and logic as close as possible as the original.
|
||||
"""
|
||||
game: str = "SMZ3"
|
||||
topology_present = False
|
||||
data_version = 0
|
||||
options = smz3_options
|
||||
item_names: Set[str] = frozenset(TotalSMZ3Item.lookup_name_to_id)
|
||||
location_names: Set[str]
|
||||
item_name_to_id = TotalSMZ3Item.lookup_name_to_id
|
||||
location_name_to_id: Dict[str, int] = {key : locations_start_id + value.Id for key, value in TotalSMZ3World(Config({}), "", 0, "").locationLookup.items()}
|
||||
|
||||
remote_items: bool = False
|
||||
remote_start_inventory: bool = False
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int):
|
||||
self.rom_name_available_event = threading.Event()
|
||||
self.locations = {}
|
||||
self.unreachable = []
|
||||
super().__init__(world, player)
|
||||
|
||||
def generate_early(self):
|
||||
config = Config({})
|
||||
config.GameMode = GameMode.Multiworld
|
||||
config.Z3Logic = Z3Logic.Normal
|
||||
config.SMLogic = SMLogic(self.world.sm_logic[self.player].value)
|
||||
config.SwordLocation = SwordLocation(self.world.sword_location[self.player].value)
|
||||
config.MorphLocation = MorphLocation(self.world.morph_location[self.player].value)
|
||||
config.Goal = Goal.DefeatBoth
|
||||
config.KeyShuffle = KeyShuffle(self.world.key_shuffle[self.player].value)
|
||||
config.Keysanity = config.KeyShuffle != KeyShuffle.Null
|
||||
config.GanonInvincible = GanonInvincible.BeforeCrystals
|
||||
|
||||
self.local_random = random.Random(self.world.random.randint(0, 1000))
|
||||
self.smz3World = TotalSMZ3World(config, self.world.get_player_name(self.player), self.player, self.world.seed_name)
|
||||
self.smz3DungeonItems = []
|
||||
SMZ3World.location_names = frozenset(self.smz3World.locationLookup.keys())
|
||||
|
||||
self.world.state.smz3state[self.player] = TotalSMZ3Item.Progression([])
|
||||
|
||||
def generate_basic(self):
|
||||
self.smz3World.Setup(self.world.random)
|
||||
self.dungeon = TotalSMZ3Item.Item.CreateDungeonPool(self.smz3World)
|
||||
self.dungeon.reverse()
|
||||
self.progression = TotalSMZ3Item.Item.CreateProgressionPool(self.smz3World)
|
||||
self.keyCardsItems = TotalSMZ3Item.Item.CreateKeycards(self.smz3World)
|
||||
|
||||
niceItems = TotalSMZ3Item.Item.CreateNicePool(self.smz3World)
|
||||
junkItems = TotalSMZ3Item.Item.CreateJunkPool(self.smz3World)
|
||||
allJunkItems = niceItems + junkItems
|
||||
|
||||
if (self.smz3World.Config.Keysanity):
|
||||
progressionItems = self.progression + self.dungeon + self.keyCardsItems
|
||||
else:
|
||||
progressionItems = self.progression
|
||||
for item in self.keyCardsItems:
|
||||
self.world.push_precollected(SMZ3Item(item.Type.name, False, item.Type, self.item_name_to_id[item.Type.name], self.player, item))
|
||||
|
||||
itemPool = [SMZ3Item(item.Type.name, True, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in progressionItems] + \
|
||||
[SMZ3Item(item.Type.name, False, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in allJunkItems]
|
||||
self.smz3DungeonItems = [SMZ3Item(item.Type.name, True, item.Type, self.item_name_to_id[item.Type.name], self.player, item) for item in self.dungeon]
|
||||
self.world.itempool += itemPool
|
||||
|
||||
def set_rules(self):
|
||||
# SM G4 is logically required to access Ganon's Tower in SMZ3
|
||||
self.world.completion_condition[self.player] = lambda state: \
|
||||
self.smz3World.GetRegion("Ganon's Tower").CanEnter(state.smz3state[self.player]) and \
|
||||
self.smz3World.GetRegion("Ganon's Tower").TowerAscend(state.smz3state[self.player])
|
||||
|
||||
for region in self.smz3World.Regions:
|
||||
entrance = self.world.get_entrance('Menu' + "->" + region.Name, self.player)
|
||||
set_rule(entrance, lambda state, region=region: region.CanEnter(state.smz3state[self.player]))
|
||||
for loc in region.Locations:
|
||||
l = self.locations[loc.Name]
|
||||
if self.world.accessibility[self.player] != 'locations':
|
||||
l.always_allow = lambda state, item, loc=loc: \
|
||||
item.game == "SMZ3" and \
|
||||
loc.alwaysAllow(TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World), state.smz3state[self.player])
|
||||
old_rule = l.item_rule
|
||||
l.item_rule = lambda item, loc=loc, region=region: (\
|
||||
item.game != "SMZ3" or \
|
||||
loc.allow(TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World), None) and \
|
||||
region.CanFill(TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World))) and old_rule(item)
|
||||
set_rule(l, lambda state, loc=loc: loc.Available(state.smz3state[self.player]))
|
||||
|
||||
def create_regions(self):
|
||||
self.create_locations(self.player)
|
||||
startRegion = self.create_region(self.world, self.player, 'Menu')
|
||||
self.world.regions.append(startRegion)
|
||||
|
||||
for region in self.smz3World.Regions:
|
||||
currentRegion = self.create_region(self.world, self.player, region.Name, region.locationLookup.keys(), [region.Name + "->" + 'Menu'])
|
||||
self.world.regions.append(currentRegion)
|
||||
entrance = self.world.get_entrance(region.Name + "->" + 'Menu', self.player)
|
||||
entrance.connect(startRegion)
|
||||
exit = Entrance(self.player, 'Menu' + "->" + region.Name, startRegion)
|
||||
startRegion.exits.append(exit)
|
||||
exit.connect(currentRegion)
|
||||
|
||||
def apply_sm_custom_sprite(self):
|
||||
itemSprites = ["off_world_prog_item.bin", "off_world_item.bin"]
|
||||
itemSpritesAddress = [0xF800, 0xF900]
|
||||
idx = 0
|
||||
offworldSprites = {}
|
||||
for fileName in itemSprites:
|
||||
with open(world_folder + "/data/custom_sprite/" + fileName, 'rb') as stream:
|
||||
buffer = bytearray(stream.read())
|
||||
offworldSprites[0x04Eff2 + 10*((0x6B + 0x40) + idx)] = bytearray(getWordArray(itemSpritesAddress[idx])) + buffer[0:8]
|
||||
offworldSprites[0x090000 + itemSpritesAddress[idx]] = buffer[8:264]
|
||||
idx += 1
|
||||
return offworldSprites
|
||||
|
||||
def convert_to_sm_item_name(self, itemName):
|
||||
charMap = { "A" : 0x3CE0,
|
||||
"B" : 0x3CE1,
|
||||
"C" : 0x3CE2,
|
||||
"D" : 0x3CE3,
|
||||
"E" : 0x3CE4,
|
||||
"F" : 0x3CE5,
|
||||
"G" : 0x3CE6,
|
||||
"H" : 0x3CE7,
|
||||
"I" : 0x3CE8,
|
||||
"J" : 0x3CE9,
|
||||
"K" : 0x3CEA,
|
||||
"L" : 0x3CEB,
|
||||
"M" : 0x3CEC,
|
||||
"N" : 0x3CED,
|
||||
"O" : 0x3CEE,
|
||||
"P" : 0x3CEF,
|
||||
"Q" : 0x3CF0,
|
||||
"R" : 0x3CF1,
|
||||
"S" : 0x3CF2,
|
||||
"T" : 0x3CF3,
|
||||
"U" : 0x3CF4,
|
||||
"V" : 0x3CF5,
|
||||
"W" : 0x3CF6,
|
||||
"X" : 0x3CF7,
|
||||
"Y" : 0x3CF8,
|
||||
"Z" : 0x3CF9,
|
||||
" " : 0x3C4E,
|
||||
"!" : 0x3CFF,
|
||||
"?" : 0x3CFE,
|
||||
"'" : 0x3CFD,
|
||||
"," : 0x3CFB,
|
||||
"." : 0x3CFA,
|
||||
"-" : 0x3CCF,
|
||||
"_" : 0x000E,
|
||||
"1" : 0x3C00,
|
||||
"2" : 0x3C01,
|
||||
"3" : 0x3C02,
|
||||
"4" : 0x3C03,
|
||||
"5" : 0x3C04,
|
||||
"6" : 0x3C05,
|
||||
"7" : 0x3C06,
|
||||
"8" : 0x3C07,
|
||||
"9" : 0x3C08,
|
||||
"0" : 0x3C09,
|
||||
"%" : 0x3C0A}
|
||||
data = []
|
||||
|
||||
itemName = itemName.upper()[:26]
|
||||
itemName = itemName.strip()
|
||||
itemName = itemName.center(26, " ")
|
||||
itemName = "___" + itemName + "___"
|
||||
|
||||
for char in itemName:
|
||||
(w0, w1) = getWord(charMap.get(char, 0x3C4E))
|
||||
data.append(w0)
|
||||
data.append(w1)
|
||||
return data
|
||||
|
||||
def convert_to_lttp_item_name(self, itemName):
|
||||
return bytearray(itemName[:19].center(19, " ") , 'utf8') + bytearray(0)
|
||||
|
||||
def apply_item_names(self):
|
||||
patch = {}
|
||||
sm_remote_idx = 0
|
||||
lttp_remote_idx = 0
|
||||
for location in self.smz3World.Locations:
|
||||
if self.world.worlds[location.APLocation.item.player].game != self.game:
|
||||
if location.Type == LocationType.Visible or location.Type == LocationType.Chozo or location.Type == LocationType.Hidden:
|
||||
patch[0x390000 + sm_remote_idx*64] = self.convert_to_sm_item_name(location.APLocation.item.name)
|
||||
sm_remote_idx += 1
|
||||
progressionItem = (0 if location.APLocation.item.advancement else 0x8000) + sm_remote_idx
|
||||
patch[0x386000 + (location.Id * 8) + 6] = bytearray(getWordArray(progressionItem))
|
||||
else:
|
||||
patch[0x390000 + 100 * 64 + lttp_remote_idx * 20] = self.convert_to_lttp_item_name(location.APLocation.item.name)
|
||||
lttp_remote_idx += 1
|
||||
progressionItem = (0 if location.APLocation.item.advancement else 0x8000) + lttp_remote_idx
|
||||
patch[0x386000 + (location.Id * 8) + 6] = bytearray(getWordArray(progressionItem))
|
||||
|
||||
return patch
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
try:
|
||||
base_combined_rom = get_base_rom_bytes()
|
||||
basepatch = IPS_Patch.load(world_folder + "/data/zsm.ips")
|
||||
base_combined_rom = basepatch.apply(base_combined_rom)
|
||||
|
||||
patcher = TotalSMZ3Patch(self.smz3World,
|
||||
[world.smz3World for key, world in self.world.worlds.items() if isinstance(world, SMZ3World)],
|
||||
self.world.seed_name,
|
||||
self.world.seed,
|
||||
self.local_random,
|
||||
self.world.world_name_lookup,
|
||||
next(iter(loc.player for loc in self.world.get_locations() if loc.item == self.create_item("SilverArrows"))))
|
||||
patches = patcher.Create(self.smz3World.Config)
|
||||
patches.update(self.apply_sm_custom_sprite())
|
||||
patches.update(self.apply_item_names())
|
||||
for addr, bytes in patches.items():
|
||||
offset = 0
|
||||
for byte in bytes:
|
||||
base_combined_rom[addr + offset] = byte
|
||||
offset += 1
|
||||
|
||||
outfilebase = 'AP_' + self.world.seed_name
|
||||
outfilepname = f'_P{self.player}'
|
||||
outfilepname += f"_{self.world.player_name[self.player].replace(' ', '_')}" \
|
||||
|
||||
filename = os.path.join(output_directory, f'{outfilebase}{outfilepname}.sfc')
|
||||
with open(filename, "wb") as binary_file:
|
||||
binary_file.write(base_combined_rom)
|
||||
Patch.create_patch_file(filename, player=self.player, player_name=self.world.player_name[self.player], game=Patch.GAME_SMZ3)
|
||||
os.remove(filename)
|
||||
self.rom_name = bytearray(patcher.title, 'utf8')
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
self.rom_name_available_event.set() # make sure threading continues and errors are collected
|
||||
|
||||
def modify_multidata(self, multidata: dict):
|
||||
import base64
|
||||
if (not self.smz3World.Config.Keysanity):
|
||||
for item_name in self.keyCardsItems:
|
||||
item_id = self.item_name_to_id.get(item_name.Type.name, None)
|
||||
try:
|
||||
multidata["precollected_items"][self.player].remove(item_id)
|
||||
except ValueError as e:
|
||||
logger.warning(f"Attempted to remove nonexistent item id {item_id} from smz3 precollected items ({item_name})")
|
||||
|
||||
# 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()
|
||||
payload = multidata["connect_names"][self.world.player_name[self.player]]
|
||||
multidata["connect_names"][new_name] = payload
|
||||
del (multidata["connect_names"][self.world.player_name[self.player]])
|
||||
|
||||
def fill_slot_data(self):
|
||||
slot_data = {}
|
||||
return slot_data
|
||||
|
||||
def collect(self, state: CollectionState, item: Item) -> bool:
|
||||
state.smz3state[item.player].Add([TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World)])
|
||||
if item.advancement:
|
||||
state.prog_items[item.name, item.player] += 1
|
||||
return True # indicate that a logical state change has occured
|
||||
return False
|
||||
|
||||
def remove(self, state: CollectionState, item: Item) -> bool:
|
||||
name = self.collect_item(state, item, True)
|
||||
if name:
|
||||
state.smz3state[item.player].Remove([TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[item.name], self.smz3World)])
|
||||
state.prog_items[name, item.player] -= 1
|
||||
if state.prog_items[name, item.player] < 1:
|
||||
del (state.prog_items[name, item.player])
|
||||
return True
|
||||
return False
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return SMZ3Item(name, True, TotalSMZ3Item.ItemType[name], self.item_name_to_id[name], player = self.player)
|
||||
|
||||
def pre_fill(self):
|
||||
from Fill import fill_restrictive
|
||||
self.InitialFillInOwnWorld()
|
||||
|
||||
if (not self.smz3World.Config.Keysanity):
|
||||
locations = [loc for loc in self.locations.values() if loc.item is None]
|
||||
self.world.random.shuffle(locations)
|
||||
|
||||
all_state = self.world.get_all_state(False)
|
||||
for item in self.smz3DungeonItems:
|
||||
all_state.remove(item)
|
||||
|
||||
all_dungeonItems = self.smz3DungeonItems[:]
|
||||
fill_restrictive(self.world, all_state, locations, all_dungeonItems, True, True)
|
||||
# some small or big keys (those always_allow) can be unreachable in-game
|
||||
# while logic still collects some of them (probably to simulate the player collecting pot keys in the logic), some others don't
|
||||
# so we need to remove those exceptions as progression items
|
||||
if self.world.accessibility[self.player] != 'locations':
|
||||
exception_item = [TotalSMZ3Item.ItemType.BigKeySW, TotalSMZ3Item.ItemType.BigKeySP, TotalSMZ3Item.ItemType.KeyTH]
|
||||
for item in self.smz3DungeonItems:
|
||||
if item.item.Type in exception_item and item.location.always_allow(all_state, item) and not all_state.can_reach(item.location):
|
||||
item.advancement = False
|
||||
item.item.Progression = False
|
||||
item.location.event = False
|
||||
self.unreachable.append(item.location)
|
||||
|
||||
def get_pre_fill_items(self):
|
||||
if (not self.smz3World.Config.Keysanity):
|
||||
return self.smz3DungeonItems
|
||||
else:
|
||||
return []
|
||||
|
||||
def write_spoiler(self, spoiler_handle: TextIO):
|
||||
self.world.spoiler.unreachables.update(self.unreachable)
|
||||
|
||||
def FillItemAtLocation(self, itemPool, itemType, location):
|
||||
itemToPlace = TotalSMZ3Item.Item.Get(itemPool, itemType, self.smz3World)
|
||||
if (itemToPlace == None):
|
||||
raise Exception(f"Tried to place item {itemType} at {location.Name}, but there is no such item in the item pool")
|
||||
else:
|
||||
location.Item = itemToPlace
|
||||
itemFromPool = next((i for i in self.world.itempool if i.player == self.player and i.name == itemToPlace.Type.name), None)
|
||||
if itemFromPool is not None:
|
||||
self.world.get_location(location.Name, self.player).place_locked_item(itemFromPool)
|
||||
self.world.itempool.remove(itemFromPool)
|
||||
else:
|
||||
itemFromPool = next((i for i in self.smz3DungeonItems if i.player == self.player and i.name == itemToPlace.Type.name), None)
|
||||
if itemFromPool is not None:
|
||||
self.world.get_location(location.Name, self.player).place_locked_item(itemFromPool)
|
||||
self.smz3DungeonItems.remove(itemFromPool)
|
||||
itemPool.remove(itemToPlace)
|
||||
|
||||
def FrontFillItemInOwnWorld(self, itemPool, itemType):
|
||||
item = TotalSMZ3Item.Item.Get(itemPool, itemType, self.smz3World)
|
||||
location = next(iter(self.world.random.sample(TotalSMZ3Location.AvailableGlobal(TotalSMZ3Location.Empty(self.smz3World.Locations), self.smz3World.Items()), 1)), None)
|
||||
if (location == None):
|
||||
raise Exception(f"Tried to front fill {item.Name} in, but no location was available")
|
||||
|
||||
location.Item = item
|
||||
itemFromPool = next((i for i in self.world.itempool if i.player == self.player and i.name == item.Type.name), None)
|
||||
if itemFromPool is not None:
|
||||
self.world.get_location(location.Name, self.player).place_locked_item(itemFromPool)
|
||||
self.world.itempool.remove(itemFromPool)
|
||||
itemPool.remove(item)
|
||||
|
||||
def InitialFillInOwnWorld(self):
|
||||
self.FillItemAtLocation(self.dungeon, TotalSMZ3Item.ItemType.KeySW, self.smz3World.GetLocation("Skull Woods - Pinball Room"))
|
||||
|
||||
# /* Check Swords option and place as needed */
|
||||
if self.smz3World.Config.SwordLocation == SwordLocation.Uncle:
|
||||
self.FillItemAtLocation(self.progression, TotalSMZ3Item.ItemType.ProgressiveSword, self.smz3World.GetLocation("Link's Uncle"))
|
||||
elif self.smz3World.Config.SwordLocation == SwordLocation.Early:
|
||||
self.FrontFillItemInOwnWorld(self.progression, TotalSMZ3Item.ItemType.ProgressiveSword)
|
||||
|
||||
# /* Check Morph option and place as needed */
|
||||
if self.smz3World.Config.MorphLocation == MorphLocation.Original:
|
||||
self.FillItemAtLocation(self.progression, TotalSMZ3Item.ItemType.Morph, self.smz3World.GetLocation("Morphing Ball"))
|
||||
elif self.smz3World.Config.MorphLocation == MorphLocation.Early:
|
||||
self.FrontFillItemInOwnWorld(self.progression, TotalSMZ3Item.ItemType.Morph)
|
||||
|
||||
# /* We place a PB and Super in Sphere 1 to make sure the filler
|
||||
# * doesn't start locking items behind this when there are a
|
||||
# * high chance of the trash fill actually making them available */
|
||||
self.FrontFillItemInOwnWorld(self.progression, TotalSMZ3Item.ItemType.Super)
|
||||
self.FrontFillItemInOwnWorld(self.progression, TotalSMZ3Item.ItemType.PowerBomb)
|
||||
|
||||
def create_locations(self, player: int):
|
||||
for name, id in SMZ3World.location_name_to_id.items():
|
||||
newLoc = SMZ3Location(player, name, id)
|
||||
self.locations[name] = newLoc
|
||||
self.smz3World.locationLookup[name].APLocation = newLoc
|
||||
|
||||
def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
ret = Region(name, RegionType.LightWorld, name, player)
|
||||
ret.world = world
|
||||
if locations:
|
||||
for loc in locations:
|
||||
location = self.locations[loc]
|
||||
location.parent_region = ret
|
||||
ret.locations.append(location)
|
||||
if exits:
|
||||
for exit in exits:
|
||||
ret.exits.append(Entrance(player, exit, ret))
|
||||
return ret
|
||||
|
||||
|
||||
class SMZ3Location(Location):
|
||||
game: str = "SMZ3"
|
||||
|
||||
def __init__(self, player: int, name: str, address=None, parent=None):
|
||||
super(SMZ3Location, self).__init__(player, name, address, parent)
|
||||
|
||||
def can_fill(self, state: CollectionState, item: Item, check_access=True) -> bool:
|
||||
oldItem = self.item
|
||||
self.item = item
|
||||
result = self.always_allow(state, item) or (self.item_rule(item) and (not check_access or self.can_reach(state)))
|
||||
self.item = oldItem
|
||||
return result
|
||||
|
||||
class SMZ3Item(Item):
|
||||
game = "SMZ3"
|
||||
|
||||
def __init__(self, name, advancement, type, code, player: int = None, item = None):
|
||||
self.type = type
|
||||
self.item = item
|
||||
super(SMZ3Item, self).__init__(name, advancement, code, player)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,252 @@
|
|||
import itertools
|
||||
|
||||
def range_union(ranges):
|
||||
ret = []
|
||||
for rg in sorted([[r.start, r.stop] for r in ranges]):
|
||||
begin, end = rg[0], rg[-1]
|
||||
if ret and ret[-1][1] > begin:
|
||||
ret[-1][1] = max(ret[-1][1], end)
|
||||
else:
|
||||
ret.append([begin, end])
|
||||
return [range(r[0], r[1]) for r in ret]
|
||||
|
||||
# adapted from ips-util for python 3.2 (https://pypi.org/project/ips-util/)
|
||||
class IPS_Patch(object):
|
||||
def __init__(self, patchDict=None):
|
||||
self.records = []
|
||||
self.truncate_length = None
|
||||
self.max_size = 0
|
||||
if patchDict is not None:
|
||||
for addr, data in patchDict.items():
|
||||
byteData = bytearray(data)
|
||||
self.add_record(addr, byteData)
|
||||
|
||||
def toDict(self):
|
||||
ret = {}
|
||||
for record in self.records:
|
||||
if 'rle_count' in record:
|
||||
ret[record['address']] = [int.from_bytes(record['data'],'little')]*record['rle_count']
|
||||
else:
|
||||
ret[record['address']] = [int(b) for b in record['data']]
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def load(filename):
|
||||
loaded_patch = IPS_Patch()
|
||||
with open(filename, 'rb') as file:
|
||||
header = file.read(5)
|
||||
if header != b'PATCH':
|
||||
raise Exception('Not a valid IPS patch file!')
|
||||
while True:
|
||||
address_bytes = file.read(3)
|
||||
if address_bytes == b'EOF':
|
||||
break
|
||||
address = int.from_bytes(address_bytes, byteorder='big')
|
||||
length = int.from_bytes(file.read(2), byteorder='big')
|
||||
rle_count = 0
|
||||
if length == 0:
|
||||
rle_count = int.from_bytes(file.read(2), byteorder='big')
|
||||
length = 1
|
||||
data = file.read(length)
|
||||
if rle_count > 0:
|
||||
loaded_patch.add_rle_record(address, data, rle_count)
|
||||
else:
|
||||
loaded_patch.add_record(address, data)
|
||||
|
||||
truncate_bytes = file.read(3)
|
||||
if len(truncate_bytes) == 3:
|
||||
loaded_patch.set_truncate_length(int.from_bytes(truncate_bytes, byteorder='big'))
|
||||
return loaded_patch
|
||||
|
||||
@staticmethod
|
||||
def create(original_data, patched_data):
|
||||
# The heuristics for optimizing a patch were chosen with reference to
|
||||
# the source code of Flips: https://github.com/Alcaro/Flips
|
||||
|
||||
patch = IPS_Patch()
|
||||
|
||||
run_in_progress = False
|
||||
current_run_start = 0
|
||||
current_run_data = bytearray()
|
||||
|
||||
runs = []
|
||||
|
||||
if len(original_data) > len(patched_data):
|
||||
patch.set_truncate_length(len(patched_data))
|
||||
original_data = original_data[:len(patched_data)]
|
||||
elif len(original_data) < len(patched_data):
|
||||
original_data += bytes([0] * (len(patched_data) - len(original_data)))
|
||||
|
||||
if original_data[-1] == 0 and patched_data[-1] == 0:
|
||||
patch.add_record(len(patched_data) - 1, bytes([0]))
|
||||
|
||||
for index, (original, patched) in enumerate(zip(original_data, patched_data)):
|
||||
if not run_in_progress:
|
||||
if original != patched:
|
||||
run_in_progress = True
|
||||
current_run_start = index
|
||||
current_run_data = bytearray([patched])
|
||||
else:
|
||||
if original == patched:
|
||||
runs.append((current_run_start, current_run_data))
|
||||
run_in_progress = False
|
||||
else:
|
||||
current_run_data.append(patched)
|
||||
if run_in_progress:
|
||||
runs.append((current_run_start, current_run_data))
|
||||
|
||||
for start, data in runs:
|
||||
if start == int.from_bytes(b'EOF', byteorder='big'):
|
||||
start -= 1
|
||||
data = bytes([patched_data[start - 1]]) + data
|
||||
|
||||
grouped_byte_data = list([
|
||||
{'val': key, 'count': sum(1 for _ in group), 'is_last': False}
|
||||
for key,group in itertools.groupby(data)
|
||||
])
|
||||
|
||||
grouped_byte_data[-1]['is_last'] = True
|
||||
|
||||
record_in_progress = bytearray()
|
||||
pos = start
|
||||
|
||||
for group in grouped_byte_data:
|
||||
if len(record_in_progress) > 0:
|
||||
# We don't want to interrupt a record in progress with a new header unless
|
||||
# this group is longer than two complete headers.
|
||||
if group['count'] > 13:
|
||||
patch.add_record(pos, record_in_progress)
|
||||
pos += len(record_in_progress)
|
||||
record_in_progress = bytearray()
|
||||
|
||||
patch.add_rle_record(pos, bytes([group['val']]), group['count'])
|
||||
pos += group['count']
|
||||
else:
|
||||
record_in_progress += bytes([group['val']] * group['count'])
|
||||
elif (group['count'] > 3 and group['is_last']) or group['count'] > 8:
|
||||
# We benefit from making this an RLE record if the length is at least 8,
|
||||
# or the length is at least 3 and we know it to be the last part of this diff.
|
||||
|
||||
# Make sure not to overflow the maximum length. Split it up if necessary.
|
||||
remaining_length = group['count']
|
||||
while remaining_length > 0xffff:
|
||||
patch.add_rle_record(pos, bytes([group['val']]), 0xffff)
|
||||
remaining_length -= 0xffff
|
||||
pos += 0xffff
|
||||
|
||||
patch.add_rle_record(pos, bytes([group['val']]), remaining_length)
|
||||
pos += remaining_length
|
||||
else:
|
||||
# Just begin a new standard record.
|
||||
record_in_progress += bytes([group['val']] * group['count'])
|
||||
|
||||
if len(record_in_progress) > 0xffff:
|
||||
patch.add_record(pos, record_in_progress[:0xffff])
|
||||
record_in_progress = record_in_progress[0xffff:]
|
||||
pos += 0xffff
|
||||
|
||||
# Finalize any record still in progress.
|
||||
if len(record_in_progress) > 0:
|
||||
patch.add_record(pos, record_in_progress)
|
||||
|
||||
return patch
|
||||
|
||||
def add_record(self, address, data):
|
||||
if address == int.from_bytes(b'EOF', byteorder='big'):
|
||||
raise RuntimeError('Start address {0:x} is invalid in the IPS format. Please shift your starting address back by one byte to avoid it.'.format(address))
|
||||
if address > 0xffffff:
|
||||
raise RuntimeError('Start address {0:x} is too large for the IPS format. Addresses must fit into 3 bytes.'.format(address))
|
||||
if len(data) > 0xffff:
|
||||
raise RuntimeError('Record with length {0} is too large for the IPS format. Records must be less than 65536 bytes.'.format(len(data)))
|
||||
if len(data) == 0: # ignore empty records
|
||||
return
|
||||
record = {'address': address, 'data': data, 'size':len(data)}
|
||||
self.appendRecord(record)
|
||||
|
||||
def add_rle_record(self, address, data, count):
|
||||
if address == int.from_bytes(b'EOF', byteorder='big'):
|
||||
raise RuntimeError('Start address {0:x} is invalid in the IPS format. Please shift your starting address back by one byte to avoid it.'.format(address))
|
||||
if address > 0xffffff:
|
||||
raise RuntimeError('Start address {0:x} is too large for the IPS format. Addresses must fit into 3 bytes.'.format(address))
|
||||
if count > 0xffff:
|
||||
raise RuntimeError('RLE record with length {0} is too large for the IPS format. RLE records must be less than 65536 bytes.'.format(count))
|
||||
if len(data) != 1:
|
||||
raise RuntimeError('Data for RLE record must be exactly one byte! Received {0}.'.format(data))
|
||||
record = {'address': address, 'data': data, 'rle_count': count, 'size': count}
|
||||
self.appendRecord(record)
|
||||
|
||||
def appendRecord(self, record):
|
||||
sz = record['address'] + record['size']
|
||||
if sz > self.max_size:
|
||||
self.max_size = sz
|
||||
self.records.append(record)
|
||||
|
||||
def set_truncate_length(self, truncate_length):
|
||||
self.truncate_length = truncate_length
|
||||
|
||||
def encode(self):
|
||||
encoded_bytes = bytearray()
|
||||
|
||||
encoded_bytes += 'PATCH'.encode('ascii')
|
||||
|
||||
for record in self.records:
|
||||
encoded_bytes += record['address'].to_bytes(3, byteorder='big')
|
||||
if 'rle_count' in record:
|
||||
encoded_bytes += (0).to_bytes(2, byteorder='big')
|
||||
encoded_bytes += record['rle_count'].to_bytes(2, byteorder='big')
|
||||
else:
|
||||
encoded_bytes += len(record['data']).to_bytes(2, byteorder='big')
|
||||
encoded_bytes += record['data']
|
||||
|
||||
encoded_bytes += 'EOF'.encode('ascii')
|
||||
|
||||
if self.truncate_length is not None:
|
||||
encoded_bytes += self.truncate_length.to_bytes(3, byteorder='big')
|
||||
|
||||
return encoded_bytes
|
||||
|
||||
# save patch into IPS file
|
||||
def save(self, path):
|
||||
with open(path, 'wb') as ipsFile:
|
||||
ipsFile.write(self.encode())
|
||||
|
||||
# applies patch on an existing bytearray
|
||||
def apply(self, in_data):
|
||||
out_data = bytearray(in_data)
|
||||
|
||||
for record in self.records:
|
||||
if record['address'] >= len(out_data):
|
||||
out_data += bytes([0] * (record['address'] - len(out_data) + 1))
|
||||
|
||||
if 'rle_count' in record:
|
||||
out_data[record['address'] : record['address'] + record['rle_count']] = b''.join([record['data']] * record['rle_count'])
|
||||
else:
|
||||
out_data[record['address'] : record['address'] + len(record['data'])] = record['data']
|
||||
|
||||
if self.truncate_length is not None:
|
||||
out_data = out_data[:self.truncate_length]
|
||||
|
||||
return out_data
|
||||
|
||||
# applies patch on an opened file
|
||||
def applyFile(self, handle):
|
||||
for record in self.records:
|
||||
handle.seek(record['address'])
|
||||
if 'rle_count' in record:
|
||||
handle.write(bytearray(b'').join([record['data']]) * record['rle_count'])
|
||||
else:
|
||||
handle.write(record['data'])
|
||||
|
||||
# appends an IPS_Patch on top of this one
|
||||
def append(self, patch):
|
||||
if patch.truncate_length is not None and (self.truncate_length is None or patch.truncate_length > self.truncate_length):
|
||||
self.set_truncate_length(patch.truncate_length)
|
||||
for record in patch.records:
|
||||
if record['size'] > 0: # ignore empty records
|
||||
self.appendRecord(record)
|
||||
|
||||
# gets address ranges written to by this patch
|
||||
def getRanges(self):
|
||||
def getRange(record):
|
||||
return range(record['address'], record['address']+record['size'])
|
||||
return range_union([getRange(record) for record in self.records])
|
Loading…
Reference in New Issue