500 lines
28 KiB
Python
500 lines
28 KiB
Python
import os
|
|
import settings
|
|
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
|
|
from worlds.generic.Rules import add_item_rule
|
|
|
|
|
|
class MMBN3Settings(settings.Group):
|
|
class RomFile(settings.UserFilePath):
|
|
"""File name of the MMBN3 Blue US rom"""
|
|
copy_to = "Mega Man Battle Network 3 - Blue Version (USA).gba"
|
|
description = "MMBN3 ROM File"
|
|
md5s = [MMBN3DeltaPatch.hash]
|
|
|
|
rom_file: RomFile = RomFile(RomFile.copy_to)
|
|
rom_start: bool = True
|
|
|
|
|
|
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"
|
|
options_dataclass = MMBN3Options
|
|
options: MMBN3Options
|
|
settings: typing.ClassVar[MMBN3Settings]
|
|
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.options.extra_ranks > 0:
|
|
self.item_frequencies[ItemName.Progressive_Undernet_Rank] = 8 + self.options.extra_ranks
|
|
|
|
if not self.options.include_jobs:
|
|
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
|
|
# Do not place any progression items on WWW Island
|
|
if region_info.name == RegionName.WWW_Island:
|
|
add_item_rule(loc, lambda item: not item.advancement)
|
|
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:
|
|
entrance = region.connect(name_to_region[connection])
|
|
|
|
# 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)
|
|
|
|
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.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.options.trade_quest_hinting == 0:
|
|
item_name_text = "Check"
|
|
# Partial item hinting
|
|
elif self.options.trade_quest_hinting == 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 self.options.as_dict("extra_ranks", "include_jobs", "trade_quest_hinting")
|
|
|
|
|
|
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
|