Mega Man Battle Network 3: Implement New Game (#1198)

* Initializes MMBN3 world with empty files

* Adds item names to item dict

* Adds locations and names

* Adds skeleton of MMBN3Client. Mostly copy pasta from OOT

* Fixed some style and formatting

* More incremental Lua tests

* Adds all locations and checking to Lua connector

* Made class definitions for TextPet Parser

* Begun connecting item delivery system through lua and textpet

* Lua Connection can now send test items

* Item Delivery is now parameterized. Test command can send any chip

* Adds the ability to send non-chip items

* Fixes name errors in python client

* Fixes count for zenny, attempts to fix bugfrags

* Fixes an issue where you always received 255 bugfrags

* Converts zenny and bugfrag amounts to little endian bytecode

* Checks game state before sending chips

Adds debug option to display information overlayed on rom
Fixes chip indexing issue for chips with ids over 255
Minor text fixes

* Adds in some animation reset instructions during item get message

* Stores previously collected item index in save, re-sends missing items

* Adds title screen check before sending locations

Loading items from save could not be done via RAM. Had to be added in
assembly

* Adds progressive undernet check

* Added library for lzss decoding bits of rom

* More progress on parsing text events from ROM

* Adds a way to inject messages into ScriptArchive data structure and generate bytecode

* Adds Item definitions, passes to client

* Adds regions and item collection rules

* Touched up a few names and values that have changed in preparation for the final patching

* Modifying messages via item is now successful

* Added generate_output hook to generate ROM data

* Generates ROM successfully

* Fixes navi cust give index

* Whoops forgot to wrap this in brackets

* Injects extra scripts for undernet rankings

* Programs had ammount and color swapped

* Prompts the user for their username when connecting

* Adds flagClear to the list of commands to avoid overwriting

* Fixes message box crashes and several other multiworld issues

* Fixes IDs and names of several items and locations

* Added .gba to gitignore

* Fixes compatibility after recent rebase

* Fixes some locations and items that are otherwise unobtainable

* Attempts to make a working launcher in the installer

* Creates installer and fixes several inaccessible locations

* Many minor changes to items, locations, and requirements made during testing

* Adds an info page for MMBN3

* Fixes failing tests by removing duplicate IDs and properly marking progression items

* Accidentally forgot to un-remove the thing

* Whoops, changed this by accident

* Updates self.world references to self.multiworld

* Fixes imports to use from imports instead of using the namespace

* Removed some leftover merge artifacts from inno setup

* Puts back that darned signtool line again

* Adds Overworld Metro keys as items

* Adds TamaCode and puts shortcuts behind cyber passes

* Fixes Numberman code 16 check

* Fixes metro access logic and adds text to metro

* Reworks Lua to fix crashing when many items are queued

* Items for other BN3 games for different players are no longer given in the main player's ROM as well

* Fixes incorrect Item ID for ACDC Metro

* Fixes multi-box text messages

* Adds timer before sending an item

* Forgot to remove the second box of SubMems

* Updates patch and lua to prevent softlocks and crashes

* Adds options for extra undernet ranks, exclude jobs

* Extra GigFreez now gives 20 bugfrags

* Additional Progressive Undernets can no longer appear on the WWW Base

* Moves item signal byte to empty area of flags instead of end of RAM

* Adds Chocolate Shop locations and navi chips to fill them

* Fixes save crash, and added chocolates to lua

* Fixes chocolate stand selling out text, removes DrillMan cube in Undernet

* Replaces old messaging system with direct memory manipulation for receiving items

* Removes NDSPY requirements from MMBN3 by manually adapting the GBA's lz10 algorithm

* Fixes the names of Hospital-1 Locations

* Adds Canary Bit to avoid sending checks when title screen check fails

* Gaining a cybermetro pass will now open the shortcut immediately

* Randomizes the two accessible areas of Undernet 7, adds Hammer as item

* Adds new locations to connector lua

* Injects the name of the item into trade quests

* Fixes copy-paste error in docs

* Fixes merge artifacts and depracated code

* Nut-wafer stand now faces Lan the right way after buying

* Removes unused Goal Option and updates the readme to include most recent changes

* Touch-ups and formatting changes

* The Great Fillerization update. Dozens of items changed to Filler

* Replaces instances of Mega Man with MegaMan

* Update worlds/mmbn3/docs/en_MegaMan Battle Network 3.md

Co-authored-by: el-u <109771707+el-u@users.noreply.github.com>

* Update worlds/mmbn3/__init__.py

Co-authored-by: el-u <109771707+el-u@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: el-u <109771707+el-u@users.noreply.github.com>
Co-authored-by: SoldierofOrder <107806872+SoldierofOrder@users.noreply.github.com>

* Changes code ordering to suit base class's

* assert_generate now checks for roms. Minor text fixes

* Makes player specific frequency and excluded location options

* Apply suggestions from code review

Co-authored-by: el-u <109771707+el-u@users.noreply.github.com>

* Addresses suggested changes from PR review

* Replaces ndspy lz10 with MIT-compliant nlzss lz10

* apworld compatibility fix for mmbn3_options from utils

* Addressing more comments by el-u

* APworld will now pull patch from zip folder

* Apply suggestions from code review

Co-authored-by: el-u <109771707+el-u@users.noreply.github.com>

* Cleaned up comments for progressive undernet ROM function, moved index list to field to avoid re-initializing

* Removes improper player-indexed location/item dicts, replaces with world member variables

* Avoids redefining list in progressive undernet ROM function

* Filler items can no longer be generated beyond their specified amounts

* Fixes list copying issue with item frequencies

* Adds BN3 Client Generation back into Launcher settings

* Fixes typos causing huge problems

* Fixed non-relative import for apworld

* Removes custom enum implementation that broke pickle

* Displays message when attempting to load an incorrect ROM, will not attempt to patch it

* Filler items can now only be placed once

* Changes path in setup doc to match Lua path changes

* Fixes file extension for MMBN3 file

* Replaces magic number with reference to value in NetUtils

* Moves victory rules to set_rules. Removes commented out code

* Rewrites Lua script to send block of memory

* Fixes off-by-one error in sending bytes for locations

* Fixes issue with invalid characters in text parsing, and WWW monitor text box parsing

* Moves trade text injection to init so it has access to options

* Attempts to split the text boxes for hinted items

* Trade checks now provide hints if the option is set for them

* Fixes escape character issue for BizHawk 2.9.1

Something in Bizhawk lua parsing changed to dislike the escaped tilde.
I'm not even entirely sure why it was escaped in the first place, but
this should fix the compatibility of it.

* Re-adds desk check that it turns out actually does exist

* Updates requirements to mention bizhawk 2.7 instead of 2.3.1

* Fixes off-by-one error in command byte counts

* Fixes program color indices

* Fixes newline PEP violations

* Reverts an accidental whitespace change made to launcher.py

* Fixes URL formatting on link to settings from setup guide

Co-authored-by: Zach Parks <zach@alliware.com>

* Splits several lines in the readme to avoid excessive length

* Fixes formatting and (hopefully) reduces cringe of joke in setup doc

* Removes unnecessary constructor

* Changes item frequency generation to avoid reusing the same references

Co-authored-by: Zach Parks <zach@alliware.com>

---------

Co-authored-by: el-u <109771707+el-u@users.noreply.github.com>
Co-authored-by: SoldierofOrder <107806872+SoldierofOrder@users.noreply.github.com>
Co-authored-by: Zach Parks <zach@alliware.com>
This commit is contained in:
digiholic 2023-06-29 12:36:01 -06:00 committed by GitHub
parent 776b5fab7c
commit 325299286b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 4249 additions and 6 deletions

372
MMBN3Client.py Normal file
View File

@ -0,0 +1,372 @@
import asyncio
import hashlib
import json
import os
import multiprocessing
import subprocess
import zipfile
from asyncio import StreamReader, StreamWriter
import bsdiff4
from CommonClient import CommonContext, server_loop, gui_enabled, \
ClientCommandProcessor, logger, get_base_parser
import Utils
from NetUtils import ClientStatus
from worlds.mmbn3.Items import items_by_id
from worlds.mmbn3.Rom import get_base_rom_path
from worlds.mmbn3.Locations import all_locations, scoutable_locations
SYSTEM_MESSAGE_ID = 0
CONNECTION_TIMING_OUT_STATUS = "Connection timing out. Please restart your emulator, then restart connector_mmbn3.lua"
CONNECTION_REFUSED_STATUS = \
"Connection refused. Please start your emulator and make sure connector_mmbn3.lua is running"
CONNECTION_RESET_STATUS = "Connection was reset. Please restart your emulator, then restart connector_mmbn3.lua"
CONNECTION_TENTATIVE_STATUS = "Initial Connection Made"
CONNECTION_CONNECTED_STATUS = "Connected"
CONNECTION_INITIAL_STATUS = "Connection has not been initiated"
CONNECTION_INCORRECT_ROM = "Supplied Base Rom does not match US GBA Blue Version. Please provide the correct ROM version"
script_version: int = 2
debugEnabled = False
locations_checked = []
items_sent = []
itemIndex = 1
CHECKSUM_BLUE = "6fe31df0144759b34ad666badaacc442"
class MMBN3CommandProcessor(ClientCommandProcessor):
def __init__(self, ctx):
super().__init__(ctx)
def _cmd_gba(self):
"""Check GBA Connection State"""
if isinstance(self.ctx, MMBN3Context):
logger.info(f"GBA Status: {self.ctx.gba_status}")
def _cmd_debug(self):
"""Toggle the Debug Text overlay in ROM"""
global debugEnabled
debugEnabled = not debugEnabled
logger.info("Debug Overlay Enabled" if debugEnabled else "Debug Overlay Disabled")
class MMBN3Context(CommonContext):
command_processor = MMBN3CommandProcessor
game = "MegaMan Battle Network 3"
items_handling = 0b001 # full local
def __init__(self, server_address, password):
super().__init__(server_address, password)
self.gba_streams: (StreamReader, StreamWriter) = None
self.gba_sync_task = None
self.gba_status = CONNECTION_INITIAL_STATUS
self.awaiting_rom = False
self.location_table = {}
self.version_warning = False
self.auth_name = None
self.slot_data = dict()
self.patching_error = False
async def server_auth(self, password_requested: bool = False):
if password_requested and not self.password:
await super(MMBN3Context, self).server_auth(password_requested)
if self.auth_name is None:
self.awaiting_rom = True
logger.info("No ROM detected, awaiting conection to Bizhawk to authenticate to the multiworld server")
return
logger.info("Attempting to decode from ROM... ")
self.awaiting_rom = False
self.auth = self.auth_name.decode("utf8").replace('\x00', '')
logger.info("Connecting as "+self.auth)
await self.send_connect(name=self.auth)
def run_gui(self):
from kvui import GameManager
class MMBN3Manager(GameManager):
logging_pairs = [
("Client", "Archipelago")
]
base_title = "Archipelago MegaMan Battle Network 3 Client"
self.ui = MMBN3Manager(self)
self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI")
def on_package(self, cmd: str, args: dict):
if cmd == 'Connected':
self.slot_data = args.get("slot_data", {})
print(self.slot_data)
class ItemInfo:
id = 0x00
sender = ""
type = ""
count = 1
itemName = "Unknown"
itemID = 0x00 # Item ID, Chip ID, etc.
subItemID = 0x00 # Code for chips, color for programs
itemIndex = 1
def __init__(self, id, sender, type):
self.id = id
self.sender = sender
self.type = type
def get_json(self):
json_data = {
"id": self.id,
"sender": self.sender,
"type": self.type,
"itemName": self.itemName,
"itemID": self.itemID,
"subItemID": self.subItemID,
"count": self.count,
"itemIndex": self.itemIndex
}
return json_data
def get_payload(ctx: MMBN3Context):
global debugEnabled
items_sent = []
for i, item in enumerate(ctx.items_received):
item_data = items_by_id[item.item]
new_item = ItemInfo(i, ctx.player_names[item.player], item_data.type)
new_item.itemIndex = i+1
new_item.itemName = item_data.itemName
new_item.type = item_data.type
new_item.itemID = item_data.itemID
new_item.subItemID = item_data.subItemID
new_item.count = item_data.count
items_sent.append(new_item)
return json.dumps({
"items": [item.get_json() for item in items_sent],
"debug": debugEnabled
})
async def parse_payload(payload: dict, ctx: MMBN3Context, force: bool):
# Game completion handling
if payload["gameComplete"] and not ctx.finished_game:
await ctx.send_msgs([{
"cmd": "StatusUpdate",
"status": ClientStatus.CLIENT_GOAL
}])
ctx.finished_game = True
# Locations handling
if ctx.location_table != payload["locations"]:
ctx.location_table = payload["locations"]
locs = [loc.id for loc in all_locations
if check_location_packet(loc, ctx.location_table)]
await ctx.send_msgs([{
"cmd": "LocationChecks",
"locations": locs
}])
# If trade hinting is enabled, send scout checks
if ctx.slot_data.get("trade_quest_hinting", 0) == 2:
scouted_locs = [loc.id for loc in scoutable_locations
if check_location_scouted(loc, payload["locations"])]
await ctx.send_msgs([{
"cmd": "LocationScouts",
"locations": scouted_locs,
"create_as_hint": 2
}])
def check_location_packet(location, memory):
if len(memory) == 0:
return False
# Our keys have to be strings to come through the JSON lua plugin so we have to turn our memory address into a string as well
location_key = hex(location.flag_byte)[2:]
byte = memory.get(location_key)
if byte is not None:
return byte & location.flag_mask
def check_location_scouted(location, memory):
if len(memory) == 0:
return False
location_key = hex(location.hint_flag)[2:]
byte = memory.get(location_key)
if byte is not None:
return byte & location.hint_flag_mask
async def gba_sync_task(ctx: MMBN3Context):
logger.info("Starting GBA connector. Use /gba for status information.")
if ctx.patching_error:
logger.error('Unable to Patch ROM. No ROM provided or ROM does not match US GBA Blue Version.')
while not ctx.exit_event.is_set():
error_status = None
if ctx.gba_streams:
(reader, writer) = ctx.gba_streams
msg = get_payload(ctx).encode()
writer.write(msg)
writer.write(b'\n')
try:
await asyncio.wait_for(writer.drain(), timeout=1.5)
try:
# Data will return a dict with up to four fields
# 1. str: player name (always)
# 2. int: script version (always)
# 3. dict[str, byte]: value of location's memory byte
# 4. bool: whether the game currently registers as complete
data = await asyncio.wait_for(reader.readline(), timeout=10)
data_decoded = json.loads(data.decode())
reported_version = data_decoded.get("scriptVersion", 0)
if reported_version >= script_version:
if ctx.game is not None and "locations" in data_decoded:
# Not just a keep alive ping, parse
asyncio.create_task((parse_payload(data_decoded, ctx, False)))
if not ctx.auth:
ctx.auth_name = bytes(data_decoded["playerName"])
if ctx.awaiting_rom:
logger.info("Awaiting data from ROM...")
await ctx.server_auth(False)
else:
if not ctx.version_warning:
logger.warning(f"Your Lua script is version {reported_version}, expected {script_version}."
"Please update to the latest version."
"Your connection to the Archipelago server will not be accepted.")
ctx.version_warning = True
except asyncio.TimeoutError:
logger.debug("Read Timed Out, Reconnecting")
error_status = CONNECTION_TIMING_OUT_STATUS
writer.close()
ctx.gba_streams = None
except ConnectionResetError:
logger.debug("Read failed due to Connection Lost, Reconnecting")
error_status = CONNECTION_RESET_STATUS
writer.close()
ctx.gba_streams = None
except TimeoutError:
logger.debug("Connection Timed Out, Reconnecting")
error_status = CONNECTION_TIMING_OUT_STATUS
writer.close()
ctx.gba_streams = None
except ConnectionResetError:
logger.debug("Connection Lost, Reconnecting")
error_status = CONNECTION_RESET_STATUS
writer.close()
ctx.gba_streams = None
if ctx.gba_status == CONNECTION_TENTATIVE_STATUS:
if not error_status:
logger.info("Successfully Connected to GBA")
ctx.gba_status = CONNECTION_CONNECTED_STATUS
else:
ctx.gba_status = f"Was tentatively connected but error occurred: {error_status}"
elif error_status:
ctx.gba_status = error_status
logger.info("Lost connection to GBA and attempting to reconnect. Use /gba for status updates")
else:
try:
logger.debug("Attempting to connect to GBA")
ctx.gba_streams = await asyncio.wait_for(asyncio.open_connection("localhost", 28922), timeout=10)
ctx.gba_status = CONNECTION_TENTATIVE_STATUS
except TimeoutError:
logger.debug("Connection Timed Out, Trying Again")
ctx.gba_status = CONNECTION_TIMING_OUT_STATUS
continue
except ConnectionRefusedError:
logger.debug("Connection Refused, Trying Again")
ctx.gba_status = CONNECTION_REFUSED_STATUS
continue
async def run_game(romfile):
options = Utils.get_options().get("mmbn3_options", None)
if options is None:
auto_start = True
else:
auto_start = options.get("rom_start", True)
if auto_start:
import webbrowser
webbrowser.open(romfile)
elif os.path.isfile(auto_start):
subprocess.Popen([auto_start, romfile],
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
async def patch_and_run_game(apmmbn3_file):
base_name = os.path.splitext(apmmbn3_file)[0]
with zipfile.ZipFile(apmmbn3_file, 'r') as patch_archive:
try:
with patch_archive.open("delta.bsdiff4", 'r') as stream:
patch_data = stream.read()
except KeyError:
raise FileNotFoundError("Patch file missing from archive.")
rom_file = get_base_rom_path()
with open(rom_file, 'rb') as rom:
rom_bytes = rom.read()
patched_bytes = bsdiff4.patch(rom_bytes, patch_data)
patched_rom_file = base_name+".gba"
with open(patched_rom_file, 'wb') as patched_rom:
patched_rom.write(patched_bytes)
asyncio.create_task(run_game(patched_rom_file))
def confirm_checksum():
rom_file = get_base_rom_path()
if not os.path.exists(rom_file):
return False
with open(rom_file, 'rb') as rom:
rom_bytes = rom.read()
basemd5 = hashlib.md5()
basemd5.update(rom_bytes)
return CHECKSUM_BLUE == basemd5.hexdigest()
if __name__ == "__main__":
Utils.init_logging("MMBN3Client")
async def main():
multiprocessing.freeze_support()
parser = get_base_parser()
parser.add_argument("patch_file", default="", type=str, nargs="?",
help="Path to an APMMBN3 file")
args = parser.parse_args()
checksum_matches = confirm_checksum()
if checksum_matches:
if args.patch_file:
asyncio.create_task(patch_and_run_game(args.patch_file))
ctx = MMBN3Context(args.connect, args.password)
if not checksum_matches:
ctx.patching_error = True
ctx.server_task = asyncio.create_task(server_loop(ctx), name="Server Loop")
if gui_enabled:
ctx.run_gui()
ctx.run_cli()
ctx.gba_sync_task = asyncio.create_task(gba_sync_task(ctx), name="GBA Sync")
await ctx.exit_event.wait()
ctx.server_address = None
await ctx.shutdown()
if ctx.gba_sync_task:
await ctx.gba_sync_task
import colorama
colorama.init()
asyncio.run(main())
colorama.deinit()

View File

@ -339,6 +339,10 @@ def get_default_options() -> OptionsType:
"wargroove_options": { "wargroove_options": {
"root_directory": "C:/Program Files (x86)/Steam/steamapps/common/Wargroove" "root_directory": "C:/Program Files (x86)/Steam/steamapps/common/Wargroove"
}, },
"mmbn3_options": {
"rom_file": "Mega Man Battle Network 3 - Blue Version (USA).gba",
"rom_start": True
},
"adventure_options": { "adventure_options": {
"rom_file": "ADVNTURE.BIN", "rom_file": "ADVNTURE.BIN",
"display_msgs": True, "display_msgs": True,

View File

@ -0,0 +1,723 @@
local socket = require("socket")
local json = require('json')
local math = require('math')
require('common')
local last_modified_date = '2023-31-05' -- Should be the last modified date
local script_version = 4
local bizhawk_version = client.getversion()
local bizhawk_major, bizhawk_minor, bizhawk_patch = bizhawk_version:match("(%d+)%.(%d+)%.?(%d*)")
bizhawk_major = tonumber(bizhawk_major)
bizhawk_minor = tonumber(bizhawk_minor)
if bizhawk_patch == "" then
bizhawk_patch = 0
else
bizhawk_patch = tonumber(bizhawk_patch)
end
local STATE_OK = "Ok"
local STATE_TENTATIVELY_CONNECTED = "Tentatively Connected"
local STATE_INITIAL_CONNECTION_MADE = "Initial Connection Made"
local STATE_UNINITIALIZED = "Uninitialized"
local prevstate = ""
local curstate = STATE_UNINITIALIZED
local mmbn3Socket = nil
local frame = 0
-- States
local ITEMSTATE_NONINITIALIZED = "Game Not Yet Started" -- Game has not yet started
local ITEMSTATE_NONITEM = "Non-Itemable State" -- Do not send item now. RAM is not capable of holding
local ITEMSTATE_IDLE = "Item State Ready" -- Ready for the next item if there are any
local ITEMSTATE_SENT = "Item Sent Not Claimed" -- The ItemBit is set, but the dialog has not been closed yet
local itemState = ITEMSTATE_NONINITIALIZED
local itemQueued = nil
local itemQueueCounter = 120
local debugEnabled = false
local game_complete = false
local backup_bytes = nil
local itemsReceived = {}
local previousMessageBit = 0x00
local key_item_start_address = 0x20019C0
-- The Canary Byte is a flag byte that is intentionally left unused. If this byte is FF, then we know the flag
-- data cannot be trusted, so we don't send checks.
local canary_byte = 0x20001A9
local charDict = {
[' ']=0x00,['0']=0x01,['1']=0x02,['2']=0x03,['3']=0x04,['4']=0x05,['5']=0x06,['6']=0x07,['7']=0x08,['8']=0x09,['9']=0x0A,
['A']=0x0B,['B']=0x0C,['C']=0x0D,['D']=0x0E,['E']=0x0F,['F']=0x10,['G']=0x11,['H']=0x12,['I']=0x13,['J']=0x14,['K']=0x15,
['L']=0x16,['M']=0x17,['N']=0x18,['O']=0x19,['P']=0x1A,['Q']=0x1B,['R']=0x1C,['S']=0x1D,['T']=0x1E,['U']=0x1F,['V']=0x20,
['W']=0x21,['X']=0x22,['Y']=0x23,['Z']=0x24,['a']=0x25,['b']=0x26,['c']=0x27,['d']=0x28,['e']=0x29,['f']=0x2A,['g']=0x2B,
['h']=0x2C,['i']=0x2D,['j']=0x2E,['k']=0x2F,['l']=0x30,['m']=0x31,['n']=0x32,['o']=0x33,['p']=0x34,['q']=0x35,['r']=0x36,
['s']=0x37,['t']=0x38,['u']=0x39,['v']=0x3A,['w']=0x3B,['x']=0x3C,['y']=0x3D,['z']=0x3E,['-']=0x3F,['×']=0x40,[']=']=0x41,
[':']=0x42,['+']=0x43,['÷']=0x44,['']=0x45,['*']=0x46,['!']=0x47,['?']=0x48,['%']=0x49,['&']=0x4A,[',']=0x4B,['']=0x4C,
['.']=0x4D,['']=0x4E,[';']=0x4F,['\'']=0x50,['\"']=0x51,['~']=0x52,['/']=0x53,['(']=0x54,[')']=0x55,['']=0x56,['']=0x57,
["[V2]"]=0x58,["[V3]"]=0x59,["[V4]"]=0x5A,["[V5]"]=0x5B,['@']=0x5C,['']=0x5D,['']=0x5E,["[MB]"]=0x5F,['']=0x60,['_']=0x61,
["[circle1]"]=0x62,["[circle2]"]=0x63,["[cross1]"]=0x64,["[cross2]"]=0x65,["[bracket1]"]=0x66,["[bracket2]"]=0x67,["[ModTools1]"]=0x68,
["[ModTools2]"]=0x69,["[ModTools3]"]=0x6A,['Σ']=0x6B,['Ω']=0x6C,['α']=0x6D,['β']=0x6E,['#']=0x6F,['']=0x70,['>']=0x71,
['<']=0x72,['']=0x73,["[BowneGlobal1]"]=0x74,["[BowneGlobal2]"]=0x75,["[BowneGlobal3]"]=0x76,["[BowneGlobal4]"]=0x77,
["[BowneGlobal5]"]=0x78,["[BowneGlobal6]"]=0x79,["[BowneGlobal7]"]=0x7A,["[BowneGlobal8]"]=0x7B,["[BowneGlobal9]"]=0x7C,
["[BowneGlobal10]"]=0x7D,["[BowneGlobal11]"]=0x7E,['\n']=0xE8
}
local TableConcat = function(t1,t2)
for i=1,#t2 do
t1[#t1+1] = t2[i]
end
return t1
end
local int32ToByteList_le = function(x)
bytes = {}
hexString = string.format("%08x", x)
for i=#hexString, 1, -2 do
hbyte = hexString:sub(i-1, i)
table.insert(bytes,tonumber(hbyte,16))
end
return bytes
end
local int16ToByteList_le = function(x)
bytes = {}
hexString = string.format("%04x", x)
for i=#hexString, 1, -2 do
hbyte = hexString:sub(i-1, i)
table.insert(bytes,tonumber(hbyte,16))
end
return bytes
end
local IsInMenu = function()
return bit.band(memory.read_u8(0x0200027A),0x10) ~= 0
end
local IsInTransition = function()
return bit.band(memory.read_u8(0x02001880), 0x10) ~= 0
end
local IsInDialog = function()
return bit.band(memory.read_u8(0x02009480),0x01) ~= 0
end
local IsInBattle = function()
return memory.read_u8(0x020097F8) == 0x08
end
local IsItemQueued = function()
return memory.read_u8(0x2000224) == 0x01
end
-- This function actually determines when you're on ANY full-screen menu (navi cust, link battle, etc.) but we
-- don't want to check any locations there either so it's fine.
local IsOnTitle = function()
return bit.band(memory.read_u8(0x020097F8),0x04) == 0
end
local IsItemable = function()
return not IsInMenu() and not IsInTransition() and not IsInDialog() and not IsInBattle() and not IsOnTitle() and not IsItemQueued()
end
local is_game_complete = function()
if IsOnTitle() or itemState == ITEMSTATE_NONINITIALIZED then return game_complete end
-- If the game is already marked complete, do not read memory
if game_complete then return true end
local is_alpha_defeated = bit.band(memory.read_u8(0x2000433), 0x01) ~= 0
if (is_alpha_defeated) then
game_complete = true
return true
end
-- Game is still ongoing
return false
end
local saveItemIndexToRAM = function(newIndex)
memory.write_s16_le(0x20000AE,newIndex)
end
local loadItemIndexFromRAM = function()
last_index = memory.read_s16_le(0x20000AE)
if (last_index < 0) then
last_index = 0
saveItemIndexToRAM(0)
end
return last_index
end
local loadPlayerNameFromROM = function()
return memory.read_bytes_as_array(0x7FFFC0,63,"ROM")
end
local check_all_locations = function()
local location_checks = {}
-- Title Screen should not check items
if itemState == ITEMSTATE_NONINITIALIZED or IsInTransition() then
return location_checks
end
if memory.read_u8(canary_byte) == 0xFF then
return location_checks
end
for k,v in pairs(memory.read_bytes_as_dict(0x02000000, 0x434)) do
str_k = string.format("%x", k)
location_checks[str_k] = v
end
return location_checks
end
local Check_Progressive_Undernet_ID = function()
ordered_offsets = { 0x020019DB,0x020019DC,0x020019DD,0x020019DE,0x020019DF,0x020019E0,0x020019FA,0x020019E2 }
for i=1,#ordered_offsets do
offset=ordered_offsets[i]
if memory.read_u8(offset) == 0 then
return i
end
end
return 9
end
local GenerateTextBytes = function(message)
bytes = {}
for i = 1, #message do
local c = message:sub(i,i)
table.insert(bytes, charDict[c])
end
return bytes
end
-- Item Message Generation functions
local Next_Progressive_Undernet_ID = function(index)
ordered_IDs = { 27,28,29,30,31,32,58,34}
if index > #ordered_IDs then
--It shouldn't reach this point, but if it does, just give another GigFreez I guess
return 34
end
item_index=ordered_IDs[index]
return item_index
end
local Extra_Progressive_Undernet = function()
fragBytes = int32ToByteList_le(20)
bytes = {
0xF6, 0x50, fragBytes[1], fragBytes[2], fragBytes[3], fragBytes[4], 0xFF, 0xFF, 0xFF
}
bytes = TableConcat(bytes, GenerateTextBytes("The extra data\ndecompiles into:\n\"20 BugFrags\"!!"))
return bytes
end
local GenerateChipGet = function(chip, code, amt)
chipBytes = int16ToByteList_le(chip)
bytes = {
0xF6, 0x10, chipBytes[1], chipBytes[2], code, amt,
charDict['G'], charDict['o'], charDict['t'], charDict[' '], charDict['a'], charDict[' '], charDict['c'], charDict['h'], charDict['i'], charDict['p'], charDict[' '], charDict['f'], charDict['o'], charDict['r'], charDict['\n'],
}
if chip < 256 then
bytes = TableConcat(bytes, {
charDict['\"'], 0xF9,0x00,chipBytes[1],0x01,0x00,0xF9,0x00,code,0x03, charDict['\"'],charDict['!'],charDict['!']
})
else
bytes = TableConcat(bytes, {
charDict['\"'], 0xF9,0x00,chipBytes[1],0x02,0x00,0xF9,0x00,code,0x03, charDict['\"'],charDict['!'],charDict['!']
})
end
return bytes
end
local GenerateKeyItemGet = function(item, amt)
bytes = {
0xF6, 0x00, item, amt,
charDict['G'], charDict['o'], charDict['t'], charDict[' '], charDict['a'], charDict['\n'],
charDict['\"'], 0xF9, 0x00, item, 0x00, charDict['\"'],charDict['!'],charDict['!']
}
return bytes
end
local GenerateSubChipGet = function(subchip, amt)
-- SubChips have an extra bit of trouble. If you have too many, they're supposed to skip to another text bank that doesn't give you the item
-- Instead, I'm going to just let it get eaten
bytes = {
0xF6, 0x20, subchip, amt, 0xFF, 0xFF, 0xFF,
charDict['G'], charDict['o'], charDict['t'], charDict[' '], charDict['a'], charDict['\n'],
charDict['S'], charDict['u'], charDict['b'], charDict['C'], charDict['h'], charDict['i'], charDict['p'], charDict[' '], charDict['f'], charDict['o'], charDict['r'], charDict['\n'],
charDict['\"'], 0xF9, 0x00, subchip, 0x00, charDict['\"'],charDict['!'],charDict['!']
}
return bytes
end
local GenerateZennyGet = function(amt)
zennyBytes = int32ToByteList_le(amt)
bytes = {
0xF6, 0x30, zennyBytes[1], zennyBytes[2], zennyBytes[3], zennyBytes[4], 0xFF, 0xFF, 0xFF,
charDict['G'], charDict['o'], charDict['t'], charDict[' '], charDict['a'], charDict['\n'], charDict['\"']
}
-- The text needs to be added one char at a time, so we need to convert the number to a string then iterate through it
zennyStr = tostring(amt)
for i = 1, #zennyStr do
local c = zennyStr:sub(i,i)
table.insert(bytes, charDict[c])
end
bytes = TableConcat(bytes, {
charDict[' '], charDict['Z'], charDict['e'], charDict['n'], charDict['n'], charDict['y'], charDict['s'], charDict['\"'],charDict['!'],charDict['!']
})
return bytes
end
local GenerateProgramGet = function(program, color, amt)
bytes = {
0xF6, 0x40, (program * 4), amt, color,
charDict['G'], charDict['o'], charDict['t'], charDict[' '], charDict['a'], charDict[' '], charDict['N'], charDict['a'], charDict['v'], charDict['i'], charDict['\n'],
charDict['C'], charDict['u'], charDict['s'], charDict['t'], charDict['o'], charDict['m'], charDict['i'], charDict['z'], charDict['e'], charDict['r'], charDict[' '], charDict['P'], charDict['r'], charDict['o'], charDict['g'], charDict['r'], charDict['a'], charDict['m'], charDict[':'], charDict['\n'],
charDict['\"'], 0xF9, 0x00, program, 0x05, charDict['\"'],charDict['!'],charDict['!']
}
return bytes
end
local GenerateBugfragGet = function(amt)
fragBytes = int32ToByteList_le(amt)
bytes = {
0xF6, 0x50, fragBytes[1], fragBytes[2], fragBytes[3], fragBytes[4], 0xFF, 0xFF, 0xFF,
charDict['G'], charDict['o'], charDict['t'], charDict[':'], charDict['\n'], charDict['\"']
}
-- The text needs to be added one char at a time, so we need to convert the number to a string then iterate through it
bugFragStr = tostring(amt)
for i = 1, #bugFragStr do
local c = bugFragStr:sub(i,i)
table.insert(bytes, charDict[c])
end
bytes = TableConcat(bytes, {
charDict[' '], charDict['B'], charDict['u'], charDict['g'], charDict['F'], charDict['r'], charDict['a'], charDict['g'], charDict['s'], charDict['\"'],charDict['!'],charDict['!']
})
return bytes
end
local GenerateGetMessageFromItem = function(item)
--Special case for progressive undernet
if item["type"] == "undernet" then
undernet_id = Check_Progressive_Undernet_ID()
if undernet_id > 8 then
return Extra_Progressive_Undernet()
end
return GenerateKeyItemGet(Next_Progressive_Undernet_ID(undernet_id),1)
elseif item["type"] == "chip" then
return GenerateChipGet(item["itemID"], item["subItemID"], item["count"])
elseif item["type"] == "key" then
return GenerateKeyItemGet(item["itemID"], item["count"])
elseif item["type"] == "subchip" then
return GenerateSubChipGet(item["itemID"], item["count"])
elseif item["type"] == "zenny" then
return GenerateZennyGet(item["count"])
elseif item["type"] == "program" then
return GenerateProgramGet(item["itemID"], item["subItemID"], item["count"])
elseif item["type"] == "bugfrag" then
return GenerateBugfragGet(item["count"])
end
return GenerateTextBytes("Empty Message")
end
local GetMessage = function(item)
startBytes = {0x02, 0x00}
playerLockBytes = {0xF8,0x00, 0xF8, 0x10}
msgOpenBytes = {0xF1, 0x02}
textBytes = GenerateTextBytes("Receiving\ndata from\n"..item["sender"]..".")
dotdotWaitBytes = {0xEA,0x00,0x0A,0x00,0x4D,0xEA,0x00,0x0A,0x00,0x4D}
continueBytes = {0xEB, 0xE9}
-- continueBytes = {0xE9}
playReceiveAnimationBytes = {0xF8,0x04,0x18}
chipGiveBytes = GenerateGetMessageFromItem(item)
playerFinishBytes = {0xF8, 0x0C}
playerUnlockBytes={0xEB, 0xF8, 0x08}
-- playerUnlockBytes={0xF8, 0x08}
endMessageBytes = {0xF8, 0x10, 0xE7}
bytes = {}
bytes = TableConcat(bytes,startBytes)
bytes = TableConcat(bytes,playerLockBytes)
bytes = TableConcat(bytes,msgOpenBytes)
bytes = TableConcat(bytes,textBytes)
bytes = TableConcat(bytes,dotdotWaitBytes)
bytes = TableConcat(bytes,continueBytes)
bytes = TableConcat(bytes,playReceiveAnimationBytes)
bytes = TableConcat(bytes,chipGiveBytes)
bytes = TableConcat(bytes,playerFinishBytes)
bytes = TableConcat(bytes,playerUnlockBytes)
bytes = TableConcat(bytes,endMessageBytes)
return bytes
end
local getChipCodeIndex = function(chip_id, chip_code)
chipCodeArrayStartAddress = 0x8011510 + (0x20 * chip_id)
for i=1,6 do
currentCode = memory.read_u8(chipCodeArrayStartAddress + (i-1))
if currentCode == chip_code then
return i-1
end
end
return 0
end
local getProgramColorIndex = function(program_id, program_color)
-- The general case, most programs use white pink or yellow. This is the values the enums already have
if program_id >= 23 and program_id <= 47 then
return program_color-1
end
--The final three programs only have a color index 0, so just return those
if program_id > 47 then
return 0
end
--BrakChrg as an AP item only comes in orange, index 0
if program_id == 3 then
return 0
end
-- every other AP obtainable program returns only color index 3
return 3
end
local addChip = function(chip_id, chip_code, amount)
chipStartAddress = 0x02001F60
chipOffset = 0x12 * chip_id
chip_code_index = getChipCodeIndex(chip_id, chip_code)
currentChipAddress = chipStartAddress + chipOffset + chip_code_index
currentChipCount = memory.read_u8(currentChipAddress)
memory.write_u8(currentChipAddress,currentChipCount+amount)
end
local addProgram = function(program_id, program_color, amount)
programStartAddress = 0x02001A80
programOffset = 0x04 * program_id
program_code_index = getProgramColorIndex(program_id, program_color)
currentProgramAddress = programStartAddress + programOffset + program_code_index
currentProgramCount = memory.read_u8(currentProgramAddress)
memory.write_u8(currentProgramAddress, currentProgramCount+amount)
end
local addSubChip = function(subchip_id, amount)
subChipStartAddress = 0x02001A30
--SubChip indices start after the key items, so subtract 112 from the index to get the actual subchip index
currentSubChipAddress = subChipStartAddress + (subchip_id - 112)
currentSubChipCount = memory.read_u8(currentSubChipAddress)
--TODO check submem, reject if number too big
memory.write_u8(currentSubChipAddress, currentSubChipCount+amount)
end
local changeZenny = function(val)
if val == nil then
return 0
end
if memory.read_u32_le(0x20018F4) <= math.abs(tonumber(val)) and tonumber(val) < 0 then
memory.write_u32_le(0x20018f4, 0)
val = 0
return "empty"
end
memory.write_u32_le(0x20018f4, memory.read_u32_le(0x20018F4) + tonumber(val))
if memory.read_u32_le(0x20018F4) > 999999 then
memory.write_u32_le(0x20018F4, 999999)
end
return val
end
local changeFrags = function(val)
if val == nil then
return 0
end
if memory.read_u16_le(0x20018F8) <= math.abs(tonumber(val)) and tonumber(val) < 0 then
memory.write_u16_le(0x20018f8, 0)
val = 0
return "empty"
end
memory.write_u16_le(0x20018f8, memory.read_u16_le(0x20018F8) + tonumber(val))
if memory.read_u16_le(0x20018F8) > 9999 then
memory.write_u16_le(0x20018F8, 9999)
end
return val
end
-- Fix Health Pools
local fix_hp = function()
-- Current Health fix
if IsInBattle() and not (memory.read_u16_le(0x20018A0) == memory.read_u16_le(0x2037294)) then
memory.write_u16_le(0x20018A0, memory.read_u16_le(0x2037294))
end
-- Max Health Fix
if IsInBattle() and not (memory.read_u16_le(0x20018A2) == memory.read_u16_le(0x2037296)) then
memory.write_u16_le(0x20018A2, memory.read_u16_le(0x2037296))
end
end
local changeRegMemory = function(amt)
regMemoryAddress = 0x02001897
currentRegMem = memory.read_u8(regMemoryAddress)
memory.write_u8(regMemoryAddress, currentRegMem + amt)
end
local changeMaxHealth = function(val)
fix_hp()
if val == nil then
fix_hp()
return 0
end
if math.abs(tonumber(val)) >= memory.read_u16_le(0x20018A2) and tonumber(val) < 0 then
memory.write_u16_le(0x20018A2, 0)
if IsInBattle() then
memory.write_u16_le(0x2037296, memory.read_u16_le(0x20018A2))
if memory.read_u16_le(0x2037296) >= memory.read_u16_le(0x20018A2) then
memory.write_u16_le(0x2037296, memory.read_u16_le(0x20018A2))
end
end
fix_hp()
return "lethal"
end
memory.write_u16_le(0x20018A2, memory.read_u16_le(0x20018A2) + tonumber(val))
if memory.read_u16_le(0x20018A2) > 9999 then
memory.write_u16_le(0x20018A2, 9999)
end
if IsInBattle() then
memory.write_u16_le(0x2037296, memory.read_u16_le(0x20018A2))
end
fix_hp()
return val
end
local SendItem = function(item)
if item["type"] == "undernet" then
undernet_id = Check_Progressive_Undernet_ID()
if undernet_id > 8 then
-- Generate Extra BugFrags
changeFrags(20)
gui.addmessage("Receiving extra Undernet Rank from "..item["sender"]..", +20 BugFrags")
-- print("Receiving extra Undernet Rank from "..item["sender"]..", +20 BugFrags")
else
itemAddress = key_item_start_address + Next_Progressive_Undernet_ID(undernet_id)
itemCount = memory.read_u8(itemAddress)
itemCount = itemCount + item["count"]
memory.write_u8(itemAddress, itemCount)
gui.addmessage("Received Undernet Rank from player "..item["sender"])
-- print("Received Undernet Rank from player "..item["sender"])
end
elseif item["type"] == "chip" then
addChip(item["itemID"], item["subItemID"], item["count"])
gui.addmessage("Received Chip "..item["itemName"].." from player "..item["sender"])
-- print("Received Chip "..item["itemName"].." from player "..item["sender"])
elseif item["type"] == "key" then
itemAddress = key_item_start_address + item["itemID"]
itemCount = memory.read_u8(itemAddress)
itemCount = itemCount + item["count"]
memory.write_u8(itemAddress, itemCount)
-- HPMemory will increase the internal counter but not actually increase the HP. If the item is one of those, do that
if item["itemID"] == 96 then
changeMaxHealth(20)
end
-- Same for the RegUps, but there's three of those
if item["itemID"] == 98 then
changeRegMemory(1)
end
if item["itemID"] == 99 then
changeRegMemory(2)
end
if item["itemID"] == 100 then
changeRegMemory(3)
end
gui.addmessage("Received Key Item "..item["itemName"].." from player "..item["sender"])
-- print("Received Key Item "..item["itemName"].." from player "..item["sender"])
elseif item["type"] == "subchip" then
addSubChip(item["itemID"], item["count"])
gui.addmessage("Received SubChip "..item["itemName"].." from player "..item["sender"])
-- print("Received SubChip "..item["itemName"].." from player "..item["sender"])
elseif item["type"] == "zenny" then
changeZenny(item["count"])
gui.addmessage("Received "..item["count"].."z from "..item["sender"])
-- print("Received "..item["count"].."z from "..item["sender"])
elseif item["type"] == "program" then
addProgram(item["itemID"], item["subItemID"], item["count"])
gui.addmessage("Received Program "..item["itemName"].." from player "..item["sender"])
-- print("Received Program "..item["itemName"].." from player "..item["sender"])
elseif item["type"] == "bugfrag" then
changeFrags(item["count"])
gui.addmessage("Received "..item["count"].." BugFrag(s) from "..item["sender"])
-- print("Received "..item["count"].." BugFrag(s) from "..item["sender"])
end
end
-- Set the flags for opening the shortcuts as soon as the Cybermetro passes are received to save having to check email
local OpenShortcuts = function()
if (memory.read_u8(key_item_start_address + 92) > 0) then
memory.write_u8(0x2000032, bit.bor(memory.read_u8(0x2000032),0x10))
end
-- if CSciPass
if (memory.read_u8(key_item_start_address + 93) > 0) then
memory.write_u8(0x2000032, bit.bor(memory.read_u8(0x2000032),0x08))
end
if (memory.read_u8(key_item_start_address + 94) > 0) then
memory.write_u8(0x2000032, bit.bor(memory.read_u8(0x2000032),0x20))
end
if (memory.read_u8(key_item_start_address + 95) > 0) then
memory.write_u8(0x2000032, bit.bor(memory.read_u8(0x2000032),0x40))
end
end
local RestoreItemRam = function()
if backup_bytes ~= nil then
memory.write_bytes_as_array(0x203fe10, backup_bytes)
end
backup_bytes = nil
end
local process_block = function(block)
-- Sometimes the block is nothing, if this is the case then quietly stop processing
if block == nil then
return
end
debugEnabled = block['debug']
-- Queue item for receiving, if one exists
if (itemsReceived ~= block['items']) then
itemsReceived = block['items']
end
return
end
local itemStateMachineProcess = function()
if itemState == ITEMSTATE_NONINITIALIZED then
itemQueueCounter = 120
-- Only exit this state the first time a dialog window pops up. This way we know for sure that we're ready to receive
if not IsInMenu() and (IsInDialog() or IsInTransition()) then
itemState = ITEMSTATE_NONITEM
end
elseif itemState == ITEMSTATE_NONITEM then
itemQueueCounter = 120
-- Always attempt to restore the previously stored memory in this state
-- Exit this state whenever the game is in an itemable status
if IsItemable() then
itemState = ITEMSTATE_IDLE
end
elseif itemState == ITEMSTATE_IDLE then
-- Remain Idle until an item is sent or we enter a non itemable status
if not IsItemable() then
itemState = ITEMSTATE_NONITEM
end
if itemQueueCounter == 0 then
if #itemsReceived > loadItemIndexFromRAM() and not IsItemQueued() then
itemQueued = itemsReceived[loadItemIndexFromRAM()+1]
SendItem(itemQueued)
itemState = ITEMSTATE_SENT
end
else
itemQueueCounter = itemQueueCounter - 1
end
elseif itemState == ITEMSTATE_SENT then
-- Once the item is sent, wait for the dialog to close. Then clear the item bit and be ready for the next item.
if IsInTransition() or IsInMenu() or IsOnTitle() then
itemState = ITEMSTATE_NONITEM
itemQueued = nil
RestoreItemRam()
elseif not IsInDialog() then
itemState = ITEMSTATE_IDLE
saveItemIndexToRAM(itemQueued["itemIndex"])
itemQueued = nil
RestoreItemRam()
end
end
end
local receive = function()
l, e = mmbn3Socket:receive()
-- Handle incoming message
if e == 'closed' then
if curstate == STATE_OK then
print("Connection closed")
end
curstate = STATE_UNINITIALIZED
return
elseif e == 'timeout' then
print("timeout")
return
elseif e ~= nil then
print(e)
curstate = STATE_UNINITIALIZED
return
end
process_block(json.decode(l))
end
local send = function()
-- Determine message to send back
local retTable = {}
retTable["playerName"] = loadPlayerNameFromROM()
retTable["scriptVersion"] = script_version
retTable["locations"] = check_all_locations()
retTable["gameComplete"] = is_game_complete()
-- Send the message
msg = json.encode(retTable).."\n"
local ret, error = mmbn3Socket:send(msg)
if ret == nil then
print(error)
elseif curstate == STATE_INITIAL_CONNECTION_MADE then
curstate = STATE_TENTATIVELY_CONNECTED
elseif curstate == STATE_TENTATIVELY_CONNECTED then
print("Connected!")
curstate = STATE_OK
end
end
function main()
if (bizhawk_major > 2 or (bizhawk_major == 2 and bizhawk_minor >= 7)==false) then
print("Must use a version of bizhawk 2.7.0 or higher")
return
end
server, error = socket.bind('localhost', 28922)
while true do
frame = frame + 1
if not (curstate == prevstate) then
prevstate = curstate
end
itemStateMachineProcess()
if (curstate == STATE_OK) or (curstate == STATE_INITIAL_CONNECTION_MADE) or (curstate == STATE_TENTATIVELY_CONNECTED) then
-- If we're connected and everything's fine, receive and send data from the network
if (frame % 60 == 0) then
receive()
send()
-- Perform utility functions which read and write data but aren't directly related to checks
OpenShortcuts()
end
elseif (curstate == STATE_UNINITIALIZED) then
-- If we're uninitialized, attempt to make the connection.
if (frame % 120 == 0) then
server:settimeout(2)
local client, timeout = server:accept()
if timeout == nil then
print('Initial Connection Made')
curstate = STATE_INITIAL_CONNECTION_MADE
mmbn3Socket = client
mmbn3Socket:settimeout(0)
else
print('Connection failed, ensure MMBN3Client is running and rerun connector_mmbn3.lua')
return
end
end
end
-- Handle the debug data display
gui.cleartext()
if debugEnabled then
-- gui.text(0,0,"Item Queued: "..tostring(IsItemQueued()))
-- gui.text(0,16,"In Battle: "..tostring(IsInBattle()))
-- gui.text(0,32,"In Dialog: "..tostring(IsInDialog()))
-- gui.text(0,48,"In Menu: "..tostring(IsInMenu()))
gui.text(0,48,"Item Wait Time: "..tostring(itemQueueCounter))
gui.text(0,64,itemState)
if itemQueued == nil then
gui.text(0,80,"No item queued")
else
gui.text(0,80,itemQueued["type"].." "..itemQueued["itemID"])
end
gui.text(0,96,"Item Index: "..loadItemIndexFromRAM())
end
emu.frameadvance()
end
end
main()

View File

@ -167,7 +167,10 @@ zillion_options:
# RetroArch doesn't make it easy to launch a game from the command line. # RetroArch doesn't make it easy to launch a game from the command line.
# You have to know the path to the emulator core library on the user's computer. # You have to know the path to the emulator core library on the user's computer.
rom_start: "retroarch" rom_start: "retroarch"
mmbn3_options:
# File name of the MMBN3 Blue US rom
rom_file: "Mega Man Battle Network 3 - Blue Version (USA).gba"
rom_start: true
adventure_options: adventure_options:
# File name of the standard NTSC Adventure rom. # File name of the standard NTSC Adventure rom.
# The licensed "The 80 Classic Games" CD-ROM contains this. # The licensed "The 80 Classic Games" CD-ROM contains this.
@ -185,7 +188,3 @@ adventure_options:
rom_args: " " rom_args: " "
# Set this to true to display item received messages in Emuhawk # Set this to true to display item received messages in Emuhawk
display_msgs: true display_msgs: true

View File

@ -63,6 +63,7 @@ Name: "generator/oot"; Description: "Ocarina of Time ROM Setup"; Types: full
Name: "generator/zl"; Description: "Zillion ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 150000; Flags: disablenouninstallwarning Name: "generator/zl"; Description: "Zillion ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 150000; Flags: disablenouninstallwarning
Name: "generator/pkmn_r"; Description: "Pokemon Red ROM Setup"; Types: full hosting Name: "generator/pkmn_r"; Description: "Pokemon Red ROM Setup"; Types: full hosting
Name: "generator/pkmn_b"; Description: "Pokemon Blue ROM Setup"; Types: full hosting Name: "generator/pkmn_b"; Description: "Pokemon Blue ROM Setup"; Types: full hosting
Name: "generator/mmbn3"; Description: "MegaMan Battle Network 3"; Types: full hosting; ExtraDiskSpaceRequired: 8388608; Flags: disablenouninstallwarning
Name: "generator/ladx"; Description: "Link's Awakening DX ROM Setup"; Types: full hosting Name: "generator/ladx"; Description: "Link's Awakening DX ROM Setup"; Types: full hosting
Name: "generator/tloz"; Description: "The Legend of Zelda ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 135168; Flags: disablenouninstallwarning Name: "generator/tloz"; Description: "The Legend of Zelda ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 135168; Flags: disablenouninstallwarning
Name: "server"; Description: "Server"; Types: full hosting Name: "server"; Description: "Server"; Types: full hosting
@ -81,6 +82,7 @@ Name: "client/ff1"; Description: "Final Fantasy 1"; Types: full playing
Name: "client/pkmn"; Description: "Pokemon Client" Name: "client/pkmn"; Description: "Pokemon Client"
Name: "client/pkmn/red"; Description: "Pokemon Client - Pokemon Red Setup"; Types: full playing; ExtraDiskSpaceRequired: 1048576 Name: "client/pkmn/red"; Description: "Pokemon Client - Pokemon Red Setup"; Types: full playing; ExtraDiskSpaceRequired: 1048576
Name: "client/pkmn/blue"; Description: "Pokemon Client - Pokemon Blue Setup"; Types: full playing; ExtraDiskSpaceRequired: 1048576 Name: "client/pkmn/blue"; Description: "Pokemon Client - Pokemon Blue Setup"; Types: full playing; ExtraDiskSpaceRequired: 1048576
Name: "client/mmbn3"; Description: "MegaMan Battle Network 3 Client"; Types: full playing;
Name: "client/ladx"; Description: "Link's Awakening Client"; Types: full playing; ExtraDiskSpaceRequired: 1048576 Name: "client/ladx"; Description: "Link's Awakening Client"; Types: full playing; ExtraDiskSpaceRequired: 1048576
Name: "client/cf"; Description: "ChecksFinder"; Types: full playing Name: "client/cf"; Description: "ChecksFinder"; Types: full playing
Name: "client/sc2"; Description: "Starcraft 2"; Types: full playing Name: "client/sc2"; Description: "Starcraft 2"; Types: full playing
@ -105,6 +107,7 @@ Source: "{code:GetOoTROMPath}"; DestDir: "{app}"; DestName: "The Legend of Zelda
Source: "{code:GetZlROMPath}"; DestDir: "{app}"; DestName: "Zillion (UE) [!].sms"; Flags: external; Components: client/zl or generator/zl Source: "{code:GetZlROMPath}"; DestDir: "{app}"; DestName: "Zillion (UE) [!].sms"; Flags: external; Components: client/zl or generator/zl
Source: "{code:GetRedROMPath}"; DestDir: "{app}"; DestName: "Pokemon Red (UE) [S][!].gb"; Flags: external; Components: client/pkmn/red or generator/pkmn_r Source: "{code:GetRedROMPath}"; DestDir: "{app}"; DestName: "Pokemon Red (UE) [S][!].gb"; Flags: external; Components: client/pkmn/red or generator/pkmn_r
Source: "{code:GetBlueROMPath}"; DestDir: "{app}"; DestName: "Pokemon Blue (UE) [S][!].gb"; Flags: external; Components: client/pkmn/blue or generator/pkmn_b Source: "{code:GetBlueROMPath}"; DestDir: "{app}"; DestName: "Pokemon Blue (UE) [S][!].gb"; Flags: external; Components: client/pkmn/blue or generator/pkmn_b
Source: "{code:GetBN3ROMPath}"; DestDir: "{app}"; DestName: "Mega Man Battle Network 3 - Blue Version (USA).gba"; Flags: external; Components: client/mmbn3
Source: "{code:GetLADXROMPath}"; DestDir: "{app}"; DestName: "Legend of Zelda, The - Link's Awakening DX (USA, Europe) (SGB Enhanced).gbc"; Flags: external; Components: client/ladx or generator/ladx Source: "{code:GetLADXROMPath}"; DestDir: "{app}"; DestName: "Legend of Zelda, The - Link's Awakening DX (USA, Europe) (SGB Enhanced).gbc"; Flags: external; Components: client/ladx or generator/ladx
Source: "{code:GetTLoZROMPath}"; DestDir: "{app}"; DestName: "Legend of Zelda, The (U) (PRG0) [!].nes"; Flags: external; Components: client/tloz or generator/tloz Source: "{code:GetTLoZROMPath}"; DestDir: "{app}"; DestName: "Legend of Zelda, The (U) (PRG0) [!].nes"; Flags: external; Components: client/tloz or generator/tloz
Source: "{code:GetAdvnROMPath}"; DestDir: "{app}"; DestName: "ADVNTURE.BIN"; Flags: external; Components: client/advn Source: "{code:GetAdvnROMPath}"; DestDir: "{app}"; DestName: "ADVNTURE.BIN"; Flags: external; Components: client/advn
@ -128,6 +131,7 @@ Source: "{#source_path}\ArchipelagoFF1Client.exe"; DestDir: "{app}"; Flags: igno
Source: "{#source_path}\ArchipelagoPokemonClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/pkmn Source: "{#source_path}\ArchipelagoPokemonClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/pkmn
Source: "{#source_path}\ArchipelagoChecksFinderClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/cf Source: "{#source_path}\ArchipelagoChecksFinderClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/cf
Source: "{#source_path}\ArchipelagoStarcraft2Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sc2 Source: "{#source_path}\ArchipelagoStarcraft2Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sc2
Source: "{#source_path}\ArchipelagoMMBN3Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/mmbn3
Source: "{#source_path}\ArchipelagoZelda1Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/tloz Source: "{#source_path}\ArchipelagoZelda1Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/tloz
Source: "{#source_path}\ArchipelagoWargrooveClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/wargroove Source: "{#source_path}\ArchipelagoWargrooveClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/wargroove
Source: "{#source_path}\ArchipelagoKH2Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/kh2 Source: "{#source_path}\ArchipelagoKH2Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/kh2
@ -148,6 +152,7 @@ Name: "{group}\{#MyAppName} Final Fantasy 1 Client"; Filename: "{app}\Archipelag
Name: "{group}\{#MyAppName} Pokemon Client"; Filename: "{app}\ArchipelagoPokemonClient.exe"; Components: client/pkmn Name: "{group}\{#MyAppName} Pokemon Client"; Filename: "{app}\ArchipelagoPokemonClient.exe"; Components: client/pkmn
Name: "{group}\{#MyAppName} ChecksFinder Client"; Filename: "{app}\ArchipelagoChecksFinderClient.exe"; Components: client/cf Name: "{group}\{#MyAppName} ChecksFinder Client"; Filename: "{app}\ArchipelagoChecksFinderClient.exe"; Components: client/cf
Name: "{group}\{#MyAppName} Starcraft 2 Client"; Filename: "{app}\ArchipelagoStarcraft2Client.exe"; Components: client/sc2 Name: "{group}\{#MyAppName} Starcraft 2 Client"; Filename: "{app}\ArchipelagoStarcraft2Client.exe"; Components: client/sc2
Name: "{group}\{#MyAppName} MegaMan Battle Network 3 Client"; Filename: "{app}\ArchipelagoMMBN3Client.exe"; Components: client/mmbn3
Name: "{group}\{#MyAppName} The Legend of Zelda Client"; Filename: "{app}\ArchipelagoZelda1Client.exe"; Components: client/tloz Name: "{group}\{#MyAppName} The Legend of Zelda Client"; Filename: "{app}\ArchipelagoZelda1Client.exe"; Components: client/tloz
Name: "{group}\{#MyAppName} Kingdom Hearts 2 Client"; Filename: "{app}\ArchipelagoKH2Client.exe"; Components: client/kh2 Name: "{group}\{#MyAppName} Kingdom Hearts 2 Client"; Filename: "{app}\ArchipelagoKH2Client.exe"; Components: client/kh2
Name: "{group}\{#MyAppName} Adventure Client"; Filename: "{app}\ArchipelagoAdventureClient.exe"; Components: client/advn Name: "{group}\{#MyAppName} Adventure Client"; Filename: "{app}\ArchipelagoAdventureClient.exe"; Components: client/advn
@ -165,6 +170,7 @@ Name: "{commondesktop}\{#MyAppName} Final Fantasy 1 Client"; Filename: "{app}\Ar
Name: "{commondesktop}\{#MyAppName} Pokemon Client"; Filename: "{app}\ArchipelagoPokemonClient.exe"; Tasks: desktopicon; Components: client/pkmn Name: "{commondesktop}\{#MyAppName} Pokemon Client"; Filename: "{app}\ArchipelagoPokemonClient.exe"; Tasks: desktopicon; Components: client/pkmn
Name: "{commondesktop}\{#MyAppName} ChecksFinder Client"; Filename: "{app}\ArchipelagoChecksFinderClient.exe"; Tasks: desktopicon; Components: client/cf Name: "{commondesktop}\{#MyAppName} ChecksFinder Client"; Filename: "{app}\ArchipelagoChecksFinderClient.exe"; Tasks: desktopicon; Components: client/cf
Name: "{commondesktop}\{#MyAppName} Starcraft 2 Client"; Filename: "{app}\ArchipelagoStarcraft2Client.exe"; Tasks: desktopicon; Components: client/sc2 Name: "{commondesktop}\{#MyAppName} Starcraft 2 Client"; Filename: "{app}\ArchipelagoStarcraft2Client.exe"; Tasks: desktopicon; Components: client/sc2
Name: "{commondesktop}\{#MyAppName} MegaMan Battle Network 3 Client"; Filename: "{app}\ArchipelagoMMBN3Client.exe"; Tasks: desktopicon; Components: client/mmbn3
Name: "{commondesktop}\{#MyAppName} The Legend of Zelda Client"; Filename: "{app}\ArchipelagoZelda1Client.exe"; Tasks: desktopicon; Components: client/tloz Name: "{commondesktop}\{#MyAppName} The Legend of Zelda Client"; Filename: "{app}\ArchipelagoZelda1Client.exe"; Tasks: desktopicon; Components: client/tloz
Name: "{commondesktop}\{#MyAppName} Wargroove Client"; Filename: "{app}\ArchipelagoWargrooveClient.exe"; Tasks: desktopicon; Components: client/wargroove Name: "{commondesktop}\{#MyAppName} Wargroove Client"; Filename: "{app}\ArchipelagoWargrooveClient.exe"; Tasks: desktopicon; Components: client/wargroove
Name: "{commondesktop}\{#MyAppName} Kingdom Hearts 2 Client"; Filename: "{app}\ArchipelagoKH2Client.exe"; Tasks: desktopicon; Components: client/kh2 Name: "{commondesktop}\{#MyAppName} Kingdom Hearts 2 Client"; Filename: "{app}\ArchipelagoKH2Client.exe"; Tasks: desktopicon; Components: client/kh2
@ -249,6 +255,11 @@ Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch"; ValueData: "Ar
Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; Components: client/pkmn Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; Components: client/pkmn
Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/pkmn Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/pkmn
Root: HKCR; Subkey: ".apbn3"; ValueData: "{#MyAppName}bn3bpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/mmbn3
Root: HKCR; Subkey: "{#MyAppName}bn3bpatch"; ValueData: "Archipelago MegaMan Battle Network 3 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/mmbn3
Root: HKCR; Subkey: "{#MyAppName}bn3bpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoMMBN3Client.exe,0"; ValueType: string; ValueName: ""; Components: client/mmbn3
Root: HKCR; Subkey: "{#MyAppName}bn3bpatch\shell\open\command"; ValueData: """{app}\ArchipelagoMMBN3Client.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/mmbn3
Root: HKCR; Subkey: ".apladx"; ValueData: "{#MyAppName}ladxpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/ladx Root: HKCR; Subkey: ".apladx"; ValueData: "{#MyAppName}ladxpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/ladx
Root: HKCR; Subkey: "{#MyAppName}ladxpatch"; ValueData: "Archipelago Links Awakening DX Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/ladx Root: HKCR; Subkey: "{#MyAppName}ladxpatch"; ValueData: "Archipelago Links Awakening DX Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/ladx
Root: HKCR; Subkey: "{#MyAppName}ladxpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoLinksAwakeningClient.exe,0"; ValueType: string; ValueName: ""; Components: client/ladx Root: HKCR; Subkey: "{#MyAppName}ladxpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoLinksAwakeningClient.exe,0"; ValueType: string; ValueName: ""; Components: client/ladx
@ -331,6 +342,9 @@ var RedROMFilePage: TInputFileWizardPage;
var bluerom: string; var bluerom: string;
var BlueROMFilePage: TInputFileWizardPage; var BlueROMFilePage: TInputFileWizardPage;
var bn3rom: string;
var BN3ROMFilePage: TInputFileWizardPage;
var ladxrom: string; var ladxrom: string;
var LADXROMFilePage: TInputFileWizardPage; var LADXROMFilePage: TInputFileWizardPage;
@ -450,6 +464,20 @@ begin
'.gb'); '.gb');
end; end;
function AddGBARomPage(name: string): TInputFileWizardPage;
begin
Result :=
CreateInputFilePage(
wpSelectComponents,
'Select ROM File',
'Where is your ' + name + ' located?',
'Select the file, then click Next.');
Result.Add(
'Location of ROM file:',
'GBA ROM files|*.gba|All files|*.*',
'.gba');
end;
function AddSMSRomPage(name: string): TInputFileWizardPage; function AddSMSRomPage(name: string): TInputFileWizardPage;
begin begin
Result := Result :=
@ -458,7 +486,6 @@ begin
'Select ROM File', 'Select ROM File',
'Where is your ' + name + ' located?', 'Where is your ' + name + ' located?',
'Select the file, then click Next.'); 'Select the file, then click Next.');
Result.Add( Result.Add(
'Location of ROM file:', 'Location of ROM file:',
'SMS ROM files|*.sms|All files|*.*', 'SMS ROM files|*.sms|All files|*.*',
@ -541,6 +568,8 @@ begin
Result := not (L2ACROMFilePage.Values[0] = '') Result := not (L2ACROMFilePage.Values[0] = '')
else if (assigned(OoTROMFilePage)) and (CurPageID = OoTROMFilePage.ID) then else if (assigned(OoTROMFilePage)) and (CurPageID = OoTROMFilePage.ID) then
Result := not (OoTROMFilePage.Values[0] = '') Result := not (OoTROMFilePage.Values[0] = '')
else if (assigned(BN3ROMFilePage)) and (CurPageID = BN3ROMFilePage.ID) then
Result := not (BN3ROMFilePage.Values[0] = '')
else if (assigned(ZlROMFilePage)) and (CurPageID = ZlROMFilePage.ID) then else if (assigned(ZlROMFilePage)) and (CurPageID = ZlROMFilePage.ID) then
Result := not (ZlROMFilePage.Values[0] = '') Result := not (ZlROMFilePage.Values[0] = '')
else if (assigned(RedROMFilePage)) and (CurPageID = RedROMFilePage.ID) then else if (assigned(RedROMFilePage)) and (CurPageID = RedROMFilePage.ID) then
@ -765,6 +794,22 @@ begin
Result := ''; Result := '';
end; end;
function GetBN3ROMPath(Param: string): string;
begin
if Length(bn3rom) > 0 then
Result := bn3rom
else if Assigned(BN3ROMFilePage) then
begin
R := CompareStr(GetMD5OfFile(BN3ROMFilePage.Values[0]), '6fe31df0144759b34ad666badaacc442')
if R <> 0 then
MsgBox('MegaMan Battle Network 3 Blue ROM validation failed. Very likely wrong file.', mbInformation, MB_OK);
Result := BN3ROMFilePage.Values[0]
end
else
Result := '';
end;
procedure InitializeWizard(); procedure InitializeWizard();
begin begin
AddOoTRomPage(); AddOoTRomPage();
@ -801,6 +846,10 @@ begin
if Length(bluerom) = 0 then if Length(bluerom) = 0 then
BlueROMFilePage:= AddGBRomPage('Pokemon Blue (UE) [S][!].gb'); BlueROMFilePage:= AddGBRomPage('Pokemon Blue (UE) [S][!].gb');
bn3rom := CheckRom('Mega Man Battle Network 3 - Blue Version (USA).gba','6fe31df0144759b34ad666badaacc442');
if Length(bn3rom) = 0 then
BN3ROMFilePage:= AddGBARomPage('Mega Man Battle Network 3 - Blue Version (USA).gba');
ladxrom := CheckRom('Legend of Zelda, The - Link''s Awakening DX (USA, Europe) (SGB Enhanced).gbc','07c211479386825042efb4ad31bb525f'); ladxrom := CheckRom('Legend of Zelda, The - Link''s Awakening DX (USA, Europe) (SGB Enhanced).gbc','07c211479386825042efb4ad31bb525f');
if Length(ladxrom) = 0 then if Length(ladxrom) = 0 then
LADXROMFilePage:= AddGBRomPage('Legend of Zelda, The - Link''s Awakening DX (USA, Europe) (SGB Enhanced).gbc'); LADXROMFilePage:= AddGBRomPage('Legend of Zelda, The - Link''s Awakening DX (USA, Europe) (SGB Enhanced).gbc');
@ -842,6 +891,8 @@ begin
Result := not (WizardIsComponentSelected('generator/pkmn_r') or WizardIsComponentSelected('client/pkmn/red')); Result := not (WizardIsComponentSelected('generator/pkmn_r') or WizardIsComponentSelected('client/pkmn/red'));
if (assigned(BlueROMFilePage)) and (PageID = BlueROMFilePage.ID) then if (assigned(BlueROMFilePage)) and (PageID = BlueROMFilePage.ID) then
Result := not (WizardIsComponentSelected('generator/pkmn_b') or WizardIsComponentSelected('client/pkmn/blue')); Result := not (WizardIsComponentSelected('generator/pkmn_b') or WizardIsComponentSelected('client/pkmn/blue'));
if (assigned(BN3ROMFilePage)) and (PageID = BN3ROMFilePage.ID) then
Result := not (WizardIsComponentSelected('generator/mmbn3') or WizardIsComponentSelected('client/mmbn3'));
if (assigned(LADXROMFilePage)) and (PageID = LADXROMFilePage.ID) then if (assigned(LADXROMFilePage)) and (PageID = LADXROMFilePage.ID) then
Result := not (WizardIsComponentSelected('generator/ladx') or WizardIsComponentSelected('client/ladx')); Result := not (WizardIsComponentSelected('generator/ladx') or WizardIsComponentSelected('client/ladx'));
if (assigned(TLoZROMFilePage)) and (PageID = TLoZROMFilePage.ID) then if (assigned(TLoZROMFilePage)) and (PageID = TLoZROMFilePage.ID) then

View File

@ -116,6 +116,9 @@ components: List[Component] = [
file_identifier=SuffixIdentifier('.apzl')), file_identifier=SuffixIdentifier('.apzl')),
# Kingdom Hearts 2 # Kingdom Hearts 2
Component('KH2 Client', "KH2Client"), Component('KH2 Client', "KH2Client"),
#MegaMan Battle Network 3
Component('MMBN3 Client', 'MMBN3Client', file_identifier=SuffixIdentifier('.apbn3'))
] ]

194
worlds/mmbn3/BN3RomUtils.py Normal file

File diff suppressed because one or more lines are too long

345
worlds/mmbn3/Items.py Normal file
View File

@ -0,0 +1,345 @@
import typing
from enum import IntEnum, Enum
from BaseClasses import Item, ItemClassification
from .Names.ItemName import ItemName
class ItemType(str, Enum):
Undernet = "undernet"
Chip = "chip"
KeyItem = "key"
SubChip = "subchip"
Zenny = "zenny"
Program = "program"
BugFrag = "bugfrag"
External = "External"
__str__ = str.__str__
class ProgramColor(IntEnum):
White = 1
Pink = 2
Yellow = 3
Red = 4
Blue = 5
Green = 6
Orange = 7
Purple = 8
Dark = 9
def chip_code(c):
if c == '*':
return 26
return ord(c) - ord('A')
class ItemData(typing.NamedTuple):
code: int
itemName: str
progression: ItemClassification
type: ItemType
itemID: int = 0x00
subItemID: int = 0x00
count: int = 1
recipient: str = "Myself"
class MMBN3Item(Item):
game: str = "MegaMan Battle Network 3"
keyItemList: typing.List[ItemData] = [
ItemData(0xB31000, ItemName.Progressive_Undernet_Rank, ItemClassification.progression_skip_balancing, ItemType.Undernet, 27),
ItemData(0xB31001, ItemName.CACDCPas, ItemClassification.progression, ItemType.KeyItem, 92),
ItemData(0xB31002, ItemName.CSciPas, ItemClassification.progression, ItemType.KeyItem, 93),
ItemData(0xB31003, ItemName.CYokaPas, ItemClassification.progression, ItemType.KeyItem, 94),
ItemData(0xB31004, ItemName.CBeacPas, ItemClassification.progression, ItemType.KeyItem, 95),
ItemData(0xB31005, ItemName.WWW_ID, ItemClassification.progression, ItemType.KeyItem, 47),
ItemData(0xB31006, ItemName.OrderSys, ItemClassification.useful, ItemType.KeyItem, 12),
ItemData(0xB31007, ItemName.ModTools, ItemClassification.useful, ItemType.KeyItem, 55),
ItemData(0xB31008, ItemName.ExpMem, ItemClassification.useful, ItemType.KeyItem, 97),
ItemData(0xB31009, ItemName.SpinWht, ItemClassification.useful, ItemType.KeyItem, 71),
ItemData(0xB3100A, ItemName.SpinPink, ItemClassification.useful, ItemType.KeyItem, 72),
ItemData(0xB3100B, ItemName.SpinYllw, ItemClassification.useful, ItemType.KeyItem, 73),
ItemData(0xB3100C, ItemName.SpinRed, ItemClassification.useful, ItemType.KeyItem, 74),
ItemData(0xB3100D, ItemName.SpinBlue, ItemClassification.useful, ItemType.KeyItem, 75),
ItemData(0xB3100E, ItemName.SpinGrn, ItemClassification.useful, ItemType.KeyItem, 76),
ItemData(0xB3100F, ItemName.SpinOrange, ItemClassification.useful, ItemType.KeyItem, 77),
ItemData(0xB31010, ItemName.SpinPurple, ItemClassification.useful, ItemType.KeyItem, 78),
ItemData(0xB31011, ItemName.SpinDark, ItemClassification.useful, ItemType.KeyItem, 79),
ItemData(0xB31012, ItemName.HPMemory, ItemClassification.useful, ItemType.KeyItem, 96),
ItemData(0xB31013, ItemName.RegUP1, ItemClassification.filler, ItemType.KeyItem, 98),
ItemData(0xB31014, ItemName.RegUP2, ItemClassification.useful, ItemType.KeyItem, 99),
ItemData(0xB31015, ItemName.RegUP3, ItemClassification.useful, ItemType.KeyItem, 100),
ItemData(0xB31016, ItemName.Mr_Famous_Wristband, ItemClassification.filler, ItemType.KeyItem, 57),
ItemData(0xB31017, ItemName.SubMem, ItemClassification.filler, ItemType.KeyItem, 101),
ItemData(0xB310B1, ItemName.SubPET, ItemClassification.progression, ItemType.KeyItem, 10),
ItemData(0xB310B2, ItemName.Needle, ItemClassification.progression, ItemType.KeyItem, 14),
ItemData(0xB310B3, ItemName.PETCase, ItemClassification.progression, ItemType.KeyItem, 16),
# ItemData(0xB310B4, ItemName.Parasol, ItemClassification.progression, ItemType.KeyItem, 3),
ItemData(0xB310DF, ItemName.Hammer, ItemClassification.progression, ItemType.KeyItem, 56)
]
subChipList: typing.List[ItemData] = [
ItemData(0xB31018, ItemName.Unlocker, ItemClassification.useful, ItemType.SubChip, 117),
ItemData(0xB31019, ItemName.Untrap, ItemClassification.filler, ItemType.SubChip, 115),
ItemData(0xB3101A, ItemName.LockEnmy, ItemClassification.filler, ItemType.SubChip, 116),
ItemData(0xB3101B, ItemName.MiniEnrg, ItemClassification.filler, ItemType.SubChip, 112),
ItemData(0xB3101C, ItemName.FullEnrg, ItemClassification.filler, ItemType.SubChip, 113),
ItemData(0xB3101D, ItemName.SneakRun, ItemClassification.filler, ItemType.SubChip, 114)
]
chipList: typing.List[ItemData] = [
ItemData(0xB3101E, ItemName.AirShoes_star, ItemClassification.useful, ItemType.Chip, 168, chip_code('*')),
ItemData(0xB3101F, ItemName.AirShot3_star, ItemClassification.useful, ItemType.Chip, 6, chip_code('*')),
ItemData(0xB31020, ItemName.AntiNavi_M, ItemClassification.useful, ItemType.Chip, 190, chip_code('M')),
ItemData(0xB31021, ItemName.AntiRecv_B, ItemClassification.filler, ItemType.Chip, 193, chip_code('B')),
ItemData(0xB31022, ItemName.AntiSword_Y, ItemClassification.useful, ItemType.Chip, 192, chip_code('Y')),
ItemData(0xB31023, ItemName.Aqua_plus_30_star, ItemClassification.useful, ItemType.Chip, 197, chip_code('*')),
ItemData(0xB31024, ItemName.Aura_F, ItemClassification.useful, ItemType.Chip, 176, chip_code('F')),
ItemData(0xB31025, ItemName.BambooSword_N, ItemClassification.useful, ItemType.Chip, 36, chip_code('N')),
ItemData(0xB31026, ItemName.Barr100_E, ItemClassification.useful, ItemType.Chip, 174, chip_code('E')),
ItemData(0xB31027, ItemName.Barr200_E, ItemClassification.useful, ItemType.Chip, 175, chip_code('E')),
ItemData(0xB31028, ItemName.Barrier_E, ItemClassification.useful, ItemType.Chip, 173, chip_code('E')),
ItemData(0xB31029, ItemName.Barrier_L, ItemClassification.filler, ItemType.Chip, 173, chip_code('L')),
ItemData(0xB3102A, ItemName.BlkBomb1_P, ItemClassification.useful, ItemType.Chip, 27, chip_code('P')),
ItemData(0xB3102B, ItemName.BlkBomb2_S, ItemClassification.useful, ItemType.Chip, 28, chip_code('S')),
ItemData(0xB3102C, ItemName.Cannon_C, ItemClassification.filler, ItemType.Chip, 1, chip_code('C')),
ItemData(0xB3102D, ItemName.CopyDmg_star, ItemClassification.filler, ItemType.Chip, 194, chip_code('*')),
ItemData(0xB3102E, ItemName.CustSwrd_Z, ItemClassification.filler, ItemType.Chip, 37, chip_code('Z')),
ItemData(0xB3102F, ItemName.DynaWave_V, ItemClassification.progression, ItemType.Chip, 46, chip_code('V')),
ItemData(0xB31030, ItemName.ElecSwrd_P, ItemClassification.useful, ItemType.Chip, 35, chip_code('P')),
ItemData(0xB31031, ItemName.Fire_plus_30_star, ItemClassification.filler, ItemType.Chip, 196, chip_code('*')),
ItemData(0xB31032, ItemName.FireRat_H, ItemClassification.useful, ItemType.Chip, 117, chip_code('H')),
ItemData(0xB31033, ItemName.FireSwrd_P, ItemClassification.progression, ItemType.Chip, 33, chip_code('P')),
ItemData(0xB31034, ItemName.FstGauge_star, ItemClassification.useful, ItemType.Chip, 158, chip_code('*')),
ItemData(0xB31035, ItemName.GaiaBlde_star, ItemClassification.useful, ItemType.Chip, 276, chip_code('*')),
ItemData(0xB31036, ItemName.Geddon1_star, ItemClassification.filler, ItemType.Chip, 138, chip_code('*')),
ItemData(0xB31037, ItemName.Geddon1_D, ItemClassification.filler, ItemType.Chip, 138, chip_code('D')),
ItemData(0xB31038, ItemName.Geddon3_U, ItemClassification.useful, ItemType.Chip, 140, chip_code('U')),
ItemData(0xB31039, ItemName.Geyser_B, ItemClassification.useful, ItemType.Chip, 107, chip_code('B')),
ItemData(0xB3103A, ItemName.GrabBack_K, ItemClassification.progression, ItemType.Chip, 136, chip_code('K')),
ItemData(0xB3103B, ItemName.GrabRvng_A, ItemClassification.filler, ItemType.Chip, 137, chip_code('A')),
ItemData(0xB3103C, ItemName.GrabRvng_Y, ItemClassification.filler, ItemType.Chip, 137, chip_code('Y')),
ItemData(0xB3103D, ItemName.GutStrght_S, ItemClassification.useful, ItemType.Chip, 48, chip_code('S')),
ItemData(0xB3103E, ItemName.Guardian_O, ItemClassification.useful, ItemType.Chip, 203, chip_code('O')),
ItemData(0xB3103F, ItemName.GutImpact_H, ItemClassification.useful, ItemType.Chip, 49, chip_code('H')),
ItemData(0xB31040, ItemName.GutImpct_J, ItemClassification.useful, ItemType.Chip, 49, chip_code('J')),
ItemData(0xB31041, ItemName.GutPunch_E, ItemClassification.filler, ItemType.Chip, 47, chip_code('E')),
ItemData(0xB31042, ItemName.GutPunch_B, ItemClassification.filler, ItemType.Chip, 47, chip_code('B')),
ItemData(0xB31043, ItemName.GutStrgt_Q, ItemClassification.useful, ItemType.Chip, 48, chip_code('Q')),
ItemData(0xB31044, ItemName.Hammer_T, ItemClassification.useful, ItemType.Chip, 64, chip_code('T')),
ItemData(0xB31045, ItemName.HeatSide_T, ItemClassification.filler, ItemType.Chip, 19, chip_code('T')),
ItemData(0xB31046, ItemName.HeroSwrd_P, ItemClassification.useful, ItemType.Chip, 207, chip_code('P')),
ItemData(0xB31047, ItemName.HiCannon_star, ItemClassification.filler, ItemType.Chip, 2, chip_code('*')),
ItemData(0xB31048, ItemName.Hole_star, ItemClassification.useful, ItemType.Chip, 161, chip_code('*')),
ItemData(0xB31049, ItemName.IceStage_star, ItemClassification.filler, ItemType.Chip, 180, chip_code('*')),
ItemData(0xB3104A, ItemName.Invis_star, ItemClassification.useful, ItemType.Chip, 160, chip_code('*')),
ItemData(0xB3104B, ItemName.Jealousy_J, ItemClassification.filler, ItemType.Chip, 214, chip_code('J')),
ItemData(0xB3104C, ItemName.Lance_S, ItemClassification.useful, ItemType.Chip, 75, chip_code('S')),
ItemData(0xB3104D, ItemName.LongSwrd_E, ItemClassification.filler, ItemType.Chip, 32, chip_code('E')),
ItemData(0xB3104E, ItemName.Magnum1_A, ItemClassification.progression, ItemType.Chip, 81, chip_code('A')),
ItemData(0xB3104F, ItemName.Muramasa_M, ItemClassification.useful, ItemType.Chip, 202, chip_code('M')),
ItemData(0xB31050, ItemName.Navi_plus_40_star, ItemClassification.filler, ItemType.Chip, 206, chip_code('*')),
ItemData(0xB31051, ItemName.Panic_C, ItemClassification.filler, ItemType.Chip, 41, chip_code('C')),
ItemData(0xB31052, ItemName.PanlOut3_star, ItemClassification.filler, ItemType.Chip, 120, chip_code('*')),
ItemData(0xB31053, ItemName.Poltergeist_G, ItemClassification.filler, ItemType.Chip, 213, chip_code('G')),
ItemData(0xB31054, ItemName.Prism_Q, ItemClassification.useful, ItemType.Chip, 142, chip_code('Q')),
ItemData(0xB31055, ItemName.Recov10_star, ItemClassification.filler, ItemType.Chip, 121, chip_code('*')),
ItemData(0xB31056, ItemName.Recov120_star, ItemClassification.useful, ItemType.Chip, 125, chip_code('*')),
ItemData(0xB31057, ItemName.Recov120_O, ItemClassification.filler, ItemType.Chip, 125, chip_code('O')),
ItemData(0xB31058, ItemName.Recov120_S, ItemClassification.progression, ItemType.Chip, 125, chip_code('S')),
ItemData(0xB31059, ItemName.Recov150_P, ItemClassification.filler, ItemType.Chip, 126, chip_code('P')),
ItemData(0xB3105A, ItemName.Recov200_N, ItemClassification.filler, ItemType.Chip, 127, chip_code('N')),
ItemData(0xB3105B, ItemName.Recov30_star, ItemClassification.progression, ItemType.Chip, 122, chip_code('*')),
ItemData(0xB3105C, ItemName.Recov300_R, ItemClassification.useful, ItemType.Chip, 128, chip_code('R')),
ItemData(0xB3105D, ItemName.Recov50_G, ItemClassification.filler, ItemType.Chip, 123, chip_code('G')),
ItemData(0xB3105E, ItemName.Repair_star, ItemClassification.filler, ItemType.Chip, 159, chip_code('*')),
ItemData(0xB3105F, ItemName.Repair_A, ItemClassification.filler, ItemType.Chip, 159, chip_code('A')),
ItemData(0xB31060, ItemName.Rockcube_star, ItemClassification.filler, ItemType.Chip, 141, chip_code('*')),
ItemData(0xB31061, ItemName.Rook_F, ItemClassification.filler, ItemType.Chip, 153, chip_code('F')),
ItemData(0xB31062, ItemName.Salamndr_star, ItemClassification.useful, ItemType.Chip, 273, chip_code('*')),
ItemData(0xB31063, ItemName.SandStage_C, ItemClassification.filler, ItemType.Chip, 182, chip_code('C')),
ItemData(0xB31064, ItemName.SideGun_S, ItemClassification.filler, ItemType.Chip, 12, chip_code('S')),
ItemData(0xB31065, ItemName.Slasher_B, ItemClassification.useful, ItemType.Chip, 43, chip_code('B')),
ItemData(0xB31066, ItemName.SloGuage_star, ItemClassification.filler, ItemType.Chip, 157, chip_code('*')),
ItemData(0xB31067, ItemName.Snake_D, ItemClassification.useful, ItemType.Chip, 131, chip_code('D')),
ItemData(0xB31068, ItemName.Snctuary_C, ItemClassification.useful, ItemType.Chip, 184, chip_code('C')),
ItemData(0xB31069, ItemName.Spreader_star, ItemClassification.useful, ItemType.Chip, 13, chip_code('*')),
ItemData(0xB3106A, ItemName.Spreader_N, ItemClassification.useful, ItemType.Chip, 13, chip_code('N')),
ItemData(0xB3106B, ItemName.Spreader_P, ItemClassification.useful, ItemType.Chip, 13, chip_code('P')),
ItemData(0xB3106C, ItemName.StepCross_Q, ItemClassification.useful, ItemType.Chip, 40, chip_code('Q')),
ItemData(0xB3106D, ItemName.StepCross_R, ItemClassification.useful, ItemType.Chip, 40, chip_code('R')),
ItemData(0xB3106E, ItemName.StepSwrd_M, ItemClassification.useful, ItemType.Chip, 39, chip_code('M')),
ItemData(0xB3106F, ItemName.StepSwrd_N, ItemClassification.useful, ItemType.Chip, 39, chip_code('N')),
ItemData(0xB31070, ItemName.StepSwrd_O, ItemClassification.useful, ItemType.Chip, 39, chip_code('O')),
ItemData(0xB31071, ItemName.StepCross_S, ItemClassification.useful, ItemType.Chip, 40, chip_code('S')),
ItemData(0xB31072, ItemName.Sword_E, ItemClassification.filler, ItemType.Chip, 30, chip_code('E')),
ItemData(0xB31073, ItemName.Team1_star, ItemClassification.useful, ItemType.Chip, 132, chip_code('*')),
ItemData(0xB31074, ItemName.Team2_star, ItemClassification.useful, ItemType.Chip, 169, chip_code('*')),
ItemData(0xB31075, ItemName.Thndrblt_star, ItemClassification.useful, ItemType.Chip, 275, chip_code('*')),
ItemData(0xB31076, ItemName.Tornado_L, ItemClassification.filler, ItemType.Chip, 65, chip_code('L')),
ItemData(0xB31077, ItemName.Fountain_star, ItemClassification.useful, ItemType.Chip, 274, chip_code('*')),
ItemData(0xB31078, ItemName.VarSword_B, ItemClassification.useful, ItemType.Chip, 38, chip_code('B')),
ItemData(0xB31079, ItemName.VarSword_F, ItemClassification.useful, ItemType.Chip, 38, chip_code('F')),
ItemData(0xB3107A, ItemName.WideSwrd_C, ItemClassification.progression, ItemType.Chip, 31, chip_code('C')),
ItemData(0xB3107B, ItemName.WideSwrd_E, ItemClassification.filler, ItemType.Chip, 31, chip_code('E')),
ItemData(0xB3107C, ItemName.WideSwrd_L, ItemClassification.filler, ItemType.Chip, 31, chip_code('L')),
ItemData(0xB3107D, ItemName.Yo_Yo1_D, ItemClassification.filler, ItemType.Chip, 69, chip_code('D')),
ItemData(0xB3107E, ItemName.ZeusHammer_Z, ItemClassification.useful, ItemType.Chip, 208, chip_code('Z')),
# ItemData(0xB3107F, ItemName.BassGS_X, ItemClassification.useful, ItemType.Chip, 312, ChipCode('X')),
ItemData(0xB3107F, ItemName.Bass_X, ItemClassification.useful, ItemType.Chip, 311, chip_code('X')),
ItemData(0xB31080, ItemName.DeltaRay_Z, ItemClassification.useful, ItemType.Chip, 302, chip_code('Z')),
ItemData(0xB31081, ItemName.Punk_P, ItemClassification.useful, ItemType.Chip, 272, chip_code('P')),
ItemData(0xB31082, ItemName.DarkAura_A, ItemClassification.useful, ItemType.Chip, 309, chip_code('A')),
ItemData(0xB31083, ItemName.AlphaArm_Omega_V, ItemClassification.useful, ItemType.Chip, 310, chip_code('V')),
ItemData(0xB310AC, ItemName.SonicWav_W, ItemClassification.progression, ItemType.Chip, 45, chip_code('W')),
ItemData(0xB310AD, ItemName.Bubbler_C, ItemClassification.progression, ItemType.Chip, 14, chip_code('C')),
ItemData(0xB310AE, ItemName.Shake1_S, ItemClassification.progression, ItemType.Chip, 110, chip_code('S')),
ItemData(0xB310AF, ItemName.HoleMetr_H, ItemClassification.progression, ItemType.Chip, 88, chip_code('H')),
ItemData(0xB310B0, ItemName.Shadow_J, ItemClassification.progression, ItemType.Chip, 165, chip_code('J')),
ItemData(0xB310B8, ItemName.Roll_R, ItemClassification.filler, ItemType.Chip, 219, chip_code('R')),
ItemData(0xB310B9, ItemName.RollV2_R, ItemClassification.filler, ItemType.Chip, 220, chip_code('R')),
ItemData(0xB310BA, ItemName.RollV3_R, ItemClassification.useful, ItemType.Chip, 221, chip_code('R')),
ItemData(0xB310BB, ItemName.GutsMan_G, ItemClassification.filler, ItemType.Chip, 222, chip_code('G')),
ItemData(0xB310BC, ItemName.GutsManV2_G, ItemClassification.filler, ItemType.Chip, 223, chip_code('G')),
ItemData(0xB310BD, ItemName.GutsManV3_G, ItemClassification.useful, ItemType.Chip, 224, chip_code('G')),
ItemData(0xB310BE, ItemName.ProtoMan_B, ItemClassification.filler, ItemType.Chip, 227, chip_code('B')),
ItemData(0xB310BF, ItemName.ProtoManV2_B, ItemClassification.filler, ItemType.Chip, 228, chip_code('B')),
ItemData(0xB310C0, ItemName.ProtoManV3_B, ItemClassification.useful, ItemType.Chip, 229, chip_code('B')),
ItemData(0xB310C1, ItemName.FlashMan_F, ItemClassification.filler, ItemType.Chip, 232, chip_code('F')),
ItemData(0xB310C2, ItemName.FlashManV2_F, ItemClassification.filler, ItemType.Chip, 233, chip_code('F')),
ItemData(0xB310C3, ItemName.FlashManV3_F, ItemClassification.useful, ItemType.Chip, 234, chip_code('F')),
ItemData(0xB310C4, ItemName.BeastMan_B, ItemClassification.filler, ItemType.Chip, 237, chip_code('B')),
ItemData(0xB310C5, ItemName.BeastManV2_B, ItemClassification.filler, ItemType.Chip, 238, chip_code('B')),
ItemData(0xB310C6, ItemName.BeastManV3_B, ItemClassification.useful, ItemType.Chip, 239, chip_code('B')),
ItemData(0xB310C7, ItemName.BubblMan_B, ItemClassification.filler, ItemType.Chip, 242, chip_code('B')),
ItemData(0xB310C8, ItemName.BubblManV2_B, ItemClassification.filler, ItemType.Chip, 243, chip_code('B')),
ItemData(0xB310C9, ItemName.BubblManV3_B, ItemClassification.useful, ItemType.Chip, 244, chip_code('B')),
ItemData(0xB310CA, ItemName.DesertMan_D, ItemClassification.filler, ItemType.Chip, 247, chip_code('D')),
ItemData(0xB310CB, ItemName.DesertManV2_D, ItemClassification.filler, ItemType.Chip, 248, chip_code('D')),
ItemData(0xB310CC, ItemName.DesertManV3_D, ItemClassification.useful, ItemType.Chip, 249, chip_code('D')),
ItemData(0xB310CD, ItemName.PlantMan_P, ItemClassification.filler, ItemType.Chip, 252, chip_code('P')),
ItemData(0xB310CE, ItemName.PlantManV2_P, ItemClassification.filler, ItemType.Chip, 253, chip_code('P')),
ItemData(0xB310CF, ItemName.PlantManV3_P, ItemClassification.useful, ItemType.Chip, 254, chip_code('P')),
ItemData(0xB310D0, ItemName.FlamMan_F, ItemClassification.filler, ItemType.Chip, 257, chip_code('F')),
ItemData(0xB310D1, ItemName.FlamManV2_F, ItemClassification.filler, ItemType.Chip, 258, chip_code('F')),
ItemData(0xB310D2, ItemName.FlamManV3_F, ItemClassification.useful, ItemType.Chip, 259, chip_code('F')),
ItemData(0xB310D3, ItemName.DrillMan_D, ItemClassification.filler, ItemType.Chip, 262, chip_code('D')),
ItemData(0xB310D4, ItemName.DrillManV2_D, ItemClassification.filler, ItemType.Chip, 263, chip_code('D')),
ItemData(0xB310D5, ItemName.DrillManV3_D, ItemClassification.useful, ItemType.Chip, 264, chip_code('D')),
ItemData(0xB310D6, ItemName.MetalMan_M, ItemClassification.filler, ItemType.Chip, 267, chip_code('M')),
ItemData(0xB310D7, ItemName.MetalManV2_M, ItemClassification.filler, ItemType.Chip, 268, chip_code('M')),
ItemData(0xB310D8, ItemName.MetalManV3_M, ItemClassification.useful, ItemType.Chip, 269, chip_code('M')),
ItemData(0xB310D9, ItemName.KingMan_K, ItemClassification.filler, ItemType.Chip, 277, chip_code('K')),
ItemData(0xB310DA, ItemName.KingManV2_K, ItemClassification.filler, ItemType.Chip, 278, chip_code('K')),
ItemData(0xB310DB, ItemName.KingManV3_K, ItemClassification.useful, ItemType.Chip, 279, chip_code('K')),
ItemData(0xB310DC, ItemName.BowlMan_B, ItemClassification.filler, ItemType.Chip, 287, chip_code('B')),
ItemData(0xB310DD, ItemName.BowlManV2_B, ItemClassification.filler, ItemType.Chip, 288, chip_code('B')),
ItemData(0xB310DE, ItemName.BowlManV3_B, ItemClassification.useful, ItemType.Chip, 289, chip_code('B')),
]
programList: typing.List[ItemData] = [
ItemData(0xB31084, ItemName.Airshoes, ItemClassification.useful, ItemType.Program, 29, ProgramColor.White),
ItemData(0xB31085, ItemName.Attack_plus_White, ItemClassification.filler, ItemType.Program, 41, ProgramColor.White),
ItemData(0xB31086, ItemName.Attack_plus_Pink, ItemClassification.filler, ItemType.Program, 41, ProgramColor.Pink),
ItemData(0xB31087, ItemName.BrkChrg, ItemClassification.useful, ItemType.Program, 3, ProgramColor.Orange),
ItemData(0xB31088, ItemName.Charge_plus_Pink, ItemClassification.filler, ItemType.Program, 43, ProgramColor.Pink),
ItemData(0xB31089, ItemName.Charge_plus_White, ItemClassification.filler, ItemType.Program, 43, ProgramColor.White),
ItemData(0xB3108A, ItemName.Collect, ItemClassification.useful, ItemType.Program, 28, ProgramColor.Pink),
ItemData(0xB3108B, ItemName.GigFldr1, ItemClassification.useful, ItemType.Program, 48, ProgramColor.Purple),
ItemData(0xB3108C, ItemName.HP_100_Pink, ItemClassification.filler, ItemType.Program, 36, ProgramColor.Pink),
ItemData(0xB3108D, ItemName.HP_100_Yellow, ItemClassification.filler, ItemType.Program, 36, ProgramColor.Yellow),
ItemData(0xB3108E, ItemName.HP_200_Yellow, ItemClassification.useful, ItemType.Program, 37, ProgramColor.Yellow),
ItemData(0xB3108F, ItemName.HP_500_Yellow, ItemClassification.useful, ItemType.Program, 39, ProgramColor.Yellow),
ItemData(0xB31090, ItemName.HubBatc, ItemClassification.useful, ItemType.Program, 49, ProgramColor.Orange),
ItemData(0xB31091, ItemName.Jungle, ItemClassification.useful, ItemType.Program, 27, ProgramColor.White),
ItemData(0xB31092, ItemName.OilBody, ItemClassification.useful, ItemType.Program, 24, ProgramColor.Yellow),
ItemData(0xB31093, ItemName.QuickGge, ItemClassification.useful, ItemType.Program, 31, ProgramColor.Pink),
ItemData(0xB31094, ItemName.SetSand, ItemClassification.useful, ItemType.Program, 7, ProgramColor.Green),
ItemData(0xB31095, ItemName.SneakRun, ItemClassification.useful, ItemType.Program, 23, ProgramColor.Yellow),
ItemData(0xB31096, ItemName.Speed_plus_Yellow, ItemClassification.filler, ItemType.Program, 42, ProgramColor.Yellow),
ItemData(0xB31097, ItemName.WpnLV_plus_White, ItemClassification.filler, ItemType.Program, 35, ProgramColor.White),
ItemData(0xB31098, ItemName.WpnLV_plus_Pink, ItemClassification.filler, ItemType.Program, 35, ProgramColor.Pink),
ItemData(0xB31099, ItemName.WpnLV_plus_Yellow, ItemClassification.filler, ItemType.Program, 35, ProgramColor.Yellow),
ItemData(0xB3109A, ItemName.Press, ItemClassification.progression, ItemType.Program, 20, ProgramColor.White),
ItemData(0xB310B7, ItemName.UnderSht, ItemClassification.useful, ItemType.Program, 30, ProgramColor.White)
]
zennyList: typing.List[ItemData] = [
ItemData(0xB3109B, ItemName.zenny_200z, ItemClassification.filler, ItemType.Zenny, count=200),
ItemData(0xB3109C, ItemName.zenny_500z, ItemClassification.filler, ItemType.Zenny, count=500),
ItemData(0xB3109D, ItemName.zenny_600z, ItemClassification.filler, ItemType.Zenny, count=600),
ItemData(0xB3109E, ItemName.zenny_800z, ItemClassification.filler, ItemType.Zenny, count=800),
ItemData(0xB3109F, ItemName.zenny_900z, ItemClassification.filler, ItemType.Zenny, count=900),
ItemData(0xB310A0, ItemName.zenny_1000z, ItemClassification.filler, ItemType.Zenny, count=1000),
ItemData(0xB310A1, ItemName.zenny_1200z, ItemClassification.filler, ItemType.Zenny, count=1200),
ItemData(0xB310A2, ItemName.zenny_1400z, ItemClassification.filler, ItemType.Zenny, count=1400),
ItemData(0xB310A3, ItemName.zenny_1600z, ItemClassification.filler, ItemType.Zenny, count=1600),
ItemData(0xB310A4, ItemName.zenny_1800z, ItemClassification.filler, ItemType.Zenny, count=1800),
ItemData(0xB310A5, ItemName.zenny_2000z, ItemClassification.filler, ItemType.Zenny, count=2000),
ItemData(0xB310A6, ItemName.zenny_3000z, ItemClassification.filler, ItemType.Zenny, count=3000),
ItemData(0xB310A7, ItemName.zenny_9000z, ItemClassification.filler, ItemType.Zenny, count=9000),
ItemData(0xB310A8, ItemName.zenny_10000z, ItemClassification.useful, ItemType.Zenny, count=10000),
ItemData(0xB310A9, ItemName.zenny_30000z, ItemClassification.useful, ItemType.Zenny, count=30000),
ItemData(0xB310AA, ItemName.zenny_50000z, ItemClassification.useful, ItemType.Zenny, count=50000)
]
bugFragList: typing.List[ItemData] = [
ItemData(0xB310AB, ItemName.bugfrag_30, ItemClassification.filler, ItemType.BugFrag, count=30),
ItemData(0xB310B5, ItemName.bugfrag_10, ItemClassification.filler, ItemType.BugFrag, count=10),
ItemData(0xB310B6, ItemName.bugfrag_01, ItemClassification.filler, ItemType.BugFrag, count=1)
]
item_frequencies: typing.Dict[str, int] = {
ItemName.Progressive_Undernet_Rank: 8,
ItemName.ExpMem: 2,
ItemName.Unlocker: 10,
ItemName.HPMemory: 23,
ItemName.RegUP1: 4,
ItemName.RegUP2: 13,
ItemName.RegUP3: 4,
ItemName.Untrap: 2,
ItemName.SubMem: 4,
ItemName.MiniEnrg: 3,
ItemName.FullEnrg: 5,
ItemName.CopyDmg_star: 3,
ItemName.Charge_plus_White: 2,
ItemName.Charge_plus_Pink: 2,
ItemName.zenny_600z: 2,
ItemName.zenny_800z: 2,
ItemName.zenny_1000z: 2,
ItemName.zenny_1200z: 2,
ItemName.bugfrag_01: 5,
}
all_items: typing.List[ItemData] = keyItemList + subChipList + chipList + programList + zennyList + bugFragList
item_table: typing.Dict[str, ItemData] = {item.itemName: item for item in all_items}
items_by_id: typing.Dict[int, ItemData] = {item.code: item for item in all_items}

359
worlds/mmbn3/Locations.py Normal file
View File

@ -0,0 +1,359 @@
import typing
from BaseClasses import Location
from .Names.LocationName import LocationName
class LocationData:
name: str = ""
id: int = 0x00
flag_byte: int = 0x2000030
flag_mask: int = 0x01
text_archive_address: int = 0x00
text_script_index: int = 0
text_box_indices: typing.List[int] = [0]
inject_name: bool = False
hint_flag: int = None
hint_flag_mask: int = None
def __init__(self, name, id, flag, mask, text_archive_address=0x0, text_script_index=0x0,
text_box_indices=None, inject_name=False, hint_flag=None, hint_flag_mask=None):
self.name = name
self.id = id
self.flag_byte = flag
self.flag_mask = mask
self.text_archive_address = text_archive_address
self.text_script_index = text_script_index
if text_box_indices is None:
text_box_indices = [0]
self.text_box_indices = text_box_indices
self.inject_name = inject_name
self.hint_flag = hint_flag
self.hint_flag_mask = hint_flag_mask
class MMBN3Location(Location):
game: str = "MegaMan Battle Network 3"
bmds = [
LocationData(LocationName.ACDC_1_Southwest_BMD, 0xb31000, 0x020001d0, 0x40, 0x7643B8, 231, [1]),
LocationData(LocationName.ACDC_1_Northeast_BMD, 0xb31001, 0x020001d0, 0x80, 0x7643B8, 230, [1]),
LocationData(LocationName.ACDC_2_Center_BMD, 0xb31002, 0x20001d1, 0x40, 0x7658FC, 231, [1]),
LocationData(LocationName.ACDC_2_North_BMD, 0xb31003, 0x20001d1, 0x80, 0x7658FC, 230, [1]),
LocationData(LocationName.ACDC_3_Southwest_BMD, 0xb31004, 0x20001d2, 0x80, 0x766AE0, 230, [1]),
LocationData(LocationName.ACDC_3_Northeast_BMD, 0xb31005, 0x20001d2, 0x40, 0x766AE0, 231, [1]),
LocationData(LocationName.SciLab_1_WWW_BMD, 0xb31006, 0x20001d8, 0x40, 0x7694B4, 231, [1]),
LocationData(LocationName.SciLab_1_East_BMD, 0xb31007, 0x20001d8, 0x80, 0x7694B4, 230, [1]),
LocationData(LocationName.SciLab_2_West_BMD, 0xb31008, 0x20001d9, 0x80, 0x76A4F4, 230, [1, 2]),
LocationData(LocationName.SciLab_2_South_BMD, 0xb31009, 0x20001d9, 0x40, 0x76A4F4, 231, [1]),
LocationData(LocationName.Yoka_1_North_BMD, 0xb3100a, 0x20001e0, 0x80, 0x76D1B0, 230, [1]),
LocationData(LocationName.Yoka_1_WWW_BMD, 0xb3100b, 0x20001e0, 0x20, 0x76D1B0, 232, [1]),
LocationData(LocationName.Yoka_2_Upper_BMD, 0xb3100c, 0x20001e1, 0x40, 0x76DC80, 231, [1]),
LocationData(LocationName.Yoka_2_Lower_BMD, 0xb3100d, 0x20001e1, 0x80, 0x76DC80, 230, [1]),
LocationData(LocationName.Beach_1_BMD, 0xb3100e, 0x20001e8, 0x80, 0x76FF68, 230, [1]),
LocationData(LocationName.Beach_2_West_BMD, 0xb3100f, 0x20001e9, 0x80, 0x770A90, 230, [1]),
LocationData(LocationName.Beach_2_East_BMD, 0xb31010, 0x20001e9, 0x40, 0x770A90, 231, [1, 2]),
LocationData(LocationName.Undernet_1_South_BMD, 0xb31011, 0x20001f0, 0x80, 0x77307C, 230, [1]),
LocationData(LocationName.Undernet_1_WWW_BMD, 0xb31012, 0x20001f0, 0x40, 0x77307C, 231, [1]),
LocationData(LocationName.Undernet_2_Upper_BMD, 0xb31013, 0x20001f1, 0x80, 0x773700, 230, [1, 2]),
LocationData(LocationName.Undernet_2_Lower_BMD, 0xb31014, 0x20001f1, 0x40, 0x773700, 231, [1]),
LocationData(LocationName.Undernet_3_South_BMD, 0xb31015, 0x20001f2, 0x40, 0x773EA8, 231, [1]),
LocationData(LocationName.Undernet_3_Central_BMD, 0xb31016, 0x20001f2, 0x80, 0x773EA8, 230, [1]),
LocationData(LocationName.Undernet_4_Bottom_West_BMD, 0xb31017, 0x20001f3, 0x40, 0x7746C8, 231, [1]),
LocationData(LocationName.Undernet_4_Top_Pillar_BMD, 0xb31018, 0x20001f3, 0x20, 0x7746C8, 232, [1]),
LocationData(LocationName.Undernet_4_Top_North_BMD, 0xb31019, 0x20001f3, 0x80, 0x7746C8, 230, [1]),
LocationData(LocationName.Undernet_5_Upper_BMD, 0xb3101a, 0x20001f4, 0x40, 0x774FC8, 231, [1]),
LocationData(LocationName.Undernet_5_Lower_BMD, 0xb3101b, 0x20001f4, 0x80, 0x774FC8, 230, [1]),
LocationData(LocationName.Undernet_6_East_BMD, 0xb3101c, 0x20001f5, 0x80, 0x775390, 230, [1, 2]),
LocationData(LocationName.Undernet_6_Central_BMD, 0xb3101d, 0x20001f5, 0x20, 0x775390, 232, [1]),
LocationData(LocationName.Undernet_6_TV_BMD, 0xb3101e, 0x20001f5, 0x40, 0x775390, 231, [1]),
LocationData(LocationName.Undernet_7_West_BMD, 0xb3101f, 0x20001f6, 0x80, 0x775934, 230, [1]),
LocationData(LocationName.Undernet_7_Northwest_BMD, 0xb31020, 0x20001f6, 0x20, 0x775934, 232, [1]),
LocationData(LocationName.Undernet_7_Northeast_BMD, 0xb31021, 0x20001f6, 0x40, 0x775934, 231, [1]),
LocationData(LocationName.Secret_1_South_BMD, 0xb31022, 0x2000200, 0x40, 0x7771CC, 241, [1]),
LocationData(LocationName.Secret_1_Northeast_BMD, 0xb31023, 0x2000200, 0x20, 0x7771CC, 242, [1]),
LocationData(LocationName.Secret_1_Northwest_BMD, 0xb31024, 0x2000200, 0x80, 0x7771CC, 240, [1]),
LocationData(LocationName.Secret_2_Upper_BMD, 0xb31025, 0x2000201, 0x80, 0x777888, 240, [1]),
LocationData(LocationName.Secret_2_Lower_BMD, 0xb31026, 0x2000201, 0x20, 0x777888, 242, [1]),
LocationData(LocationName.Secret_2_Island_BMD, 0xb31027, 0x2000201, 0x40, 0x777888, 241, [1]),
LocationData(LocationName.Secret_3_South_BMD, 0xb31028, 0x2000202, 0x80, 0x777EDC, 240, [1]),
LocationData(LocationName.Secret_3_Island_BMD, 0xb31029, 0x2000202, 0x40, 0x777EDC, 241, [1]),
LocationData(LocationName.Secret_3_BugFrag_BMD, 0xb3102a, 0x2000202, 0x20, 0x777EDC, 242, [1]),
LocationData(LocationName.School_1_Entrance_BMD, 0xb3102b, 0x2000208, 0x2, 0x759BF8, 237, [1, 2]),
LocationData(LocationName.School_1_North_Central_BMD, 0xb3102c, 0x2000208, 0x4, 0x759BF8, 236, [1]),
LocationData(LocationName.School_1_Far_West_BMD_2, 0xb3102d, 0x2000208, 0x1, 0x759BF8, 238, [1]),
LocationData(LocationName.School_2_Entrance_BMD, 0xb3102e, 0x2000209, 0x4, 0x75A0B4, 236, [1]),
LocationData(LocationName.School_2_South_BMD, 0xb3102f, 0x2000209, 0x1, 0x75A0B4, 238, [1]),
LocationData(LocationName.School_2_Mainframe_BMD, 0xb31030, 0x2000209, 0x2, 0x75A0B4, 237, [1]),
LocationData(LocationName.Zoo_1_East_BMD, 0xb31031, 0x2000210, 0x80, 0x75A7F8, 230, [1]),
LocationData(LocationName.Zoo_1_Central_BMD, 0xb31032, 0x2000210, 0x20, 0x75A7F8, 232, [1]),
LocationData(LocationName.Zoo_1_North_BMD, 0xb31033, 0x2000210, 0x40, 0x75A7F8, 231, [1]),
LocationData(LocationName.Zoo_2_East_BMD, 0xb31034, 0x2000211, 0x40, 0x75ADA8, 231, [1]),
LocationData(LocationName.Zoo_2_Central_BMD, 0xb31035, 0x2000211, 0x80, 0x75ADA8, 230, [1]),
LocationData(LocationName.Zoo_2_West_BMD, 0xb31036, 0x2000211, 0x20, 0x75ADA8, 232, [1]),
LocationData(LocationName.Zoo_3_North_BMD, 0xb31037, 0x2000212, 0x10, 0x75B5EC, 238, [1]),
LocationData(LocationName.Zoo_3_Central_BMD, 0xb31038, 0x2000212, 0x80, 0x75B5EC, 235, [1]),
LocationData(LocationName.Zoo_3_Path_BMD, 0xb31039, 0x2000212, 0x40, 0x75B5EC, 236, [1]),
LocationData(LocationName.Zoo_3_Northwest_BMD, 0xb3103a, 0x2000212, 0x20, 0x75B5EC, 237, [1]),
LocationData(LocationName.Zoo_4_West_BMD, 0xb3103b, 0x2000213, 0x40, 0x75BEB0, 236, [1, 2]),
LocationData(LocationName.Zoo_4_Northwest_BMD, 0xb3103c, 0x2000213, 0x80, 0x75BEB0, 235, [1]),
LocationData(LocationName.Zoo_4_Southeast_BMD, 0xb3103d, 0x2000213, 0x20, 0x75BEB0, 237, [1]),
LocationData(LocationName.Hades_South_BMD, 0xb3103e, 0x20001eb, 0x20, 0x772898, 232, [1]),
LocationData(LocationName.Hospital_1_North_BMD, 0xb3103f, 0x2000218, 0x20, 0x75C864, 232, [1]),
LocationData(LocationName.Hospital_1_Center_BMD, 0xb31040, 0x2000218, 0x80, 0x75C864, 230, [1]),
LocationData(LocationName.Hospital_1_West_BMD, 0xb31041, 0x2000218, 0x40, 0x75C864, 231, [1, 2]),
LocationData(LocationName.Hospital_2_Southwest_BMD, 0xb31042, 0x2000219, 0x20, 0x75CD64, 232, [1]),
LocationData(LocationName.Hospital_2_Central_BMD, 0xb31043, 0x2000219, 0x40, 0x75CD64, 231, [1]),
LocationData(LocationName.Hospital_2_Island_BMD, 0xb31044, 0x2000219, 0x80, 0x75CD64, 230, [1]),
LocationData(LocationName.Hospital_3_Central_BMD, 0xb31045, 0x200021a, 0x80, 0x75D004, 230, [1]),
LocationData(LocationName.Hospital_3_West_BMD, 0xb31046, 0x200021a, 0x40, 0x75D004, 231, [1]),
LocationData(LocationName.Hospital_3_Northwest_BMD, 0xb31047, 0x200021a, 0x20, 0x75D004, 232, [1, 2]),
LocationData(LocationName.Hospital_4_Central_BMD, 0xb31048, 0x200021b, 0x20, 0x75D1BC, 232, [1]),
LocationData(LocationName.Hospital_4_Southeast_BMD, 0xb31049, 0x200021b, 0x80, 0x75D1BC, 230, [1]),
LocationData(LocationName.Hospital_4_North_BMD, 0xb3104a, 0x200021b, 0x40, 0x75D1BC, 231, [1]),
LocationData(LocationName.Hospital_5_Southwest_BMD, 0xb3104b, 0x200021c, 0x20, 0x75D3DC, 232, [1]),
LocationData(LocationName.Hospital_5_Northeast_BMD, 0xb3104c, 0x200021c, 0x80, 0x75D3DC, 230, [1]),
LocationData(LocationName.Hospital_5_Island_BMD, 0xb3104d, 0x200021c, 0x40, 0x75D3DC, 231, [1]),
LocationData(LocationName.WWW_1_Central_BMD, 0xb3104e, 0x2000220, 0x10, 0x75D630, 233, [1]),
LocationData(LocationName.WWW_1_West_BMD, 0xb3104f, 0x2000220, 0x40, 0x75D630, 231, [1]),
LocationData(LocationName.WWW_1_East_BMD, 0xb31050, 0x2000220, 0x20, 0x75D630, 232, [1]),
LocationData(LocationName.WWW_2_East_BMD, 0xb31051, 0x2000221, 0x40, 0x75D790, 231, [1, 2]),
LocationData(LocationName.WWW_2_Northwest_BMD, 0xb31052, 0x2000221, 0x20, 0x75D790, 232, [1]),
LocationData(LocationName.WWW_3_East_BMD, 0xb31053, 0x2000222, 0x40, 0x75D8EC, 231, [1]),
LocationData(LocationName.WWW_3_North_BMD, 0xb31054, 0x2000222, 0x20, 0x75D8EC, 232, [1]),
LocationData(LocationName.WWW_4_Northwest_BMD, 0xb31055, 0x2000223, 0x40, 0x75DA68, 231, [1]),
LocationData(LocationName.WWW_4_Central_BMD, 0xb31056, 0x2000223, 0x20, 0x75DA68, 232, [1]),
LocationData(LocationName.ACDC_Dog_House_BMD, 0xb31057, 0x2000240, 0x80, 0x7608E4, 230, [1]),
LocationData(LocationName.ACDC_Lans_Security_Panel_BMD, 0xb31058, 0x2000242, 0x80, 0x761954, 230, [1]),
LocationData(LocationName.ACDC_Yais_Phone_BMD, 0xb31059, 0x2000244, 0x8, 0x762A04, 230, [1]),
LocationData(LocationName.ACDC_NumberMan_Display_BMD, 0xb3105a, 0x2000248, 0x8, 0x763AB4, 230, [1]),
LocationData(LocationName.ACDC_Tank_BMD_1, 0xb3105b, 0x2000247, 0x40, 0x7635FC, 231, [1, 2]),
LocationData(LocationName.ACDC_Tank_BMD_2, 0xb3105c, 0x2000247, 0x80, 0x7635FC, 230, [1]),
LocationData(LocationName.ACDC_School_Server_BMD_1, 0xb3105d, 0x2000242, 0x8, 0x761AC0, 230, [1]),
LocationData(LocationName.ACDC_School_Server_BMD_2, 0xb3105e, 0x2000242, 0x4, 0x761AC0, 231, [1]),
LocationData(LocationName.ACDC_School_Blackboard_BMD, 0xb3105f, 0x2000240, 0x8, 0x760B48, 230, [1, 2]),
LocationData(LocationName.SciLab_Vending_Machine_BMD, 0xb31060, 0x2000241, 0x80, 0x760E80, 230, [1, 2]),
LocationData(LocationName.SciLab_Virus_Lab_Door_BMD_1, 0xb31061, 0x2000249, 0x8, 0x763ED8, 230, [1]),
LocationData(LocationName.SciLab_Virus_Lab_Door_BMD_2, 0xb310ed, 0x2000249, 0x4, 0x763ED8, 231, [1]),
LocationData(LocationName.SciLab_Dads_Computer_BMD, 0xb31062, 0x2000241, 0x8, 0x761498, 230, [1]),
LocationData(LocationName.Yoka_Armor_BMD, 0xb31063, 0x2000248, 0x80, 0x763908, 230, [1, 2]),
LocationData(LocationName.Yoka_TV_BMD, 0xb31064, 0x2000247, 0x8, 0x76377C, 230, [1]),
LocationData(LocationName.Yoka_Hot_Spring_BMD, 0xb31065, 0x200024b, 0x20, 0x7603F8, 230, [1]),
LocationData(LocationName.Yoka_Ticket_Machine_BMD, 0xb31066, 0x2000246, 0x8, 0x763420, 230, [1, 2]),
LocationData(LocationName.Yoka_Giraffe_BMD, 0xb31067, 0x200024b, 0x80, 0x7602E8, 230, [1]),
LocationData(LocationName.Yoka_Panda_BMD, 0xb31068, 0x2000249, 0x80, 0x763C88, 230, [1, 2]),
LocationData(LocationName.Beach_Hospital_Bed_BMD, 0xb31069, 0x2000245, 0x8, 0x76312C, 230, [1, 2]),
LocationData(LocationName.Beach_TV_BMD, 0xb3106a, 0x2000245, 0x80, 0x762CF0, 230, [1]),
LocationData(LocationName.Beach_Vending_Machine_BMD, 0xb3106b, 0x2000246, 0x80, 0x7632B4, 230, [1]),
LocationData(LocationName.Beach_News_Van_BMD, 0xb3106c, 0x2000243, 0x80, 0x761C10, 230, [1]),
LocationData(LocationName.Beach_Battle_Console_BMD, 0xb3106d, 0x2000243, 0x8, 0x761E00, 230, [1]),
LocationData(LocationName.Beach_Security_System_BMD, 0xb3106e, 0x2000244, 0x40, 0x76274C, 231, [1, 2]),
LocationData(LocationName.Beach_Broadcast_Computer_BMD, 0xb3106f, 0x200024b, 0x2, 0x7606E4, 230, [1]),
LocationData(LocationName.Hades_Gargoyle_BMD, 0xb31070, 0x200024b, 0x8, 0x76059C, 230, [1]),
LocationData(LocationName.WWW_Wall_BMD, 0xb31071, 0x200024a, 0x80, 0x7641A4, 230, [1]),
LocationData(LocationName.Mayls_HP_BMD, 0xb31072, 0x2000239, 0x80, 0x75DCC4, 230, [1]),
LocationData(LocationName.Yais_HP_BMD_1, 0xb31073, 0x200023b, 0x80, 0x75E018, 230, [1]),
LocationData(LocationName.Yais_HP_BMD_2, 0xb31074, 0x200023b, 0x40, 0x75E018, 231, [1, 2]),
LocationData(LocationName.Dexs_HP_BMD_1, 0xb31075, 0x200023a, 0x40, 0x75DEA4, 231, [1]),
LocationData(LocationName.Dexs_HP_BMD_2, 0xb31076, 0x200023a, 0x80, 0x75DEA4, 230, [1]),
LocationData(LocationName.Tamakos_HP_BMD, 0xb31077, 0x200023c, 0x80, 0x75E2D4, 230, [1]),
LocationData(LocationName.Undernet_7_Upper_BMD, 0xb31078, 0x20001f6, 0x1, 0x775934, 250, [1]),
LocationData(LocationName.School_1_KeyDataA_BMD, 0xb31079, 0x2000208, 0x80, 0x759BF8, 230, [1]),
LocationData(LocationName.School_1_KeyDataB_BMD, 0xb3107a, 0x2000208, 0x40, 0x759BF8, 231, [1]),
LocationData(LocationName.School_1_KeyDataC_BMD, 0xb3107b, 0x2000208, 0x20, 0x759BF8, 232, [1]),
LocationData(LocationName.School_2_CodeC_BMD, 0xb3107c, 0x2000209, 0x20, 0x75A0B4, 232, [1]),
LocationData(LocationName.School_2_CodeA_BMD, 0xb3107d, 0x2000209, 0x80, 0x75A0B4, 230, [1]),
LocationData(LocationName.School_2_CodeB_BMD, 0xb3107e, 0x2000209, 0x40, 0x75A0B4, 231, [1]),
# LocationData(LocationName.Hades_HadesKey_BMD, 0xb3107f, 0x20001eb, 0x40, 0x772898, 231, [1]),
# LocationData(LocationName.WWW_1_South_BMD, 0xb31080, 0x2000220, 0x80, 0x75D630, 230, [1]),
# LocationData(LocationName.WWW_2_West_BMD, 0xb31081, 0x2000221, 0x80, 0x75D790, 230, [1]),
# LocationData(LocationName.WWW_3_South_BMD, 0xb31082, 0x2000222, 0x80, 0x75D8EC, 230, [1]),
# LocationData(LocationName.WWW_4_East_BMD, 0xb31083, 0x2000223, 0x80, 0x75DA68, 230, [1])
]
pmds = [
LocationData(LocationName.ACDC_1_PMD, 0xb31084, 0x020001d0, 0x20, 0x7643B8, 232, [1]),
LocationData(LocationName.Yoka_1_PMD, 0xb31085, 0x20001e0, 0x40, 0x76D1B0, 231, [1]),
LocationData(LocationName.Beach_1_PMD, 0xb31086, 0x20001e8, 0x40, 0x76FF68, 231, [1, 2]),
LocationData(LocationName.Undernet_7_PMD, 0xb31087, 0x20001f6, 0x10, 0x775934, 233, [1]),
LocationData(LocationName.Mayls_HP_PMD, 0xb31088, 0x2000239, 0x40, 0x75DCC4, 231, [1]),
LocationData(LocationName.SciLab_Dads_Computer_PMD, 0xb31089, 0x2000241, 0x4, 0x761498, 231, [1]),
LocationData(LocationName.Zoo_Panda_PMD, 0xb3108a, 0x2000249, 0x40, 0x763C88, 231, [1]),
LocationData(LocationName.Beach_DNN_Security_Panel_PMD, 0xb3108b, 0x2000244, 0x80, 0x76274C, 230, [1]),
LocationData(LocationName.Beach_DNN_Main_Console_PMD, 0xb3108c, 0x200024b, 0x1, 0x7606E4, 231, [1]),
LocationData(LocationName.Tamakos_HP_PMD, 0xb3108d, 0x200023c, 0x40, 0x75E2D4, 231, [1])
]
overworlds = [
LocationData(LocationName.Yoka_Mr_Quiz, 0xb310ec, 0x200005f, 0x8, 0x7473F8, 197, [0, 1]),
LocationData(LocationName.Yoka_Quiz_Master, 0xb3108e, 0x200005f, 0x4, 0x748894, 202, [0]),
LocationData(LocationName.Hospital_Quiz_Queen, 0xb3108f, 0x200005f, 0x2, 0x757724, 202, [0]),
LocationData(LocationName.Hades_Quiz_King, 0xb31090, 0x2000164, 0x8, 0x7519B0, 207, [0]),
LocationData(LocationName.ACDC_SonicWav_W_Trade, 0xb31091, 0x2000162, 0x10, 0x73A7F8, 192, [0], True, 0x2000160, 0x80),
LocationData(LocationName.ACDC_Bubbler_C_Trade, 0xb31092, 0x2000162, 0x8, 0x737634, 192, [0], True, 0x2000160, 0x40),
LocationData(LocationName.ACDC_Recov120_S_Trade, 0xb31093, 0x2000163, 0x40, 0x72FA7C, 192, [0], True, 0x2000160, 0x02),
LocationData(LocationName.SciLab_Shake1_S_Trade, 0xb31094, 0x2000163, 0x10, 0x73B9C8, 192, [0], True, 0x2000161, 0x80),
LocationData(LocationName.Yoka_FireSwrd_P_Trade, 0xb31095, 0x2000162, 0x4, 0x745488, 192, [0], True, 0x2000160, 0x20),
LocationData(LocationName.Hospital_DynaWav_V_Trade, 0xb31096, 0x2000163, 0x4, 0x754D00, 202, [0], True, 0x2000161, 0x20),
LocationData(LocationName.Beach_DNN_WideSwrd_C_Trade, 0xb31097, 0x2000162, 0x1, 0x750C9C, 192, [0], True, 0x2000160, 0x08),
LocationData(LocationName.Beach_DNN_HoleMetr_H_Trade, 0xb31098, 0x2000164, 0x10, 0x751110, 192, [0], True, 0x2000162, 0x80),
LocationData(LocationName.Beach_DNN_Shadow_J_Trade, 0xb31099, 0x2000163, 0x2, 0x750248, 192, [0], True, 0x2000161, 0x10),
LocationData(LocationName.Hades_GrabBack_K_Trade, 0xb3109a, 0x2000164, 0x80, 0x753A48, 192, [0], True, 0x2000161, 0x04),
LocationData(LocationName.Comedian, 0xb3109b, 0x200024d, 0x20, 0x76DC80, 3, [22]),
LocationData(LocationName.Villain, 0xb3109c, 0x200024d, 0x10, 0x77124C, 24, [24]),
LocationData(LocationName.ACDC_School_Desk, 0xb3109d, 0x200024c, 0x1, 0x739580, 236, [4, 5]),
LocationData(LocationName.ACDC_Class_5B_Bookshelf, 0xb3109e, 0x200024c, 0x40, 0x737634, 235, [5, 6]),
LocationData(LocationName.SciLab_Garbage_Can, 0xb3109f, 0x200024c, 0x8, 0x73AC20, 222, [4, 5]),
LocationData(LocationName.Yoka_Inn_Jars, 0xb310a0, 0x200024c, 0x80, 0x747B1C, 237, [4, 5]),
LocationData(LocationName.Yoka_Zoo_Garbage, 0xb310a1, 0x200024d, 0x8, 0x749444, 226, [4]),
LocationData(LocationName.Beach_Department_Store, 0xb310a2, 0x2000161, 0x40, 0x74C27C, 196, [0, 1]),
LocationData(LocationName.Beach_Hospital_Plaque, 0xb310a3, 0x200024c, 0x4, 0x754394, 220, [3, 4]),
LocationData(LocationName.Beach_Hospital_Pink_Door, 0xb310a4, 0x200024d, 0x4, 0x754D00, 220, [4]),
LocationData(LocationName.Beach_Hospital_Tree, 0xb310a5, 0x200024c, 0x2, 0x757724, 222, [4]),
LocationData(LocationName.Beach_Hospital_Hidden_Conversation, 0xb310a6, 0x2000162, 0x20, 0x7586F8, 191, [0]),
LocationData(LocationName.Beach_Hospital_Girl, 0xb310a7, 0x2000160, 0x1, 0x754394, 191, [0, 1]),
LocationData(LocationName.Beach_DNN_Kiosk, 0xb310a8, 0x200024e, 0x80, 0x74E184, 76, [0]),
LocationData(LocationName.Beach_DNN_Boxes, 0xb310a9, 0x200024c, 0x20, 0x74FAAC, 222, [4, 5]),
LocationData(LocationName.Beach_DNN_Poster, 0xb310aa, 0x200024d, 0x80, 0x751110, 227, [3, 4]),
LocationData(LocationName.Hades_Boat_Dock, 0xb310ab, 0x200024c, 0x10, 0x7519B0, 223, [3]),
LocationData(LocationName.WWW_Control_Room_1_Screen, 0xb310ac, 0x200024d, 0x40, 0x7596C4, 222, [3, 4]),
LocationData(LocationName.WWW_Wilys_Desk, 0xb310ad, 0x200024d, 0x2, 0x759384, 229, [3]),
LocationData(LocationName.Undernet_4_Pillar_Prog, 0xb310ae, 0x2000161, 0x1, 0x7746C8, 191, [0, 1])
]
jobs = [
LocationData(LocationName.Please_deliver_this, 0xb310af, 0x2000300, 0x8, 0x7643B8, 195, [0]),
LocationData(LocationName.My_Navi_is_sick, 0xb310b0, 0x2000300, 0x4, 0x73AC20, 192, [0, 1]),
LocationData(LocationName.Help_me_with_my_son, 0xb310b1, 0x2000300, 0x2, 0x73F8FC, 193, [0, 1]),
LocationData(LocationName.Transmission_error, 0xb310b2, 0x2000300, 0x1, 0x73CF54, 193, [0]),
LocationData(LocationName.Chip_Prices, 0xb310b3, 0x2000301, 0x80, 0x767928, 195, [0]),
LocationData(LocationName.Im_broke, 0xb310b4, 0x2000301, 0x40, 0x746578, 194, [1]),
LocationData(LocationName.Rare_chips_for_cheap, 0xb310b5, 0x2000301, 0x20, 0x762A04, 192, [0]),
LocationData(LocationName.Be_my_boyfriend, 0xb310b6, 0x2000301, 0x10, 0x77124C, 203, [0]),
LocationData(LocationName.Will_you_deliver, 0xb310b7, 0x2000301, 0x8, 0x745488, 205, [0]),
# LocationData(LocationName.Look_for_friends, 0xb310b8, 0x2000300, 0x80, 0x72DAFC, 210, [0]),
# LocationData(LocationName.Stuntmen_wanted, 0xb310b9, 0x2000300, 0x40, 0x76FF68, 194, [0]),
# LocationData(LocationName.Riot_stopped, 0xb310ba, 0x2000300, 0x20, 0x74E184, 193, [0]),
# LocationData(LocationName.Gathering_Data, 0xb310bb, 0x2000300, 0x10, 0x739580, 193, [0]),
LocationData(LocationName.Somebody_please_help, 0xb310bc, 0x2000301, 0x4, 0x73A14C, 193, [0]),
LocationData(LocationName.Looking_for_condor, 0xb310bd, 0x2000301, 0x2, 0x749444, 203, [0]),
LocationData(LocationName.Help_with_rehab, 0xb310be, 0x2000301, 0x1, 0x762CF0, 192, [3]),
LocationData(LocationName.Old_Master, 0xb310bf, 0x2000302, 0x80, 0x760E80, 193, [0]),
LocationData(LocationName.Catching_gang_members, 0xb310c0, 0x2000302, 0x40, 0x76EAE4, 193, [0]),
LocationData(LocationName.Please_adopt_a_virus, 0xb310c1, 0x2000302, 0x20, 0x76A4F4, 193, [0]),
LocationData(LocationName.Legendary_Tomes, 0xb310c2, 0x2000302, 0x10, 0x772898, 193, [0]),
LocationData(LocationName.Legendary_Tomes_Treasure, 0xb310c3, 0x200024e, 0x40, 0x739580, 225, [15]),
LocationData(LocationName.Hide_and_seek_First_Child, 0xb310c4, 0x2000188, 0x4, 0x75A7F8, 191, [0]),
LocationData(LocationName.Hide_and_seek_Second_Child, 0xb310c5, 0x2000188, 0x2, 0x75ADA8, 191, [0]),
LocationData(LocationName.Hide_and_seek_Third_Child, 0xb310c6, 0x2000188, 0x1, 0x75B5EC, 191, [0]),
LocationData(LocationName.Hide_and_seek_Fourth_Child, 0xb310c7, 0x2000189, 0x80, 0x75BEB0, 191, [0]),
LocationData(LocationName.Hide_and_seek_Completion, 0xb310c8, 0x2000302, 0x8, 0x7406A0, 193, [0]),
LocationData(LocationName.Finding_the_blue_Navi, 0xb310c9, 0x2000302, 0x4, 0x773700, 192, [0]),
LocationData(LocationName.Give_your_support, 0xb310ca, 0x2000302, 0x2, 0x752D80, 192, [0]),
LocationData(LocationName.Stamp_collecting, 0xb310cb, 0x2000302, 0x1, 0x756074, 193, [0]),
LocationData(LocationName.Help_with_a_will, 0xb310cc, 0x2000303, 0x80, 0x7382B0, 197, [0])
]
number_traders = [
LocationData(LocationName.Numberman_Code_01, 0xb310cd, 0x2000430, 0x80, 0x735734, 30, [0]),
LocationData(LocationName.Numberman_Code_02, 0xb310ce, 0x2000430, 0x40, 0x735734, 31, [0]),
LocationData(LocationName.Numberman_Code_03, 0xb310cf, 0x2000430, 0x20, 0x735734, 32, [0]),
LocationData(LocationName.Numberman_Code_04, 0xb310d0, 0x2000430, 0x10, 0x735734, 33, [0]),
LocationData(LocationName.Numberman_Code_05, 0xb310d1, 0x2000430, 0x08, 0x735734, 34, [0]),
LocationData(LocationName.Numberman_Code_06, 0xb310d2, 0x2000430, 0x04, 0x735734, 35, [0]),
LocationData(LocationName.Numberman_Code_07, 0xb310d3, 0x2000430, 0x02, 0x735734, 36, [0]),
LocationData(LocationName.Numberman_Code_08, 0xb310d4, 0x2000430, 0x01, 0x735734, 37, [0]),
LocationData(LocationName.Numberman_Code_09, 0xb310d5, 0x2000431, 0x80, 0x735734, 38, [0]),
LocationData(LocationName.Numberman_Code_10, 0xb310d6, 0x2000431, 0x40, 0x735734, 39, [0]),
LocationData(LocationName.Numberman_Code_11, 0xb310d7, 0x2000431, 0x20, 0x735734, 40, [0]),
LocationData(LocationName.Numberman_Code_12, 0xb310d8, 0x2000431, 0x10, 0x735734, 41, [0]),
LocationData(LocationName.Numberman_Code_13, 0xb310d9, 0x2000431, 0x08, 0x735734, 42, [0]),
LocationData(LocationName.Numberman_Code_14, 0xb310da, 0x2000431, 0x04, 0x735734, 43, [0]),
LocationData(LocationName.Numberman_Code_15, 0xb310db, 0x2000431, 0x02, 0x735734, 44, [0]),
LocationData(LocationName.Numberman_Code_16, 0xb310dc, 0x2000431, 0x01, 0x735734, 45, [0]),
LocationData(LocationName.Numberman_Code_17, 0xb310dd, 0x2000432, 0x80, 0x735734, 46, [0]),
LocationData(LocationName.Numberman_Code_18, 0xb310de, 0x2000432, 0x40, 0x735734, 47, [0]),
LocationData(LocationName.Numberman_Code_19, 0xb310df, 0x2000432, 0x20, 0x735734, 48, [0]),
LocationData(LocationName.Numberman_Code_20, 0xb310e0, 0x2000432, 0x10, 0x735734, 49, [0]),
LocationData(LocationName.Numberman_Code_21, 0xb310e1, 0x2000432, 0x08, 0x735734, 50, [0]),
LocationData(LocationName.Numberman_Code_22, 0xb310e2, 0x2000432, 0x04, 0x735734, 51, [0]),
LocationData(LocationName.Numberman_Code_23, 0xb310e3, 0x2000432, 0x02, 0x735734, 52, [0]),
LocationData(LocationName.Numberman_Code_24, 0xb310e4, 0x2000432, 0x01, 0x735734, 53, [0]),
LocationData(LocationName.Numberman_Code_25, 0xb310e5, 0x2000433, 0x80, 0x735734, 54, [0]),
LocationData(LocationName.Numberman_Code_26, 0xb310e6, 0x2000433, 0x40, 0x735734, 55, [0]),
LocationData(LocationName.Numberman_Code_27, 0xb310e7, 0x2000433, 0x20, 0x735734, 56, [0]),
LocationData(LocationName.Numberman_Code_28, 0xb310e8, 0x2000433, 0x10, 0x735734, 57, [0]),
LocationData(LocationName.Numberman_Code_29, 0xb310e9, 0x2000433, 0x08, 0x735734, 58, [0]),
LocationData(LocationName.Numberman_Code_30, 0xb310ea, 0x2000433, 0x04, 0x735734, 59, [0]),
LocationData(LocationName.Numberman_Code_31, 0xb310eb, 0x2000433, 0x02, 0x735734, 60, [0])
]
chocolate_shop = [
LocationData(LocationName.Chocolate_Shop_01, 0xb310ee, 0x20001c0, 0x80, 0x73F8FC, 150, [0]),
LocationData(LocationName.Chocolate_Shop_02, 0xb310ef, 0x20001c0, 0x40, 0x73F8FC, 151, [0]),
LocationData(LocationName.Chocolate_Shop_03, 0xb310f0, 0x20001c0, 0x20, 0x73F8FC, 152, [0]),
LocationData(LocationName.Chocolate_Shop_04, 0xb310f1, 0x20001c0, 0x10, 0x73F8FC, 153, [0]),
LocationData(LocationName.Chocolate_Shop_05, 0xb310f2, 0x20001c0, 0x08, 0x73F8FC, 154, [0]),
LocationData(LocationName.Chocolate_Shop_06, 0xb310f3, 0x20001c0, 0x04, 0x73F8FC, 155, [0]),
LocationData(LocationName.Chocolate_Shop_07, 0xb310f4, 0x20001c0, 0x02, 0x73F8FC, 156, [0]),
LocationData(LocationName.Chocolate_Shop_08, 0xb310f5, 0x20001c0, 0x01, 0x73F8FC, 157, [0]),
LocationData(LocationName.Chocolate_Shop_09, 0xb310f6, 0x20001c1, 0x80, 0x73F8FC, 158, [0]),
LocationData(LocationName.Chocolate_Shop_10, 0xb310f7, 0x20001c1, 0x40, 0x73F8FC, 159, [0]),
LocationData(LocationName.Chocolate_Shop_11, 0xb310f8, 0x20001c1, 0x20, 0x73F8FC, 160, [0]),
LocationData(LocationName.Chocolate_Shop_12, 0xb310f9, 0x20001c1, 0x10, 0x73F8FC, 161, [0]),
LocationData(LocationName.Chocolate_Shop_13, 0xb310fa, 0x20001c1, 0x08, 0x73F8FC, 162, [0]),
LocationData(LocationName.Chocolate_Shop_14, 0xb310fb, 0x20001c1, 0x04, 0x73F8FC, 163, [0]),
LocationData(LocationName.Chocolate_Shop_15, 0xb310fc, 0x20001c1, 0x02, 0x73F8FC, 164, [0]),
LocationData(LocationName.Chocolate_Shop_16, 0xb310fd, 0x20001c1, 0x01, 0x73F8FC, 165, [0]),
LocationData(LocationName.Chocolate_Shop_17, 0xb310fe, 0x20001c2, 0x80, 0x73F8FC, 166, [0]),
LocationData(LocationName.Chocolate_Shop_18, 0xb310ff, 0x20001c2, 0x40, 0x73F8FC, 167, [0]),
LocationData(LocationName.Chocolate_Shop_19, 0xb31100, 0x20001c2, 0x20, 0x73F8FC, 168, [0]),
LocationData(LocationName.Chocolate_Shop_20, 0xb31101, 0x20001c2, 0x10, 0x73F8FC, 169, [0]),
LocationData(LocationName.Chocolate_Shop_21, 0xb31102, 0x20001c2, 0x08, 0x73F8FC, 170, [0]),
LocationData(LocationName.Chocolate_Shop_22, 0xb31103, 0x20001c2, 0x04, 0x73F8FC, 171, [0]),
LocationData(LocationName.Chocolate_Shop_23, 0xb31104, 0x20001c2, 0x02, 0x73F8FC, 172, [0]),
LocationData(LocationName.Chocolate_Shop_24, 0xb31105, 0x20001c2, 0x01, 0x73F8FC, 173, [0]),
LocationData(LocationName.Chocolate_Shop_25, 0xb31106, 0x20001c3, 0x80, 0x73F8FC, 174, [0]),
LocationData(LocationName.Chocolate_Shop_26, 0xb31107, 0x20001c3, 0x40, 0x73F8FC, 175, [0]),
LocationData(LocationName.Chocolate_Shop_27, 0xb31108, 0x20001c3, 0x20, 0x73F8FC, 176, [0]),
LocationData(LocationName.Chocolate_Shop_28, 0xb31109, 0x20001c3, 0x10, 0x73F8FC, 177, [0]),
LocationData(LocationName.Chocolate_Shop_29, 0xb3110a, 0x20001c3, 0x08, 0x73F8FC, 178, [0]),
LocationData(LocationName.Chocolate_Shop_30, 0xb3110b, 0x20001c3, 0x04, 0x73F8FC, 179, [0]),
LocationData(LocationName.Chocolate_Shop_31, 0xb3110c, 0x20001c3, 0x02, 0x73F8FC, 180, [0]),
LocationData(LocationName.Chocolate_Shop_32, 0xb3110d, 0x20001c3, 0x01, 0x73F8FC, 181, [0]),
]
always_excluded_locations = [
LocationName.Undernet_7_PMD,
LocationName.Undernet_7_Northeast_BMD,
LocationName.Undernet_7_Northwest_BMD,
LocationName.Secret_1_Northwest_BMD,
LocationName.Secret_1_Northeast_BMD,
LocationName.Secret_1_South_BMD,
LocationName.Secret_2_Upper_BMD,
LocationName.Secret_2_Lower_BMD,
LocationName.Secret_2_Island_BMD,
LocationName.Secret_3_Island_BMD,
LocationName.Secret_3_BugFrag_BMD,
LocationName.Secret_3_South_BMD
]
all_locations: typing.List[LocationData] = bmds + pmds + overworlds + jobs + number_traders + chocolate_shop
scoutable_locations: typing.List[LocationData] = [loc for loc in all_locations if loc.hint_flag is not None]
location_table: typing.Dict[str, int] = {locData.name: locData.id for locData in all_locations}
location_data_table: typing.Dict[str, LocationData] = {locData.name: locData for locData in all_locations}
"""
def setup_locations(world, player: int):
# If we later include options to change what gets added to the random pool,
# this is where they would be changed
return {locData.name: locData.id for locData in all_locations}
"""

View File

@ -0,0 +1,238 @@
class ItemName():
## Chips
AirShoes_star = "AirShoes *"
AirShot3_star = "AirShot3 *"
AntiNavi_M = "AntiNavi M"
AntiRecv_B = "AntiRecv B"
AntiSword_Y = "AntiSword Y"
Aqua_plus_30_star = "Aqua+30 *"
Aura_F = "Aura F"
BambooSword_N = "BambooSword N"
Barr100_E = "Barr100 E"
Barr200_E = "Barr200 E"
Barrier_E = "Barrier E"
Barrier_L = "Barrier L"
BlkBomb1_P = "BlkBomb1 P"
BlkBomb2_S = "BlkBomb2 S"
Cannon_C = "Cannon C"
CopyDmg_star = "CopyDmg *"
CustSwrd_Z = "CustSwrd Z"
DynaWave_V = "DynaWave V"
ElecSwrd_P = "ElecSwrd P"
Fire_plus_30_star = "Fire+30 *"
FireRat_H = "FireRat H"
FireSwrd_P = "FireSwrd P"
FstGauge_star = "FstGauge *"
GaiaBlde_star = "GaiaBlde *"
Geddon1_star = "Geddon1 *"
Geddon1_D = "Geddon1 D"
Geddon3_U = "Geddon3 U"
Geyser_B = "Geyser B"
GrabBack_K = "GrabBack K"
GrabRvng_A = "GrabRvng A"
GrabRvng_Y = "GrabRvng Y"
GutStrght_S = "GtStrght S"
Guardian_O = "Guardian O"
GutImpact_H = "GutImpact H"
GutImpct_J = "GutImpct J"
GutPunch_E = "GutPunch E"
GutPunch_B = "GutPunch B"
GutStrgt_Q = "GutStrgt Q"
Hammer_T = "Hammer T"
HeatSide_T = "HeatSide T"
HeroSwrd_P = "HeroSwrd P"
HiCannon_star = "HiCannon *"
Hole_star = "Hole *"
IceStage_star = "IceStage *"
Invis_star = "Invis *"
Jealousy_J = "Jealousy J"
Lance_S = "Lance S"
LongSwrd_E = "LongSwrd E"
Magnum1_A = "Magnum1 A"
Muramasa_M = "Muramasa M"
Navi_plus_40_star = "Navi+40 *"
Panic_C = "Panic C"
PanlOut3_star = "PanlOut3 *"
Poltergeist_G = "Poltergeist G"
Prism_Q = "Prism Q"
Recov10_star = "Recov10 *"
Recov120_star = "Recov120 *"
Recov120_O = "Recov120 O"
Recov120_S = "Recov120 S"
Recov150_P = "Recov150 P"
Recov200_N = "Recov200 N"
Recov30_star = "Recov30 *"
Recov300_R = "Recov300 R"
Recov50_G = "Recov50 G"
Repair_star = "Repair *"
Repair_A = "Repair A"
Rockcube_star = "Rockcube *"
Rook_F = "Rook F"
Salamndr_star = "Salamndr *"
SandStage_C = "SandStage C"
SideGun_S = "SideGun S"
Slasher_B = "Slasher B"
SloGuage_star = "SloGuage *"
Snake_D = "Snake D"
Snctuary_C = "Snctuary C"
Spreader_star = "Spreader *"
Spreader_N = "Spreader N"
Spreader_P = "Spreader P"
StepCross_Q = "StepCross Q"
StepCross_R = "StepCross R"
StepSwrd_M = "StepSwrd M"
StepSwrd_N = "StepSwrd N"
StepSwrd_O = "StepSwrd O"
StepCross_S = "StpCross S"
Sword_E = "Sword E"
Team1_star = "Team1 *"
Team2_star = "Team2 *"
Thndrblt_star = "Thndrblt *"
Tornado_L = "Tornado L"
Fountain_star = "Fountain *"
VarSword_B = "VarSword B"
VarSword_F = "VarSword F"
WideSwrd_C = "WideSwrd C"
WideSwrd_E = "WideSwrd E"
WideSwrd_L = "WideSwrd L"
Yo_Yo1_D = "Yo-Yo1 D"
ZeusHammer_Z = "ZeusHammer Z"
BassGS_X = "BassGS X"
DeltaRay_Z = "DeltaRay Z"
Punk_P = "Punk P"
DarkAura_A = "DarkAura A"
AlphaArm_Omega_V = "AlphaArmΩ V"
SonicWav_W = "SonicWav W"
Bubbler_C = "Bubbler C"
Shake1_S = "Shake1 S"
HoleMetr_H = "HoleMetr H"
Shadow_J = "Shadow J"
Roll_R = "Roll R"
RollV2_R = "Roll V2 R"
RollV3_R = "Roll V3 R"
GutsMan_G = "GutsMan G"
GutsManV2_G = "GutsMan V2 G"
GutsManV3_G = "GutsMan V3 G"
ProtoMan_B = "ProtoMan B"
ProtoManV2_B = "ProtoMan V2 B"
ProtoManV3_B = "ProtoMan V3 B"
FlashMan_F = "FlashMan F"
FlashManV2_F = "FlashMan V2 F"
FlashManV3_F = "FlashMan V3 F"
BeastMan_B = "BeastMan B"
BeastManV2_B = "BeastMan V2 B"
BeastManV3_B = "BeastMan V3 B"
BubblMan_B = "BubblMan B"
BubblManV2_B = "BubblMan V2 B"
BubblManV3_B = "BubblMan V3 B"
DesertMan_D = "DesertMan D"
DesertManV2_D = "DesertMan V2 D"
DesertManV3_D = "DesertMan V3 D"
PlantMan_P = "PlantMan P"
PlantManV2_P = "PlantMan V2 P"
PlantManV3_P = "PlantMan V3 P"
FlamMan_F = "FlamMan F"
FlamManV2_F = "FlamMan V2 F"
FlamManV3_F = "FlamMan V3 F"
DrillMan_D = "DrillMan D"
DrillManV2_D = "DrillMan V2 D"
DrillManV3_D = "DrillMan V3 D"
MetalMan_M = "MetalMan M"
MetalManV2_M = "MetalMan V2 M"
MetalManV3_M = "MetalMan V3 M"
KingMan_K = "KingMan K"
KingManV2_K = "KingMan V2 K"
KingManV3_K = "KingMan V3 K"
BowlMan_B = "BowlMan B"
BowlManV2_B = "BowlMan V2 B"
BowlManV3_B = "BowlMan V3 B"
Bass_X = "Bass+ X"
## Navi Cust Programs
Airshoes = "Airshoes"
Attack_plus_White = "Attack+1 (White)"
Attack_plus_Pink = "Attack+1 (Pink)"
BrkChrg = "BrkChrg"
Charge_plus_Pink = "Charge+1 (Pink)"
Charge_plus_White = "Charge+1 (White)"
Collect = "Collect"
GigFldr1 = "GigFldr1"
HP_100_Pink = "HP+100 (Pink)"
HP_100_Yellow = "HP+100 (Yellow)"
HP_200_Yellow = "HP+200 (Yellow)"
HP_500_Yellow = "HP+500 (Yellow)"
HubBatc = "HubBatc"
Jungle = "Jungle"
OilBody = "OilBody"
QuickGge = "QuickGge"
SetSand = "SetSand"
SneakRun = "SneakRun"
Speed_plus_Yellow = "Speed+1 (Yellow)"
WpnLV_plus_Yellow = "WpnLV+1 (Yellow)"
WpnLV_plus_Pink = "WpnLV+1 (Pink)"
WpnLV_plus_White = "WpnLV+1 (White)"
Press = "Press"
UnderSht = "UnderSht"
## Currency
zenny_200z = "200z"
zenny_500z = "500z"
zenny_600z = "600z"
zenny_800z = "800z"
zenny_900z = "900z"
zenny_1000z = "1000z"
zenny_1200z = "1200z"
zenny_1400z = "1400z"
zenny_1600z = "1600z"
zenny_1800z = "1800z"
zenny_2000z = "2000z"
zenny_3000z = "3000z"
zenny_9000z = "9000z"
zenny_10000z = "10000z"
zenny_30000z = "30000z"
zenny_50000z = "50000z"
bugfrag_30 = "30 BugFrags"
bugfrag_10 = "10 BugFrags"
bugfrag_01 = "1 BugFrag"
## SubChips
MiniEnrg = "MiniEnrg"
FullEnrg = "FullEnrg"
Unlocker = "Unlocker"
Untrap = "Untrap"
LockEnmy = "LockEnmy"
## KeyItems
Progressive_Undernet_Rank = "Progressive Undernet Rank"
CACDCPas = "CACDCPas"
CSciPas = "CSciPass"
CYokaPas = "CYokaPas"
CBeacPas = "CBeacPas"
WWW_ID = "WWW ID"
OrderSys = "OrderSys"
Mr_Famous_Wristband = "Mr Famous' Wristband"
ModTools = "ModTools"
ExpMem = "ExpMem"
SpinDark = "SpinDark"
SpinPink = "SpinPink"
SpinPurple = "SpinPurple"
SpinOrange = "SpinOrange"
SpinBlue = "SpinBlue"
SpinGrn = "SpinGrn"
SpinRed = "SpinRed"
SpinWht = "SpinWht"
SpinYllw = "SpinYllw"
Parasol = "Parasol"
SubPET = "SubPET"
Needle = "Needle"
PETCase = "PETCase"
Hammer = "Hammer"
## Upgrades
HPMemory = "HPMemory"
RegUP1 = "RegUP1"
RegUP2 = "RegUP2"
RegUP3 = "RegUP3"
SubMem = "SubMem"
Victory = "Victory"

View File

@ -0,0 +1,313 @@
from enum import Enum
class LocationName():
## Blue Mystery Datas
# ACDC Area
ACDC_1_Southwest_BMD = "ACDC 1 Southwest BMD"
ACDC_1_Northeast_BMD = "ACDC 1 Northeast BMD"
ACDC_2_Center_BMD = "ACDC 2 Center BMD"
ACDC_2_North_BMD = "ACDC 2 North BMD"
ACDC_3_Southwest_BMD = "ACDC 3 Southwest BMD"
ACDC_3_Northeast_BMD = "ACDC 3 Northeast BMD"
# SciLab Area
SciLab_1_WWW_BMD = "SciLab 1 WWW BMD"
SciLab_1_East_BMD = "SciLab 1 East BMD"
SciLab_2_West_BMD = "SciLab 2 West BMD"
SciLab_2_South_BMD = "SciLab 2 South BMD"
# Yoka Area
Yoka_1_North_BMD = "Yoka 1 North BMD"
Yoka_1_WWW_BMD = "Yoka 1 WWW BMD"
Yoka_2_Upper_BMD = "Yoka 2 Upper BMD"
Yoka_2_Lower_BMD = "Yoka 2 Lower BMD"
# Beach Area
Beach_1_BMD = "Beach 1 BMD"
Beach_2_West_BMD = "Beach 2 West BMD"
Beach_2_East_BMD = "Beach 2 East BMD"
# Undernet Area
Undernet_1_South_BMD = "Undernet 1 South BMD"
Undernet_1_WWW_BMD = "Undernet 1 WWW BMD"
Undernet_2_Upper_BMD = "Undernet 2 Upper BMD"
Undernet_2_Lower_BMD = "Undernet 2 Lower BMD"
Undernet_3_South_BMD = "Undernet 3 South BMD"
Undernet_3_Central_BMD = "Undernet 3 Central BMD"
Undernet_4_Bottom_West_BMD = "Undernet 4 Bottom West BMD"
Undernet_4_Top_Pillar_BMD = "Undernet 4 Top Pillar BMD"
Undernet_4_Top_North_BMD = "Undernet 4 Top North BMD"
Undernet_5_Upper_BMD = "Undernet 5 Upper BMD"
Undernet_5_Lower_BMD = "Undernet 5 Lower BMD"
Undernet_6_East_BMD = "Undernet 6 East BMD"
Undernet_6_Central_BMD = "Undernet 6 Central BMD"
Undernet_6_TV_BMD = "Undernet 6 TV BMD"
Undernet_7_West_BMD = "Undernet 7 West BMD"
Undernet_7_Northwest_BMD = "Undernet 7 Northwest BMD"
Undernet_7_Northeast_BMD = "Undernet 7 Northeast BMD"
# Secret Area
Secret_1_South_BMD = "Secret 1 South BMD"
Secret_1_Northeast_BMD = "Secret 1 Northeast BMD"
Secret_1_Northwest_BMD = "Secret 1 Northwest BMD"
Secret_2_Upper_BMD = "Secret 2 Upper BMD"
Secret_2_Lower_BMD = "Secret 2 Lower BMD"
Secret_2_Island_BMD = "Secret 2 Island BMD"
Secret_3_South_BMD = "Secret 3 South BMD"
Secret_3_Island_BMD = "Secret 3 Island BMD"
Secret_3_BugFrag_BMD = "Secret 3 BugFrag BMD"
# School Area
School_1_Entrance_BMD = "School 1 Entrance BMD"
School_1_North_Central_BMD = "School 1 North Central BMD"
School_1_Far_West_BMD_2 = "School 1 Far West BMD 2"
School_2_Entrance_BMD = "School 2 Entrance BMD"
School_2_South_BMD = "School 2 South BMD"
School_2_Mainframe_BMD = "School 2 Mainframe BMD"
# Zoo Area
Zoo_1_East_BMD = "Zoo 1 East BMD"
Zoo_1_Central_BMD = "Zoo 1 Central BMD"
Zoo_1_North_BMD = "Zoo 1 North BMD"
Zoo_2_East_BMD = "Zoo 2 East BMD"
Zoo_2_Central_BMD = "Zoo 2 Central BMD"
Zoo_2_West_BMD = "Zoo 2 West BMD"
Zoo_3_North_BMD = "Zoo 3 North BMD"
Zoo_3_Central_BMD = "Zoo 3 Central BMD"
Zoo_3_Path_BMD = "Zoo 3 Path BMD"
Zoo_3_Northwest_BMD = "Zoo 3 Northwest BMD"
Zoo_4_West_BMD = "Zoo 4 West BMD"
Zoo_4_Northwest_BMD = "Zoo 4 Northwest BMD"
Zoo_4_Southeast_BMD = "Zoo 4 Southeast BMD"
# Hades Area
Hades_South_BMD = "Hades South BMD"
# Hospital Area
Hospital_1_Center_BMD = "Hospital 1 Center BMD"
Hospital_1_West_BMD = "Hospital 1 West BMD"
Hospital_1_North_BMD = "Hospital 1 North BMD"
Hospital_2_Southwest_BMD = "Hospital 2 Southwest BMD"
Hospital_2_Central_BMD = "Hospital 2 Central BMD"
Hospital_2_Island_BMD = "Hospital 2 Island BMD"
Hospital_3_Central_BMD = "Hospital 3 Central BMD"
Hospital_3_West_BMD = "Hospital 3 West BMD"
Hospital_3_Northwest_BMD = "Hospital 3 Northwest BMD"
Hospital_4_Central_BMD = "Hospital 4 Central BMD"
Hospital_4_Southeast_BMD = "Hospital 4 Southeast BMD"
Hospital_4_North_BMD = "Hospital 4 North BMD"
Hospital_5_Southwest_BMD = "Hospital 5 Southwest BMD"
Hospital_5_Northeast_BMD = "Hospital 5 Northeast BMD"
Hospital_5_Island_BMD = "Hospital 5 Island BMD"
# WWW Area
WWW_1_Central_BMD = "WWW 1 Central BMD"
WWW_1_West_BMD = "WWW 1 West BMD"
WWW_1_East_BMD = "WWW 1 East BMD"
WWW_2_East_BMD = "WWW 2 East BMD"
WWW_2_Northwest_BMD = "WWW 2 Northwest BMD"
WWW_3_East_BMD = "WWW 3 East BMD"
WWW_3_North_BMD = "WWW 3 North BMD"
WWW_4_Northwest_BMD = "WWW 4 Northwest BMD"
WWW_4_Central_BMD = "WWW 4 Central BMD"
# Misc Net Area
ACDC_Dog_House_BMD = "ACDC Dog House BMD"
ACDC_Lans_Security_Panel_BMD = "ACDC Lan's Security Panel BMD"
ACDC_Yais_Phone_BMD = "ACDC Yai's Phone BMD"
ACDC_NumberMan_Display_BMD = "ACDC NumberMan Display BMD"
ACDC_Tank_BMD_1 = "ACDC Tank BMD 1"
ACDC_Tank_BMD_2 = "ACDC Tank BMD 2"
ACDC_School_Server_BMD_1 = "ACDC School Server BMD 1"
ACDC_School_Server_BMD_2 = "ACDC School Server BMD 2"
ACDC_School_Blackboard_BMD = "ACDC School Blackboard BMD"
SciLab_Vending_Machine_BMD = "SciLab Vending Machine BMD"
SciLab_Virus_Lab_Door_BMD_1 = "SciLab Virus Lab Door BMD 1"
SciLab_Virus_Lab_Door_BMD_2 = "SciLab Virus Lab Door BMD 2"
SciLab_Dads_Computer_BMD = "SciLab Dad's Computer BMD"
Yoka_Armor_BMD = "Yoka Armor BMD"
Yoka_TV_BMD = "Yoka TV BMD"
Yoka_Hot_Spring_BMD = "Yoka Hot Spring BMD"
Yoka_Ticket_Machine_BMD = "Yoka Ticket Machine BMD"
Yoka_Giraffe_BMD = "Yoka Giraffe BMD"
Yoka_Panda_BMD = "Yoka Panda BMD"
Beach_Hospital_Bed_BMD = "Beach Hospital Bed BMD"
Beach_TV_BMD = "Beach TV BMD"
Beach_Vending_Machine_BMD = "Beach Vending Machine BMD"
Beach_News_Van_BMD = "Beach News Van BMD"
Beach_Battle_Console_BMD = "Beach Battle Console BMD"
Beach_Security_System_BMD = "Beach Security System BMD"
Beach_Broadcast_Computer_BMD = "Beach Broadcast Computer BMD"
Hades_Gargoyle_BMD = "Hades Gargoyle BMD"
WWW_Wall_BMD = "WWW Wall BMD"
Mayls_HP_BMD = "Mayl's HP BMD"
Yais_HP_BMD_1 = "Yai's HP BMD 1"
Yais_HP_BMD_2 = "Yai's HP BMD 2"
Dexs_HP_BMD_1 = "Dex's HP BMD 1"
Dexs_HP_BMD_2 = "Dex's HP BMD 2"
Tamakos_HP_BMD = "Tamako's HP BMD"
# Story Item BMDs
Undernet_7_Upper_BMD = "Undernet 7 Upper BMD"
School_1_KeyDataA_BMD = "School 1 KeyDataA BMD"
School_1_KeyDataB_BMD = "School 1 KeyDataB BMD"
School_1_KeyDataC_BMD = "School 1 KeyDataC BMD"
School_2_CodeC_BMD = "School 2 CodeC BMD"
School_2_CodeA_BMD = "School 2 CodeA BMD"
School_2_CodeB_BMD = "School 2 CodeB BMD"
Hades_HadesKey_BMD = "Hades HadesKey BMD"
WWW_1_South_BMD = "WWW 1 South BMD"
WWW_2_West_BMD = "WWW 2 West BMD"
WWW_3_South_BMD = "WWW 3 South BMD"
WWW_4_East_BMD = "WWW 4 East BMD"
## Purple Mystery Data
ACDC_1_PMD = "ACDC 1 PMD"
Yoka_1_PMD = "Yoka 1 PMD"
Beach_1_PMD = "Beach 1 PMD"
Undernet_7_PMD = "Undernet 7 PMD"
Mayls_HP_PMD = "Mayl's HP PMD"
SciLab_Dads_Computer_PMD = "SciLab Dad's Computer PMD"
Zoo_Panda_PMD = "Zoo Panda PMD"
Beach_DNN_Security_Panel_PMD = "Beach DNN Security Panel PMD"
Beach_DNN_Main_Console_PMD = "Beach DNN Main Console PMD"
Tamakos_HP_PMD = "Tamako's HP PMD"
## Overworld Items
Yoka_Mr_Quiz = "Yoka Mr Quiz"
Yoka_Quiz_Master = "Yoka Quiz Master"
Hospital_Quiz_Queen = "Hospital Quiz Queen"
Hades_Quiz_King = "Hades Quiz King"
ACDC_SonicWav_W_Trade = "ACDC SonicWav W Trade"
ACDC_Bubbler_C_Trade = "ACDC Bubbler C Trade"
ACDC_Recov120_S_Trade = "ACDC Recov120 S Trade"
SciLab_Shake1_S_Trade = "SciLab Shake1 S Trade"
Yoka_FireSwrd_P_Trade = "Yoka FireSwrd P Trade"
Hospital_DynaWav_V_Trade = "Hospital DynaWav V Trade"
Beach_DNN_WideSwrd_C_Trade = "Beach DNN WideSwrd C Trade"
Beach_DNN_HoleMetr_H_Trade = "Beach DNN HoleMetr H Trade"
Beach_DNN_Shadow_J_Trade = "Beach DNN Shadow J Trade"
Hades_GrabBack_K_Trade = "Hades GrabBack K Trade"
Comedian = "Comedian"
Villain = "Villain"
Mod_Tools_Guy = "Mod Tools Guy"
ACDC_School_Desk = "ACDC School Desk"
ACDC_Class_5B_Bookshelf = "ACDC Class 5B Bookshelf"
SciLab_Garbage_Can = "SciLab Garbage Can"
Yoka_Inn_Jars = "Yoka Inn Jars"
Yoka_Zoo_Garbage = "Yoka Zoo Garbage"
Beach_Department_Store = "Beach Department Store"
Beach_Hospital_Plaque = "Beach Hospital Plaque"
Beach_Hospital_Pink_Door = "Beach Hospital Pink Door"
Beach_Hospital_Tree = "Beach Hospital Tree"
Beach_Hospital_Hidden_Conversation = "Beach Hospital Hidden Conversation"
Beach_Hospital_Girl = "Beach Hospital Girl"
Beach_DNN_Kiosk = "Beach DNN Kiosk"
Beach_DNN_Boxes = "Beach DNN Boxes"
Beach_DNN_Poster = "Beach DNN Poster"
Hades_Boat_Dock = "Hades Boat Dock"
WWW_Control_Room_1_Screen = "WWW Control Room 1 Screen"
WWW_Wilys_Desk = "WWW Wily's Desk"
Undernet_4_Pillar_Prog = "Undernet 4 Pillar Prog"
## Numberman Codes
Numberman_Code_01 = "Numberman Code 01"
Numberman_Code_02 = "Numberman Code 02"
Numberman_Code_03 = "Numberman Code 03"
Numberman_Code_04 = "Numberman Code 04"
Numberman_Code_05 = "Numberman Code 05"
Numberman_Code_06 = "Numberman Code 06"
Numberman_Code_07 = "Numberman Code 07"
Numberman_Code_08 = "Numberman Code 08"
Numberman_Code_09 = "Numberman Code 09"
Numberman_Code_10 = "Numberman Code 10"
Numberman_Code_11 = "Numberman Code 11"
Numberman_Code_12 = "Numberman Code 12"
Numberman_Code_13 = "Numberman Code 13"
Numberman_Code_14 = "Numberman Code 14"
Numberman_Code_15 = "Numberman Code 15"
Numberman_Code_16 = "Numberman Code 16"
Numberman_Code_17 = "Numberman Code 17"
Numberman_Code_18 = "Numberman Code 18"
Numberman_Code_19 = "Numberman Code 19"
Numberman_Code_20 = "Numberman Code 20"
Numberman_Code_21 = "Numberman Code 21"
Numberman_Code_22 = "Numberman Code 22"
Numberman_Code_23 = "Numberman Code 23"
Numberman_Code_24 = "Numberman Code 24"
Numberman_Code_25 = "Numberman Code 25"
Numberman_Code_26 = "Numberman Code 26"
Numberman_Code_27 = "Numberman Code 27"
Numberman_Code_28 = "Numberman Code 28"
Numberman_Code_29 = "Numberman Code 29"
Numberman_Code_30 = "Numberman Code 30"
Numberman_Code_31 = "Numberman Code 31"
## Jobs
Please_deliver_this = "Job: Please deliver this"
My_Navi_is_sick = "Job: My Navi is sick"
Help_me_with_my_son = "Job: Help me with my son!"
Transmission_error = "Job: Transmission error"
Chip_Prices = "Job: Chip Prices"
Im_broke = "Job: I'm broke?!"
Rare_chips_for_cheap = "Job: Rare chips for cheap!"
Be_my_boyfriend = "Job: Be my boyfriend"
Will_you_deliver = "Job: Will you deliver?"
Look_for_friends = "Job: Look for friends (Tora)"
Stuntmen_wanted = "Job: Stuntmen wanted! (Tora)"
Riot_stopped = "Job: Riot stopped (Tora)"
Gathering_Data = "Job: Gathering Data (Tora)"
Somebody_please_help = "Job: Somebody, please help!"
Looking_for_condor = "Job: Looking for condor"
Help_with_rehab = "Job: Help with rehab"
Old_Master = "Job: Old Master"
Catching_gang_members = "Job: Catching gang members"
Please_adopt_a_virus = "Job: Please adopt a virus!"
Legendary_Tomes = "Job: Legendary Tomes"
Legendary_Tomes_Treasure = "Job: Legendary Tomes - Treasure"
Hide_and_seek_First_Child = "Job: Hide and seek! First Child"
Hide_and_seek_Second_Child = "Job: Hide and seek! Second Child"
Hide_and_seek_Third_Child = "Job: Hide and seek! Third Child"
Hide_and_seek_Fourth_Child = "Job: Hide and seek! Fourth Child"
Hide_and_seek_Completion = "Job: Hide and seek! Completion"
Finding_the_blue_Navi = "Job: Finding the blue Navi"
Give_your_support = "Job: Give your support"
Stamp_collecting = "Job: Stamp collecting"
Help_with_a_will = "Job: Help with a will"
Alpha_Defeated = "Alpha Defeated"
## Chocolates
Chocolate_Shop_01 = "Chocolate Shop 01"
Chocolate_Shop_02 = "Chocolate Shop 02"
Chocolate_Shop_03 = "Chocolate Shop 03"
Chocolate_Shop_04 = "Chocolate Shop 04"
Chocolate_Shop_05 = "Chocolate Shop 05"
Chocolate_Shop_06 = "Chocolate Shop 06"
Chocolate_Shop_07 = "Chocolate Shop 07"
Chocolate_Shop_08 = "Chocolate Shop 08"
Chocolate_Shop_09 = "Chocolate Shop 09"
Chocolate_Shop_10 = "Chocolate Shop 10"
Chocolate_Shop_11 = "Chocolate Shop 11"
Chocolate_Shop_12 = "Chocolate Shop 12"
Chocolate_Shop_13 = "Chocolate Shop 13"
Chocolate_Shop_14 = "Chocolate Shop 14"
Chocolate_Shop_15 = "Chocolate Shop 15"
Chocolate_Shop_16 = "Chocolate Shop 16"
Chocolate_Shop_17 = "Chocolate Shop 17"
Chocolate_Shop_18 = "Chocolate Shop 18"
Chocolate_Shop_19 = "Chocolate Shop 19"
Chocolate_Shop_20 = "Chocolate Shop 20"
Chocolate_Shop_21 = "Chocolate Shop 21"
Chocolate_Shop_22 = "Chocolate Shop 22"
Chocolate_Shop_23 = "Chocolate Shop 23"
Chocolate_Shop_24 = "Chocolate Shop 24"
Chocolate_Shop_25 = "Chocolate Shop 25"
Chocolate_Shop_26 = "Chocolate Shop 26"
Chocolate_Shop_27 = "Chocolate Shop 27"
Chocolate_Shop_28 = "Chocolate Shop 28"
Chocolate_Shop_29 = "Chocolate Shop 29"
Chocolate_Shop_30 = "Chocolate Shop 30"
Chocolate_Shop_31 = "Chocolate Shop 31"
Chocolate_Shop_32 = "Chocolate Shop 32"

48
worlds/mmbn3/Options.py Normal file
View File

@ -0,0 +1,48 @@
from Options import Choice, Range, DefaultOnToggle
class ExtraRanks(Range):
"""
How many extra Undernet Ranks to add to the pool in place of filler items.
The more ranks there are, the faster the game will go.
Depending on your other options, you might not have enough filler items to replace.
If generation errors occur, consider reducing this value.
"""
display_name = "Extra Undernet Ranks"
range_start = 0
range_end = 16
default = 0
class IncludeJobs(DefaultOnToggle):
"""
Whether Jobs can be included in logic.
"""
display_name = "Include Jobs"
# Possible logic options:
# - Include Number Trader
# - Include Secret Area
# - Overworld Item Restrictions
# - Cybermetro Locked Shortcuts
class TradeQuestHinting(Choice):
"""
Whether NPCs offering Chip Trades should show what item they provide.
None - NPCs will not provide any information on what item they will give
Partial - NPCs will state if an item is progression or not, but not the specific item
Full - NPCs will state what item they will give, providing an Archipelago Hint when doing so
"""
display_name = "Trade Quest Hinting"
option_none = 0
option_partial = 1
option_full = 2
default = 2
MMBN3Options = {
"extra_ranks": ExtraRanks,
"include_jobs": IncludeJobs,
"trade_quest_hinting": TradeQuestHinting,
}

354
worlds/mmbn3/Regions.py Normal file
View File

@ -0,0 +1,354 @@
import typing
from .Names.LocationName import LocationName
class RegionName:
Menu = "Menu"
ACDC_Overworld = "ACDC Overworld"
ACDC_Cyberworld = "ACDC Cyberworld"
SciLab_Overworld = "SciLab Overworld"
SciLab_Cyberworld = "SciLab Cyberworld"
Yoka_Overworld = "Yoka Overworld"
Yoka_Cyberworld = "Yoka Cyberworld"
Beach_Overworld = "Beach Overworld"
Beach_Cyberworld = "Beach Cyberworld"
Undernet = "Undernet"
Deep_Undernet = "Deep Undernet"
Secret_Area = "Secret Area"
WWW_Island = "WWW Island"
class RegionInfo:
name: str
connections: typing.List[str]
locations: typing.List[str]
def __init__(self, name, connections, locations):
self.name = name
self.connections = connections
self.locations = locations
regions = [
RegionInfo(RegionName.Menu, [RegionName.ACDC_Overworld], []),
RegionInfo(RegionName.ACDC_Overworld,
[RegionName.ACDC_Cyberworld, RegionName.SciLab_Overworld, RegionName.Yoka_Overworld, RegionName.Beach_Overworld],
[
LocationName.ACDC_SonicWav_W_Trade,
LocationName.ACDC_Bubbler_C_Trade,
LocationName.ACDC_Recov120_S_Trade,
LocationName.ACDC_School_Desk,
LocationName.ACDC_Class_5B_Bookshelf,
LocationName.School_1_Entrance_BMD,
LocationName.School_1_North_Central_BMD,
LocationName.School_1_Far_West_BMD_2,
LocationName.School_1_KeyDataA_BMD,
LocationName.School_1_KeyDataB_BMD,
LocationName.School_1_KeyDataC_BMD,
LocationName.School_2_South_BMD,
LocationName.School_2_Entrance_BMD,
LocationName.School_2_Mainframe_BMD,
LocationName.School_2_CodeA_BMD,
LocationName.School_2_CodeB_BMD,
LocationName.School_2_CodeC_BMD,
LocationName.ACDC_Dog_House_BMD,
LocationName.ACDC_Lans_Security_Panel_BMD,
LocationName.ACDC_Yais_Phone_BMD,
LocationName.ACDC_NumberMan_Display_BMD,
LocationName.ACDC_Tank_BMD_1,
LocationName.ACDC_Tank_BMD_2,
LocationName.ACDC_School_Server_BMD_1,
LocationName.ACDC_School_Server_BMD_2,
LocationName.ACDC_School_Blackboard_BMD,
LocationName.Numberman_Code_01,
LocationName.Numberman_Code_02,
LocationName.Numberman_Code_03,
LocationName.Numberman_Code_04,
LocationName.Numberman_Code_05,
LocationName.Numberman_Code_06,
LocationName.Numberman_Code_07,
LocationName.Numberman_Code_08,
LocationName.Numberman_Code_09,
LocationName.Numberman_Code_10,
LocationName.Numberman_Code_11,
LocationName.Numberman_Code_12,
LocationName.Numberman_Code_13,
LocationName.Numberman_Code_14,
LocationName.Numberman_Code_15,
LocationName.Numberman_Code_16,
LocationName.Numberman_Code_17,
LocationName.Numberman_Code_18,
LocationName.Numberman_Code_19,
LocationName.Numberman_Code_20,
LocationName.Numberman_Code_21,
LocationName.Numberman_Code_22,
LocationName.Numberman_Code_23,
LocationName.Numberman_Code_24,
LocationName.Numberman_Code_25,
LocationName.Numberman_Code_26,
LocationName.Numberman_Code_27,
LocationName.Numberman_Code_28,
LocationName.Numberman_Code_29,
LocationName.Numberman_Code_30,
LocationName.Numberman_Code_31,
LocationName.Mayls_HP_BMD,
LocationName.Yais_HP_BMD_1,
LocationName.Yais_HP_BMD_2,
LocationName.Dexs_HP_BMD_1,
LocationName.Dexs_HP_BMD_2,
LocationName.Mayls_HP_PMD
]),
RegionInfo(RegionName.ACDC_Cyberworld,
[RegionName.SciLab_Cyberworld, RegionName.Yoka_Cyberworld, RegionName.Beach_Cyberworld],
[
LocationName.ACDC_1_Southwest_BMD,
LocationName.ACDC_1_Northeast_BMD,
LocationName.ACDC_1_PMD,
LocationName.ACDC_2_Center_BMD,
LocationName.ACDC_2_North_BMD,
LocationName.ACDC_3_Southwest_BMD,
LocationName.ACDC_3_Northeast_BMD,
]),
RegionInfo(RegionName.SciLab_Overworld,
[RegionName.SciLab_Cyberworld, RegionName.ACDC_Overworld, RegionName.Yoka_Overworld, RegionName.Beach_Overworld],
[
LocationName.SciLab_Shake1_S_Trade,
LocationName.SciLab_Garbage_Can,
LocationName.SciLab_Vending_Machine_BMD,
LocationName.SciLab_Virus_Lab_Door_BMD_1,
LocationName.SciLab_Virus_Lab_Door_BMD_2,
LocationName.SciLab_Dads_Computer_BMD,
LocationName.SciLab_Dads_Computer_PMD,
LocationName.Please_deliver_this,
LocationName.My_Navi_is_sick,
LocationName.Help_me_with_my_son,
LocationName.Transmission_error,
LocationName.Chip_Prices,
LocationName.Im_broke,
LocationName.Rare_chips_for_cheap,
LocationName.Be_my_boyfriend,
LocationName.Will_you_deliver,
#LocationName.Look_for_friends,
#LocationName.Stuntmen_wanted,
#LocationName.Riot_stopped,
#LocationName.Gathering_Data,
LocationName.Somebody_please_help,
LocationName.Looking_for_condor,
LocationName.Help_with_rehab,
LocationName.Old_Master,
LocationName.Catching_gang_members,
LocationName.Please_adopt_a_virus,
LocationName.Legendary_Tomes,
LocationName.Legendary_Tomes_Treasure,
LocationName.Hide_and_seek_First_Child,
LocationName.Hide_and_seek_Second_Child,
LocationName.Hide_and_seek_Third_Child,
LocationName.Hide_and_seek_Fourth_Child,
LocationName.Hide_and_seek_Completion,
LocationName.Finding_the_blue_Navi,
LocationName.Give_your_support,
LocationName.Stamp_collecting,
LocationName.Help_with_a_will
]),
RegionInfo(RegionName.SciLab_Cyberworld,
[RegionName.ACDC_Cyberworld, RegionName.Yoka_Cyberworld, RegionName.Beach_Cyberworld,RegionName.Deep_Undernet],
[
LocationName.SciLab_1_East_BMD,
LocationName.SciLab_1_WWW_BMD,
LocationName.SciLab_2_South_BMD,
LocationName.SciLab_2_West_BMD
]),
RegionInfo(RegionName.Yoka_Overworld,
[RegionName.Yoka_Cyberworld, RegionName.ACDC_Overworld, RegionName.SciLab_Overworld, RegionName.Beach_Overworld, RegionName.Secret_Area],
[
LocationName.Yoka_Mr_Quiz,
LocationName.Yoka_Quiz_Master,
LocationName.Yoka_FireSwrd_P_Trade,
LocationName.Yoka_Inn_Jars,
LocationName.Yoka_Zoo_Garbage,
LocationName.Zoo_Panda_PMD,
LocationName.Zoo_1_East_BMD,
LocationName.Zoo_1_North_BMD,
LocationName.Zoo_1_Central_BMD,
LocationName.Zoo_2_East_BMD,
LocationName.Zoo_2_Central_BMD,
LocationName.Zoo_2_West_BMD,
LocationName.Zoo_3_North_BMD,
LocationName.Zoo_3_Central_BMD,
LocationName.Zoo_3_Path_BMD,
LocationName.Zoo_3_Northwest_BMD,
LocationName.Zoo_4_West_BMD,
LocationName.Zoo_4_Northwest_BMD,
LocationName.Zoo_4_Southeast_BMD,
LocationName.Yoka_TV_BMD,
LocationName.Yoka_Armor_BMD,
LocationName.Yoka_Hot_Spring_BMD,
LocationName.Yoka_Ticket_Machine_BMD,
LocationName.Yoka_Giraffe_BMD,
LocationName.Yoka_Panda_BMD,
LocationName.Tamakos_HP_BMD,
LocationName.Tamakos_HP_PMD,
LocationName.Comedian,
LocationName.Chocolate_Shop_01,
LocationName.Chocolate_Shop_02,
LocationName.Chocolate_Shop_03,
LocationName.Chocolate_Shop_04,
LocationName.Chocolate_Shop_05,
LocationName.Chocolate_Shop_06,
LocationName.Chocolate_Shop_07,
LocationName.Chocolate_Shop_08,
LocationName.Chocolate_Shop_09,
LocationName.Chocolate_Shop_10,
LocationName.Chocolate_Shop_11,
LocationName.Chocolate_Shop_12,
LocationName.Chocolate_Shop_13,
LocationName.Chocolate_Shop_14,
LocationName.Chocolate_Shop_15,
LocationName.Chocolate_Shop_16,
LocationName.Chocolate_Shop_17,
LocationName.Chocolate_Shop_18,
LocationName.Chocolate_Shop_19,
LocationName.Chocolate_Shop_20,
LocationName.Chocolate_Shop_21,
LocationName.Chocolate_Shop_22,
LocationName.Chocolate_Shop_23,
LocationName.Chocolate_Shop_24,
LocationName.Chocolate_Shop_25,
LocationName.Chocolate_Shop_26,
LocationName.Chocolate_Shop_27,
LocationName.Chocolate_Shop_28,
LocationName.Chocolate_Shop_29,
LocationName.Chocolate_Shop_30,
LocationName.Chocolate_Shop_31,
LocationName.Chocolate_Shop_32
]),
RegionInfo(RegionName.Yoka_Cyberworld,
[RegionName.ACDC_Cyberworld, RegionName.SciLab_Cyberworld, RegionName.Beach_Cyberworld],
[
LocationName.Yoka_1_North_BMD,
LocationName.Yoka_1_WWW_BMD,
LocationName.Yoka_1_PMD,
LocationName.Yoka_2_Lower_BMD,
LocationName.Yoka_2_Upper_BMD,
]),
RegionInfo(RegionName.Beach_Overworld,
[RegionName.ACDC_Overworld, RegionName.SciLab_Overworld, RegionName.Yoka_Overworld, RegionName.WWW_Island],
[
LocationName.Hospital_Quiz_Queen,
LocationName.Hades_Quiz_King,
LocationName.Hospital_DynaWav_V_Trade,
LocationName.Beach_DNN_WideSwrd_C_Trade,
LocationName.Beach_DNN_HoleMetr_H_Trade,
LocationName.Beach_DNN_Shadow_J_Trade,
LocationName.Hades_GrabBack_K_Trade,
#LocationName.Mod_Tools_Guy,
LocationName.Beach_Department_Store,
LocationName.Beach_Hospital_Plaque,
LocationName.Beach_Hospital_Pink_Door,
LocationName.Beach_Hospital_Tree,
LocationName.Beach_Hospital_Hidden_Conversation,
LocationName.Beach_Hospital_Girl,
LocationName.Beach_DNN_Kiosk,
LocationName.Beach_DNN_Boxes,
LocationName.Beach_DNN_Poster,
LocationName.Hades_Boat_Dock,
LocationName.Hades_South_BMD,
LocationName.Hades_Gargoyle_BMD,
LocationName.Hospital_1_North_BMD,
LocationName.Hospital_1_West_BMD,
LocationName.Hospital_1_Center_BMD,
LocationName.Hospital_2_Island_BMD,
LocationName.Hospital_2_Central_BMD,
LocationName.Hospital_2_Southwest_BMD,
LocationName.Hospital_3_West_BMD,
LocationName.Hospital_3_Central_BMD,
LocationName.Hospital_3_Northwest_BMD,
LocationName.Hospital_4_North_BMD,
LocationName.Hospital_4_Central_BMD,
LocationName.Hospital_4_Southeast_BMD,
LocationName.Hospital_5_Island_BMD,
LocationName.Hospital_5_Northeast_BMD,
LocationName.Hospital_5_Southwest_BMD,
LocationName.Beach_Hospital_Bed_BMD,
LocationName.Beach_TV_BMD,
LocationName.Beach_Vending_Machine_BMD,
LocationName.Beach_News_Van_BMD,
LocationName.Beach_Battle_Console_BMD,
LocationName.Beach_Security_System_BMD,
LocationName.Beach_Broadcast_Computer_BMD,
LocationName.Beach_DNN_Security_Panel_PMD,
LocationName.Beach_DNN_Main_Console_PMD,
LocationName.Undernet_6_TV_BMD
]),
RegionInfo(RegionName.Beach_Cyberworld,
[RegionName.ACDC_Cyberworld, RegionName.SciLab_Cyberworld, RegionName.Yoka_Cyberworld, RegionName.Undernet],
[
LocationName.Beach_1_BMD,
LocationName.Beach_1_PMD,
LocationName.Beach_2_East_BMD,
LocationName.Beach_2_West_BMD,
LocationName.Villain
]),
RegionInfo(RegionName.Undernet,
[],
[
LocationName.Undernet_1_South_BMD,
LocationName.Undernet_1_WWW_BMD,
LocationName.Undernet_2_Lower_BMD,
LocationName.Undernet_2_Upper_BMD,
LocationName.Undernet_3_South_BMD,
LocationName.Undernet_3_Central_BMD,
LocationName.Undernet_4_Pillar_Prog,
LocationName.Undernet_4_Top_North_BMD,
LocationName.Undernet_4_Bottom_West_BMD,
LocationName.Undernet_4_Top_Pillar_BMD,
LocationName.Undernet_5_Upper_BMD
]),
RegionInfo(RegionName.Deep_Undernet,
[],
[
LocationName.Undernet_5_Lower_BMD,
LocationName.Undernet_6_East_BMD,
LocationName.Undernet_6_Central_BMD,
LocationName.Undernet_7_PMD,
LocationName.Undernet_7_West_BMD,
LocationName.Undernet_7_Northeast_BMD,
LocationName.Undernet_7_Northwest_BMD,
LocationName.Undernet_7_Upper_BMD,
]),
RegionInfo(RegionName.WWW_Island,
[],
[
LocationName.WWW_Control_Room_1_Screen,
LocationName.WWW_Wilys_Desk,
LocationName.WWW_Wall_BMD,
LocationName.WWW_1_East_BMD,
LocationName.WWW_1_West_BMD,
LocationName.WWW_1_Central_BMD,
#LocationName.WWW_1_South_BMD,
LocationName.WWW_2_East_BMD,
LocationName.WWW_2_Northwest_BMD,
#LocationName.WWW_2_West_BMD,
LocationName.WWW_3_East_BMD,
LocationName.WWW_3_North_BMD,
#LocationName.WWW_3_South_BMD,
LocationName.WWW_4_Central_BMD,
LocationName.WWW_4_Northwest_BMD,
#LocationName.WWW_4_East_BMD
LocationName.Alpha_Defeated
]),
RegionInfo(RegionName.Secret_Area,
[],
[
LocationName.Secret_1_South_BMD,
LocationName.Secret_1_Northeast_BMD,
LocationName.Secret_1_Northwest_BMD,
LocationName.Secret_2_Island_BMD,
LocationName.Secret_2_Lower_BMD,
LocationName.Secret_2_Upper_BMD,
LocationName.Secret_3_Island_BMD,
LocationName.Secret_3_South_BMD,
LocationName.Secret_3_BugFrag_BMD
])
]

347
worlds/mmbn3/Rom.py Normal file
View File

@ -0,0 +1,347 @@
from BaseClasses import ItemClassification
from Patch import APDeltaPatch
import Utils
import os
import hashlib
import bsdiff4
from .lz10 import gba_decompress, gba_compress
from .BN3RomUtils import ArchiveToReferences, read_u16_le, read_u32_le, int16_to_byte_list_le, int32_to_byte_list_le,\
generate_progressive_undernet, ArchiveToSizeComp, ArchiveToSizeUncomp, generate_item_message, \
generate_external_item_message, generate_text_bytes
from .Items import ItemType
CHECKSUM_BLUE = "6fe31df0144759b34ad666badaacc442"
def list_contains_subsequence(lst, sublist) -> bool:
sub_index = 0
for index, item in enumerate(lst):
if item == sublist[sub_index]:
sub_index += 1
if sub_index >= len(sublist):
return True
else:
sub_index = 0
return False
class ArchiveScript:
def __init__(self, index, message_bytes):
self.index = index
self.messageBoxes = []
self.set_bytes(message_bytes)
def get_bytes(self):
data = []
for message in self.messageBoxes:
data.extend(message)
return data
def set_bytes(self, message_bytes):
self.messageBoxes = []
message_box = []
command_index = 0
for byte in message_bytes:
if command_index <= 0 and (byte == 0xE9 or byte == 0xE7):
if byte == 0xE9: # More textboxes to come, don't end it yet
message_box.append(byte)
self.messageBoxes.append(message_box)
else: # It's the end of the script, add another message to end it after this one
self.messageBoxes.append(message_box)
self.messageBoxes.append([0xE7])
message_box = []
else:
if command_index <- 0:
# We can hit a command that might contain an E9 or an E7. If we do, skip checking the next few bytes
if byte == 0xF6: # CheckItem
command_index = 7
if byte == 0xF3: # CheckFlag
command_index = 7
if byte == 0xF2: # FlagSet
command_index = 4
command_index -= 1
message_box.append(byte)
# If there's still bytes left over, add them even if we didn't hit an end
if len(message_box) > 0:
self.messageBoxes.append(message_box)
def __str__(self):
s = str(self.index)+' - \n'
for messageBox in self.messageBoxes:
s += ' '+str(["{:02x}".format(x) for x in messageBox])+'\n'
class TextArchive:
def __init__(self, data, offset, size, compressed=True):
self.startOffset = offset
self.compressed = compressed
self.scripts = {}
self.scriptCount = 0xFF
self.references = ArchiveToReferences[offset]
self.unused_indices = [] # A list of places it's okay to inject new scripts
self.progressive_undernet_indices = [] # If this archive has progressive undernet, here they are in order
self.text_changed = False
if compressed:
self.compressedSize = size
self.compressedData = data
self.uncompressedData = gba_decompress(self.compressedData)
self.uncompressedSize = len(self.uncompressedData)
else:
self.uncompressedSize = size
self.uncompressedData = data
self.compressedData = gba_compress(self.uncompressedData)
self.compressedSize = len(self.compressedData)
self.scriptCount = (read_u16_le(self.uncompressedData, 0)) >> 1
for i in range(0, self.scriptCount):
start_offset = read_u16_le(self.uncompressedData, i * 2)
next_offset = read_u16_le(self.uncompressedData, (i + 1) * 2)
if start_offset != next_offset:
message_bytes = list(self.uncompressedData[start_offset:next_offset])
message = ArchiveScript(i, message_bytes)
self.scripts[i] = message
else:
self.unused_indices.append(i)
def generate_data(self, compressed=True):
header = []
scripts = []
byte_offset = self.scriptCount * 2
for i in range(0, self.scriptCount):
header.extend(int16_to_byte_list_le(byte_offset))
if i in self.scripts:
script = self.scripts[i]
scriptbytes = script.get_bytes()
scripts.extend(scriptbytes)
byte_offset += len(scriptbytes)
data = []
data.extend(header)
data.extend(scripts)
byte_data = bytes(data)
if compressed:
byte_data = gba_compress(byte_data)
return bytearray(byte_data)
def inject_item_message(self, script_index, message_indices, new_bytes):
# First step, if the old message had any flag sets or flag clears, we need to keep them.
# Mystery data has a flag set to actually remove the mystery data, and jobs often have a completion flag
for message_index in message_indices:
# print(hex(self.startOffset) + ": " + str(script_index) + " " + str(message_indices))
oldbytes = self.scripts[script_index].messageBoxes[message_index]
for i in range(len(oldbytes)-3):
# F2 00 is the code for "flagSet", with the two bytes after it being the flag to set.
# F2 04 is the code for "flagClear", which also needs to come along for the ride
# Add those to the message box after the other text.
if oldbytes[i] == 0xF2 and (oldbytes[i+1] == 0x00 or oldbytes[i+1] == 0x04):
flag = oldbytes[i:i+4]
new_bytes.extend(flag)
first_message_index = message_indices[0]
# Then, overwrite the existing script with the new one
self.scripts[script_index].messageBoxes[first_message_index] = new_bytes
for index in message_indices[1:]:
self.scripts[script_index].messageBoxes[index] = []
def inject_into_rom(self, modified_rom_data):
working_data = self.generate_data(self.compressed)
# It needs to start on a byte divisible by 4. If the rom data is not, add an FF
while len(modified_rom_data) % 4 != 0:
modified_rom_data.append(0xFF)
new_start_offset = 0x08000000 + len(modified_rom_data)
offset_byte = int32_to_byte_list_le(new_start_offset)
modified_rom_data.extend(working_data)
for offset in self.references:
modified_rom_data[offset:offset+4] = offset_byte
return modified_rom_data
def add_progression_scripts(self):
if len(self.unused_indices) < 9:
# As far as I know, this should literally not be possible.
# Every script I've looked at has dozens of unused indices, so finding 9 (8 plus one "ending" script)
# should be no problem. We re-use these so we don't have to worry about an area getting tons of these
raise AssertionError("Error in generation -- not enough room for progressive undernet in archive "+self.startOffset)
for i in range(9): # There are 8 progressive undernet ranks
new_script_index = self.unused_indices[i]
new_script = ArchiveScript(new_script_index, generate_progressive_undernet(i, self.unused_indices[i+1]))
self.scripts[new_script_index] = new_script
self.progressive_undernet_indices.append(new_script_index)
self.unused_indices = self.unused_indices[9:] # Remove the first eight elements
def inject_item_text(self, item_text, next_message=""):
item_text_bytes = generate_text_bytes(item_text)
next_message_bytes = generate_text_bytes(next_message)
for script_index in self.scripts:
script = self.scripts[script_index]
# Loop through the bytes
for message_index in range(0, len(script.messageBoxes)):
oldbytes = self.scripts[script_index].messageBoxes[message_index]
for i in range(0, len(oldbytes)-1):
if oldbytes[i] == 0x68 and oldbytes[i+1] == 0x68:
oldbytes[i:i+2] = item_text_bytes
self.text_changed = True
# If there's another text box to display, add it to the message bytes before setting them back
if len(next_message) > 0:
oldbytes.extend(next_message_bytes)
# TODO append end message nextline etc.
# I think this is "wait for button press" then "clearmessage"
oldbytes.extend([0xEB, 0xE9])
self.scripts[script_index].messageBoxes[message_index] = oldbytes
class LocalRom:
def __init__(self, file, name=None):
self.name = name
self.changed_archives = {}
self.rom_data = bytearray(get_patched_rom_bytes(file))
def get_data_chunk(self, start_offset, size):
if start_offset+size > len(self.rom_data):
print("Attempting to get data chunk beyond the size of the ROM: "+hex(start_offset)+", ROM size ends at: "+hex(len(self.rom_data)))
return self.rom_data[start_offset:start_offset + size]
def replace_item(self, location, item):
offset = location.text_archive_address
# If the archive is already loaded, use that
if offset in self.changed_archives:
archive = self.changed_archives[offset]
else:
is_compressed = offset in ArchiveToSizeComp.keys()
size = ArchiveToSizeComp[offset] if is_compressed\
else ArchiveToSizeUncomp[offset]
data = self.get_data_chunk(offset, size)
# Check if the archive we want to load has been moved by the patch. This is indicated by a 0xFF 0xFF
# as the first two bytes of the chunk
if data[0] == 0xFF and data[1] == 0xFF:
new_size_bytes = data[2:4]
new_address_le = data[4:8]
# Last byte should be zero since we're dealing with purely ROM address space
new_address_le[3] = 0x0
size = read_u16_le(new_size_bytes, 0)
data = self.get_data_chunk(read_u32_le(new_address_le, 0), size)
archive = TextArchive(data, offset, size, is_compressed)
self.changed_archives[offset] = archive
if item.type == ItemType.Undernet:
if len(archive.progressive_undernet_indices) == 0:
archive.add_progression_scripts() # Generate the new scripts
# Replace the item text box as normal. We just also add a new jump at the end of the script
item_bytes = generate_item_message(item)
changed_script = archive.scripts[location.text_script_index]
# There isn't a "Jump unconditional", so we fake one. Check flag 0 and jump
# to the start of our progression regardless of outcome
jump_to_first_undernet_bytes = [0xF3, 0x00,
0x00, 0x00,
archive.progressive_undernet_indices[0],
archive.progressive_undernet_indices[0]]
# Insert the new message second-to-last (the last index should be an end all by itself)
changed_script.messageBoxes.insert(-1, jump_to_first_undernet_bytes)
# item_bytes = jump_to_first_undernet_bytes
elif item.type == ItemType.External:
item_bytes = generate_external_item_message(item.itemName, item.recipient)
else:
item_bytes = generate_item_message(item)
archive.inject_item_message(location.text_script_index, location.text_box_indices,
item_bytes)
def insert_hint_text(self, location, short_text, long_text = ""):
"""
Replaces the placeholder text in this location's archive with short_text,
gives another text box for long_text if it's present
"""
# Replace item name placeholders
if location.inject_name:
offset = location.text_archive_address
# If the archive is already loaded, use that
if offset in self.changed_archives:
archive = self.changed_archives[offset]
else:
# It should be theoretically impossible to call insert_hint_text before actually injecting the item.
raise AssertionError("Inserting a hint at a location that doesn't have an item!")
archive.inject_item_text(short_text, long_text)
def inject_name(self, player):
authname = player
authname = authname+('\x00' * (63 - len(player)))
self.rom_data[0x7FFFC0:0x7FFFFF] = bytes(authname, 'utf8')
def write_changed_rom(self):
for archive in self.changed_archives.values():
self.rom_data = archive.inject_into_rom(self.rom_data)
def write_to_file(self, out_path):
with open(out_path, "wb") as rom:
rom.write(self.rom_data)
class MMBN3DeltaPatch(APDeltaPatch):
hash = CHECKSUM_BLUE
game = "MegaMan Battle Network 3"
patch_file_ending = ".apbn3"
result_file_ending = ".gba"
@classmethod
def get_source_data(cls) -> bytes:
return get_base_rom_bytes()
def get_base_rom_path(file_name: str = "") -> str:
options = Utils.get_options()
if not file_name:
bn3_options = options.get("mmbn3_options", None)
if bn3_options is None:
file_name = "Mega Man Battle Network 3 - Blue Version (USA).gba"
else:
file_name = bn3_options["rom_file"]
if not os.path.exists(file_name):
file_name = Utils.local_path(file_name)
return file_name
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:
file_name = get_base_rom_path(file_name)
base_rom_bytes = bytes(open(file_name, "rb").read())
basemd5 = hashlib.md5()
basemd5.update(base_rom_bytes)
if CHECKSUM_BLUE != basemd5.hexdigest():
raise Exception('Supplied Base Rom does not match US GBA Blue Version.'
'Please provide the correct ROM version')
get_base_rom_bytes.base_rom_bytes = base_rom_bytes
return base_rom_bytes
def get_patched_rom_bytes(file_name: str = "") -> bytes:
"""
Gets the patched ROM data generated from applying the ap-patch diff file to the provided ROM.
Diff patch generated by https://github.com/digiholic/bn3-ap-patch
Which should contain all changed text banks and assembly code
"""
import pkgutil
base_rom_bytes = get_base_rom_bytes(file_name)
patch_bytes = pkgutil.get_data(__name__, "data/bn3-ap-patch.bsdiff")
patched_rom_bytes = bsdiff4.patch(base_rom_bytes, patch_bytes)
return patched_rom_bytes

483
worlds/mmbn3/__init__.py Normal file
View File

@ -0,0 +1,483 @@
import os
import typing
import threading
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification, Region, Entrance, \
LocationProgressType
from worlds.AutoWorld import WebWorld, World
from .Rom import MMBN3DeltaPatch, LocalRom, get_base_rom_path
from .Items import MMBN3Item, ItemData, item_table, all_items, item_frequencies, items_by_id, ItemType
from .Locations import Location, MMBN3Location, all_locations, location_table, location_data_table, \
always_excluded_locations, jobs
from .Options import MMBN3Options
from .Regions import regions, RegionName
from .Names.ItemName import ItemName
from .Names.LocationName import LocationName
class MMBN3Web(WebWorld):
theme = "ice"
setup_en = Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the MegaMan Battle Network 3 Randomizer connected to an Archipelago Multiworld.",
"English",
"setup_en.md",
"setup/en",
["digiholic"]
)
tutorials = [setup_en]
class MMBN3World(World):
"""
Play as Lan and MegaMan to stop the evil organization WWW led by the nefarious
Dr. Wily in their plans to take over the Net! Collect BattleChips, Customize your Navi,
and utilize powerful Style Changes to grow strong enough to take on the greatest
threat the Internet has ever faced!
"""
game = "MegaMan Battle Network 3"
option_definitions = MMBN3Options
topology_present = False
data_version = 1
item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = {loc_data.name: loc_data.id for loc_data in all_locations}
excluded_locations: typing.List[str]
item_frequencies: typing.Dict[str, int]
web = MMBN3Web()
def generate_early(self) -> None:
"""
called per player before any items or locations are created. You can set properties on your world here.
Already has access to player options and RNG.
"""
self.item_frequencies = item_frequencies.copy()
if self.multiworld.extra_ranks[self.player] > 0:
self.item_frequencies[ItemName.Progressive_Undernet_Rank] = 8 + self.multiworld.extra_ranks[self.player]
if not self.multiworld.include_jobs[self.player]:
self.excluded_locations = always_excluded_locations + [job.name for job in jobs]
else:
self.excluded_locations = always_excluded_locations
def create_regions(self) -> None:
"""
called to place player's regions into the MultiWorld's regions list. If it's hard to separate, this can be done
during generate_early or basic as well.
"""
name_to_region = {}
for region_info in regions:
region = Region(region_info.name, self.player, self.multiworld)
name_to_region[region_info.name] = region
for location in region_info.locations:
loc = MMBN3Location(self.player, location, self.location_name_to_id.get(location, None), region)
if location in self.excluded_locations:
loc.progress_type = LocationProgressType.EXCLUDED
region.locations.append(loc)
self.multiworld.regions.append(region)
for region_info in regions:
region = name_to_region[region_info.name]
for connection in region_info.connections:
connection_region = name_to_region[connection]
entrance = Entrance(self.player, connection, region)
entrance.connect(connection_region)
# ACDC Pending with Start Randomizer
# if connection == RegionName.ACDC_Overworld:
# entrance.access_rule = lambda state: state.has(ItemName.Parasol, self.player)
if connection == RegionName.SciLab_Overworld:
entrance.access_rule = lambda state: state.has(ItemName.SubPET, self.player)
if connection == RegionName.Yoka_Overworld:
entrance.access_rule = lambda state: state.has(ItemName.Needle, self.player)
if connection == RegionName.Beach_Overworld:
entrance.access_rule = lambda state: state.has(ItemName.PETCase, self.player)
# ACDC Pending with Start Randomizer
# if connection == RegionName.ACDC_Cyberworld:
# entrance.access_rule = lambda state: state.has(ItemName.CACDCPas, self.player)
if connection == RegionName.SciLab_Cyberworld:
entrance.access_rule = lambda state: \
state.has(ItemName.CSciPas, self.player) or \
state.can_reach(RegionName.SciLab_Overworld, "Region", self.player)
if connection == RegionName.Yoka_Cyberworld:
entrance.access_rule = lambda state: \
state.has(ItemName.CYokaPas, self.player) or \
(
state.can_reach(RegionName.SciLab_Overworld, "Region", self.player) and
state.has(ItemName.Press, self.player)
)
if connection == RegionName.Beach_Cyberworld:
entrance.access_rule = lambda state: state.has(ItemName.CBeacPas, self.player) and\
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
if connection == RegionName.Undernet:
entrance.access_rule = lambda state: self.explore_score(state) > 8 and\
state.has(ItemName.Press, self.player)
if connection == RegionName.Secret_Area:
entrance.access_rule = lambda state: self.explore_score(state) > 12 and\
state.has(ItemName.Hammer, self.player)
if connection == RegionName.WWW_Island:
entrance.access_rule = lambda state:\
state.has(ItemName.Progressive_Undernet_Rank, self.player, 8)
region.exits.append(entrance)
def create_items(self) -> None:
# First add in all progression and useful items
required_items = []
for item in all_items:
if item.progression != ItemClassification.filler:
freq = self.item_frequencies.get(item.itemName, 1)
required_items += [item.itemName for _ in range(freq)]
for itemName in required_items:
self.multiworld.itempool.append(self.create_item(itemName))
# Then, get a random amount of fillers until we have as many items as we have locations
filler_items = []
for item in all_items:
if item.progression == ItemClassification.filler:
freq = self.item_frequencies.get(item.itemName, 1)
filler_items += [item.itemName for _ in range(freq)]
remaining = len(all_locations) - len(required_items)
for i in range(remaining):
filler_item_name = self.multiworld.random.choice(filler_items)
item = self.create_item(filler_item_name)
self.multiworld.itempool.append(item)
filler_items.remove(filler_item_name)
def set_rules(self) -> None:
"""
called to set access and item rules on locations and entrances.
"""
# Set WWW ID requirements
def has_www_id(state): return state.has(ItemName.WWW_ID, self.player)
self.multiworld.get_location(LocationName.ACDC_1_PMD, self.player).access_rule = has_www_id
self.multiworld.get_location(LocationName.SciLab_1_WWW_BMD, self.player).access_rule = has_www_id
self.multiworld.get_location(LocationName.Yoka_1_WWW_BMD, self.player).access_rule = has_www_id
self.multiworld.get_location(LocationName.Undernet_1_WWW_BMD, self.player).access_rule = has_www_id
# Set Press Program requirements
def has_press(state): return state.has(ItemName.Press, self.player)
self.multiworld.get_location(LocationName.Yoka_1_PMD, self.player).access_rule = has_press
self.multiworld.get_location(LocationName.Yoka_2_Upper_BMD, self.player).access_rule = has_press
self.multiworld.get_location(LocationName.Beach_2_East_BMD, self.player).access_rule = has_press
self.multiworld.get_location(LocationName.Hades_South_BMD, self.player).access_rule = has_press
self.multiworld.get_location(LocationName.Secret_3_BugFrag_BMD, self.player).access_rule = has_press
self.multiworld.get_location(LocationName.Secret_3_Island_BMD, self.player).access_rule = has_press
# Set Job additional area access
self.multiworld.get_location(LocationName.Please_deliver_this, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player)
self.multiworld.get_location(LocationName.My_Navi_is_sick, self.player).access_rule =\
lambda state: \
state.has(ItemName.Recov30_star, self.player)
self.multiworld.get_location(LocationName.Help_me_with_my_son, self.player).access_rule =\
lambda state:\
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player)
self.multiworld.get_location(LocationName.Transmission_error, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Chip_Prices, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) and \
state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player)
self.multiworld.get_location(LocationName.Im_broke, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player)
self.multiworld.get_location(LocationName.Rare_chips_for_cheap, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Be_my_boyfriend, self.player).access_rule =\
lambda state: \
state.can_reach(RegionName.Beach_Cyberworld, "Region", self.player)
self.multiworld.get_location(LocationName.Will_you_deliver, self.player).access_rule=\
lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player)
self.multiworld.get_location(LocationName.Somebody_please_help, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Looking_for_condor, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Help_with_rehab, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Old_Master, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Catching_gang_members, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player) and \
state.has(ItemName.Press, self.player)
self.multiworld.get_location(LocationName.Please_adopt_a_virus, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player)
self.multiworld.get_location(LocationName.Legendary_Tomes, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \
state.can_reach(RegionName.Undernet, "Region", self.player) and \
state.can_reach(RegionName.Deep_Undernet, "Region", self.player) and \
state.has_all({ItemName.Press, ItemName.Magnum1_A}, self.player)
self.multiworld.get_location(LocationName.Legendary_Tomes_Treasure, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \
state.can_reach(LocationName.Legendary_Tomes, "Location", self.player)
self.multiworld.get_location(LocationName.Hide_and_seek_First_Child, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Hide_and_seek_Second_Child, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Hide_and_seek_Third_Child, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Hide_and_seek_Fourth_Child, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Hide_and_seek_Completion, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Finding_the_blue_Navi, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Undernet, "Region", self.player)
self.multiworld.get_location(LocationName.Give_your_support, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player)
self.multiworld.get_location(LocationName.Stamp_collecting, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) and \
state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player) and \
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player) and \
state.can_reach(RegionName.Beach_Cyberworld, "Region", self.player)
self.multiworld.get_location(LocationName.Help_with_a_will, self.player).access_rule = \
lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) and \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player) and \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \
state.can_reach(RegionName.Undernet, "Region", self.player)
# Set Trade quests
self.multiworld.get_location(LocationName.ACDC_SonicWav_W_Trade, self.player).access_rule =\
lambda state: state.has(ItemName.SonicWav_W, self.player)
self.multiworld.get_location(LocationName.ACDC_Bubbler_C_Trade, self.player).access_rule =\
lambda state: state.has(ItemName.Bubbler_C, self.player)
self.multiworld.get_location(LocationName.ACDC_Recov120_S_Trade, self.player).access_rule =\
lambda state: state.has(ItemName.Recov120_S, self.player)
self.multiworld.get_location(LocationName.SciLab_Shake1_S_Trade, self.player).access_rule =\
lambda state: state.has(ItemName.Shake1_S, self.player)
self.multiworld.get_location(LocationName.Yoka_FireSwrd_P_Trade, self.player).access_rule =\
lambda state: state.has(ItemName.FireSwrd_P, self.player)
self.multiworld.get_location(LocationName.Hospital_DynaWav_V_Trade, self.player).access_rule =\
lambda state: state.has(ItemName.DynaWave_V, self.player)
self.multiworld.get_location(LocationName.Beach_DNN_WideSwrd_C_Trade, self.player).access_rule =\
lambda state: state.has(ItemName.WideSwrd_C, self.player)
self.multiworld.get_location(LocationName.Beach_DNN_HoleMetr_H_Trade, self.player).access_rule =\
lambda state: state.has(ItemName.HoleMetr_H, self.player)
self.multiworld.get_location(LocationName.Beach_DNN_Shadow_J_Trade, self.player).access_rule =\
lambda state: state.has(ItemName.Shadow_J, self.player)
self.multiworld.get_location(LocationName.Hades_GrabBack_K_Trade, self.player).access_rule =\
lambda state: state.has(ItemName.GrabBack_K, self.player)
# Set Number Traders
# The first 8 are considered cheap enough to grind for in ACDC. Protip: Try grinding in the tank
self.multiworld.get_location(LocationName.Numberman_Code_09, self.player).access_rule = \
lambda state: self.explore_score(state) > 2
self.multiworld.get_location(LocationName.Numberman_Code_10, self.player).access_rule = \
lambda state: self.explore_score(state) > 2
self.multiworld.get_location(LocationName.Numberman_Code_11, self.player).access_rule = \
lambda state: self.explore_score(state) > 2
self.multiworld.get_location(LocationName.Numberman_Code_12, self.player).access_rule = \
lambda state: self.explore_score(state) > 2
self.multiworld.get_location(LocationName.Numberman_Code_13, self.player).access_rule = \
lambda state: self.explore_score(state) > 2
self.multiworld.get_location(LocationName.Numberman_Code_14, self.player).access_rule = \
lambda state: self.explore_score(state) > 2
self.multiworld.get_location(LocationName.Numberman_Code_15, self.player).access_rule = \
lambda state: self.explore_score(state) > 2
self.multiworld.get_location(LocationName.Numberman_Code_16, self.player).access_rule = \
lambda state: self.explore_score(state) > 2
self.multiworld.get_location(LocationName.Numberman_Code_17, self.player).access_rule =\
lambda state: self.explore_score(state) > 4
self.multiworld.get_location(LocationName.Numberman_Code_18, self.player).access_rule =\
lambda state: self.explore_score(state) > 4
self.multiworld.get_location(LocationName.Numberman_Code_19, self.player).access_rule =\
lambda state: self.explore_score(state) > 4
self.multiworld.get_location(LocationName.Numberman_Code_20, self.player).access_rule =\
lambda state: self.explore_score(state) > 4
self.multiworld.get_location(LocationName.Numberman_Code_21, self.player).access_rule =\
lambda state: self.explore_score(state) > 4
self.multiworld.get_location(LocationName.Numberman_Code_22, self.player).access_rule =\
lambda state: self.explore_score(state) > 4
self.multiworld.get_location(LocationName.Numberman_Code_23, self.player).access_rule =\
lambda state: self.explore_score(state) > 4
self.multiworld.get_location(LocationName.Numberman_Code_24, self.player).access_rule =\
lambda state: self.explore_score(state) > 4
self.multiworld.get_location(LocationName.Numberman_Code_25, self.player).access_rule =\
lambda state: self.explore_score(state) > 8
self.multiworld.get_location(LocationName.Numberman_Code_26, self.player).access_rule =\
lambda state: self.explore_score(state) > 8
self.multiworld.get_location(LocationName.Numberman_Code_27, self.player).access_rule =\
lambda state: self.explore_score(state) > 8
self.multiworld.get_location(LocationName.Numberman_Code_28, self.player).access_rule =\
lambda state: self.explore_score(state) > 8
self.multiworld.get_location(LocationName.Numberman_Code_29, self.player).access_rule =\
lambda state: self.explore_score(state) > 10
self.multiworld.get_location(LocationName.Numberman_Code_30, self.player).access_rule =\
lambda state: self.explore_score(state) > 10
self.multiworld.get_location(LocationName.Numberman_Code_31, self.player).access_rule =\
lambda state: self.explore_score(state) > 10
def not_undernet(item): return item.code != item_table[ItemName.Progressive_Undernet_Rank].code or item.player != self.player
self.multiworld.get_location(LocationName.WWW_1_Central_BMD, self.player).item_rule = not_undernet
self.multiworld.get_location(LocationName.WWW_1_East_BMD, self.player).item_rule = not_undernet
self.multiworld.get_location(LocationName.WWW_2_East_BMD, self.player).item_rule = not_undernet
self.multiworld.get_location(LocationName.WWW_2_Northwest_BMD, self.player).item_rule = not_undernet
self.multiworld.get_location(LocationName.WWW_3_East_BMD, self.player).item_rule = not_undernet
self.multiworld.get_location(LocationName.WWW_3_North_BMD, self.player).item_rule = not_undernet
self.multiworld.get_location(LocationName.WWW_4_Northwest_BMD, self.player).item_rule = not_undernet
self.multiworld.get_location(LocationName.WWW_4_Central_BMD, self.player).item_rule = not_undernet
self.multiworld.get_location(LocationName.WWW_Wall_BMD, self.player).item_rule = not_undernet
self.multiworld.get_location(LocationName.WWW_Control_Room_1_Screen, self.player).item_rule = not_undernet
self.multiworld.get_location(LocationName.WWW_Wilys_Desk, self.player).item_rule = not_undernet
# place "Victory" at "Final Boss" and set collection as win condition
self.multiworld.get_location(LocationName.Alpha_Defeated, self.player) \
.place_locked_item(self.create_event(ItemName.Victory))
self.multiworld.completion_condition[self.player] = \
lambda state: state.has(ItemName.Victory, self.player)
def generate_output(self, output_directory: str) -> None:
rompath: str = ""
try:
world = self.multiworld
player = self.player
rom = LocalRom(get_base_rom_path())
for location_name in location_table.keys():
location = world.get_location(location_name, player)
ap_item = location.item
item_id = ap_item.code
if item_id is not None:
if ap_item.player != player or item_id not in items_by_id:
item = ItemData(item_id, ap_item.name, ap_item.classification, ItemType.External)
item = item._replace(recipient=self.multiworld.player_name[ap_item.player])
else:
item = items_by_id[item_id]
location_data = location_data_table[location_name]
# print("Placing item "+item.itemName+" at location "+location_data.name)
rom.replace_item(location_data, item)
if location_data.inject_name:
item_name_text = "Item"
long_item_text = ""
# No item hinting
if self.multiworld.trade_quest_hinting[self.player] == 0:
item_name_text = "Check"
# Partial item hinting
elif self.multiworld.trade_quest_hinting[self.player] == 1:
if item.progression == ItemClassification.progression \
or item.progression == ItemClassification.progression_skip_balancing:
item_name_text = "Progress"
elif item.progression == ItemClassification.useful \
or item.progression == ItemClassification.trap:
item_name_text = "Item"
else:
item_name_text = "Garbage"
if item.recipient == 'Myself':
item_name_text = "Your " + item_name_text
else:
item_name_text = item.recipient + "'s " + item_name_text
# Full item hinting
else:
owners_name = "Your" if item.recipient == 'Myself' else item.recipient + "'s"
long_item_text = f"It's {owners_name} \n\"{item.itemName}\"!!"
rom.insert_hint_text(location_data, item_name_text, long_item_text)
rom.inject_name(world.player_name[player])
rompath = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.gba")
rom.write_changed_rom()
rom.write_to_file(rompath)
patch = MMBN3DeltaPatch(os.path.splitext(rompath)[0]+MMBN3DeltaPatch.patch_file_ending, player=player,
player_name=world.player_name[player], patched_path=rompath)
patch.write()
except:
raise
finally:
if os.path.exists(rompath):
os.unlink(rompath)
@classmethod
def stage_assert_generate(cls, multiworld: "MultiWorld") -> None:
rom_file = get_base_rom_path()
if not os.path.exists(rom_file):
raise FileNotFoundError(rom_file)
def create_item(self, name: str) -> "Item":
item = item_table[name]
return MMBN3Item(item.itemName, item.progression, item.code, self.player)
def create_event(self, event: str):
# while we are at it, we can also add a helper to create events
return MMBN3Item(event, ItemClassification.progression, None, self.player)
def fill_slot_data(self):
return {name: getattr(self.multiworld, name)[self.player].value for name in self.option_definitions}
def explore_score(self, state):
"""
Determine roughly how much of the game you can explore to make certain checks not restrict much movement
"""
score = 0
if state.can_reach(RegionName.WWW_Island, "Region", self.player):
return 999
if state.can_reach(RegionName.SciLab_Overworld, "Region", self.player):
score += 3
if state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player):
score += 1
if state.can_reach(RegionName.Yoka_Overworld, "Region", self.player):
score += 2
if state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player):
score += 1
if state.can_reach(RegionName.Beach_Overworld, "Region", self.player):
score += 3
if state.can_reach(RegionName.Beach_Cyberworld, "Region", self.player):
score += 1
if state.can_reach(RegionName.Undernet, "Region", self.player):
score += 2
if state.can_reach(RegionName.Deep_Undernet, "Region", self.player):
score += 1
if state.can_reach(RegionName.Secret_Area, "Region", self.player):
score += 1
return score

Binary file not shown.

View File

@ -0,0 +1,74 @@
# MegaMan Battle Network 3
## Where is the settings page?
The [player settings page for this game](../player-settings) contains all the options you need to configure and
export a config file.
## What does randomization do to this game?
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.
The game begins in "Open Mode", in a story state just before leaving for the WWW Base. All story progress leading up to
that point has been auto completed. You will be given a Style Change upon starting, but will
have default MegaMan equipped, you can switch to your new style if you want through the Navi menu.
Higsby's Chip Trader has been replaced with a shop interface where you can pay 500z to access the next item in the
trader sequence.
Dialog has been rewritten for both the Trader and the hat kid next to the trader to explain it.
The Nut-Wafer Chocolate Stand in the Yoka Metro has been replaced with a system for trading BugFrags for items.
Dialog has been rewritten to give it a story reason for accepting BugFrags.
The Secret Area is unlocked from the start, since the Library is filled automatically. Jacking Out has been enabled
in the Secret Area.
## What is the goal of MegaMan Battle Network 3 when randomized?
Defeat Alpha on the WWW Base. You will not be able to access the Island until you have acquired the item `GigFreez`,
which will be the eight item in the `progressive-undernet` item sequence. You will need to acquire Undernet Ranks
10, 9, 8, 7, 3, 2, and 1 before acquiring `GigFreez`
(Note: The skipping of 6-4 is intentional. They do not exist in the base game.)
## What items and locations get shuffled?
Locations in which items can be found:
- All Blue and Purple Mystery Data.
- The rewards from all available Jobs (Note: The four "Tora" jobs are story progress, and therefore have
been already completed).
- All overworld item pick ups, including Trades and Quizzes with NPCs
- 31 Items from the Numberman Lottery Trader (which have been changed to require Zenny instead of lotto numbers)
- 32 Items from the Nut-Wafer Chocolate stand in Yoka Metro Station (which have been changed to require BugFrags
instead of Zenny)
Items that are shuffled:
- All of the original rewards from above (Note: Certain common chips and low amounts of Zenny have been classified
as "filler" and might be replaced with progression items)
- All four Cybermetro passes, normally obtained through story progression
- Eight Progressive Undernet Ranks, normally obtained through story progression
- Several chips required for specific Jobs or Trades that would normally be unobtainable without RNG
- The NaviCust "Press" program, normally obtained through story progression
- Two ExpMems and ModTools for the Navi Customizer, normally obtained through story progression
- Higsby's `OrderSys`, which will enable access to the Chip Order System
## What items are _not_ randomized?
Certain Key Items are kept in their original locations:
- All four of the ID Keys in the WWW-Comp machines on the WWW Base
- Items in Undernet 7 locked behind post-game progression gates, such as beating Serenade or gathering all chips
## Which items can be in another player's world?
Any shuffled item can be in other players' worlds.
## What does another world's item look like in Mega Man Battle Network 3?
Item pickups all retain their original appearance. Text Boxes for accessing an item or given in dialog will mention
what item and what player is receiving the item
## When the player receives an item, what happens?
Whenever you have an item pending, the next time you are not in a battle, menu, or dialog box, you will receive a
message on screen notifying you of the item and sender, and the item will be added directly to your inventory.

View File

@ -0,0 +1,79 @@
# Setup Guide for MegaMan Battle Network 3 Archipelago
## Important
As we are using Bizhawk, this guide is only applicable to Windows and Linux systems.
## Required Software
- Bizhawk: [Bizhawk Releases from TASVideos](https://tasvideos.org/BizHawk/ReleaseHistory)
- Version 2.7.0 and later are supported.
- Detailed installation instructions for Bizhawk can be found at the above link.
- Windows users must run the prereq installer first, which can also be found at the above link.
- The built-in Archipelago client, which can be installed [here](https://github.com/ArchipelagoMW/Archipelago/releases)
(select `MegaMan Battle Network 3 Client` during installation).
- A US MegaMan Battle Network 3 Blue Rom
## Configuring Bizhawk
Once Bizhawk has been installed, open Bizhawk and change the following settings:
- Go to Config > Customize. Switch to the Advanced tab, then switch the Lua Core from "NLua+KopiLua" to
"Lua+LuaInterface". This is required for the Lua script to function correctly.
**NOTE: Even if "Lua+LuaInterface" is already selected, toggle between the two options and reselect it. Fresh installs**
**of newer versions of Bizhawk have a tendency to show "Lua+LuaInterface" as the default selected option but still load**
**"NLua+KopiLua" until this step is done.**
- Under Config > Customize > Advanced, make sure the box for AutoSaveRAM is checked, and click the 5s button.
This reduces the possibility of losing save data in emulator crashes.
- Under Config > Customize, check the "Run in background" and "Accept background input" boxes. This will allow you to
continue playing in the background, even if another window is selected, such as the Client.
- Under Config > Hotkeys, many hotkeys are listed, with many bound to common keys on the keyboard. You will likely want
to disable most of these, which you can do quickly using `Esc`.
It is strongly recommended to associate GBA rom extensions (\*.gba) to the Bizhawk we've just installed.
To do so, we simply have to search any GBA rom we happened to own, right click and select "Open with...", unfold
the list that appears and select the bottom option "Look for another application", then browse to the Bizhawk folder
and select EmuHawk.exe.
## Configuring your YAML file
### What is a YAML file and why do I need one?
Your YAML file contains a set of configuration options which provide the generator with information about how it should
generate your game. Each player of a multiworld will provide their own YAML file. This setup allows each player to enjoy
an experience customized for their taste, and different players in the same multiworld can all have different options.
### Where do I get a YAML file?
You can customize your settings by visiting the
[MegaMan Battle Network 3 Player Settings Page](/games/MegaMan%20Battle%20Network%203/player-settings)
## Joining a MultiWorld Game
### Obtain your GBA patch file
When you join a multiworld game, you will be asked to provide your YAML file to whoever is hosting. Once that is done,
the host will provide you with either a link to download your data file, or with a zip file containing everyone's data
files. Your data file should have a `.apbn3` extension.
Double-click on your `.apbn3` file to start your client and start the ROM patch process. Once the process is finished
(this can take a while), the client and the emulator will be started automatically (if you associated the extension
to the emulator as recommended).
### Connect to the Multiserver
Once both the client and the emulator are started, you must connect them. Within the emulator click on the "Tools"
menu and select "Lua Console". Click the folder button or press Ctrl+O to open a Lua script.
Navigate to your Archipelago install folder and open `data/lua/connector_mmbn3.lua`.
To connect the client to the multiserver simply put `<address>:<port>` on the textfield on top and press enter (if the
server uses password, type in the bottom textfield `/connect <address>:<port> [password]`)
Don't forget to start manipulating RNG early by shouting during generation:
```
JACK IN!
[Your name]!
EXECUTE!
```

257
worlds/mmbn3/lz10.py Normal file
View File

@ -0,0 +1,257 @@
from collections import defaultdict
from operator import itemgetter
from struct import pack, unpack
"""
Tweaked version of nlzss modified to work with raw data and return bytes instead of operating on whole files.
LZ11 functionality has been removed since it is not necessary for MMBN3
https://github.com/magical/nlzss
"""
def gba_decompress(data: bytearray):
"""Decompress LZSS-compressed bytes. Returns a bytearray."""
header = data[:4]
if header[0] == 0x10:
decompress_raw = decompress_raw_lzss10
else:
raise DecompressionError("not as lzss-compressed file")
decompressed_size, = unpack("<L", header[1:] + b'\x00')
data = data[4:]
return decompress_raw(data, decompressed_size)
def gba_compress(data: bytearray):
byteOut = bytearray()
# header
byteOut.extend(pack("<L", (len(data) << 8) + 0x10))
# body
length = 0
for tokens in chunkit(_compress(data), 8):
flags = [type(t) == tuple for t in tokens]
byteOut.extend(pack(">B", packflags(flags)))
for t in tokens:
if type(t) == tuple:
count, disp = t
count -= 3
disp = (-disp) - 1
assert 0 <= disp < 4096
sh = (count << 12) | disp
byteOut.extend(pack(">H", sh))
else:
byteOut.extend(pack(">B", t))
length += 1
length += sum(2 if f else 1 for f in flags)
# padding
padding = 4 - (length % 4 or 4)
if padding:
byteOut.extend(b'\xff' * padding)
return byteOut
class SlidingWindow:
# The size of the sliding window
size = 4096
# The minimum displacement.
disp_min = 2
# The hard minimum — a disp less than this can't be represented in the
# compressed stream.
disp_start = 1
# The minimum length for a successful match in the window
match_min = 3
# The maximum length of a successful match, inclusive.
match_max = 3 + 0xf
def __init__(self, buf):
self.data = buf
self.hash = defaultdict(list)
self.full = False
self.start = 0
self.stop = 0
#self.index = self.disp_min - 1
self.index = 0
assert self.match_max is not None
def next(self):
if self.index < self.disp_start - 1:
self.index += 1
return
if self.full:
olditem = self.data[self.start]
assert self.hash[olditem][0] == self.start
self.hash[olditem].pop(0)
item = self.data[self.stop]
self.hash[item].append(self.stop)
self.stop += 1
self.index += 1
if self.full:
self.start += 1
else:
if self.size <= self.stop:
self.full = True
def advance(self, n=1):
"""Advance the window by n bytes"""
for _ in range(n):
self.next()
def search(self):
match_max = self.match_max
match_min = self.match_min
counts = []
indices = self.hash[self.data[self.index]]
for i in indices:
matchlen = self.match(i, self.index)
if matchlen >= match_min:
disp = self.index - i
if self.disp_min <= disp:
counts.append((matchlen, -disp))
if matchlen >= match_max:
return counts[-1]
if counts:
match = max(counts, key=itemgetter(0))
return match
return None
def match(self, start, bufstart):
size = self.index - start
if size == 0:
return 0
matchlen = 0
it = range(min(len(self.data) - bufstart, self.match_max))
for i in it:
if self.data[start + (i % size)] == self.data[bufstart + i]:
matchlen += 1
else:
break
return matchlen
def _compress(input, windowclass=SlidingWindow):
"""Generates a stream of tokens. Either a byte (int) or a tuple of (count,
displacement)."""
window = windowclass(input)
i = 0
while True:
if len(input) <= i:
break
match = window.search()
if match:
yield match
window.advance(match[0])
i += match[0]
else:
yield input[i]
window.next()
i += 1
def packflags(flags):
n = 0
for i in range(8):
n <<= 1
try:
if flags[i]:
n |= 1
except IndexError:
pass
return n
def chunkit(it, n):
buf = []
for x in it:
buf.append(x)
if n <= len(buf):
yield buf
buf = []
if buf:
yield buf
def bits(byte):
return ((byte >> 7) & 1,
(byte >> 6) & 1,
(byte >> 5) & 1,
(byte >> 4) & 1,
(byte >> 3) & 1,
(byte >> 2) & 1,
(byte >> 1) & 1,
byte & 1)
def decompress_raw_lzss10(indata, decompressed_size, _overlay=False):
"""Decompress LZSS-compressed bytes. Returns a bytearray."""
data = bytearray()
it = iter(indata)
if _overlay:
disp_extra = 3
else:
disp_extra = 1
def writebyte(b):
data.append(b)
def readbyte():
return next(it)
def readshort():
# big-endian
a = next(it)
b = next(it)
return (a << 8) | b
def copybyte():
data.append(next(it))
while len(data) < decompressed_size:
b = readbyte()
flags = bits(b)
for flag in flags:
if flag == 0:
copybyte()
elif flag == 1:
sh = readshort()
count = (sh >> 0xc) + 3
disp = (sh & 0xfff) + disp_extra
for _ in range(count):
writebyte(data[-disp])
else:
raise ValueError(flag)
if decompressed_size <= len(data):
break
if len(data) != decompressed_size:
raise DecompressionError("decompressed size does not match the expected size")
return data
class DecompressionError(ValueError):
pass