MMBN3: Logic and Bug Fixes, New Checks (#3646)

* PMDs now check to make sure you have enough unlockers for all of them before any are in logic, to avoid softlocks

* Adds Humor and BlckMnd to the pool and sets logic for Villain and Comedian. Patch not yet updated to remove starting inventory

* Adds Serenade as a check

* Fixes hide and seek completion to use proper Yoka Zoo map. Updates bsdiff patch to 1.2

* Adds option for excluding Secret Area, and item/location groups for further customization

* Update worlds/mmbn3/Locations.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

* Update worlds/mmbn3/Regions.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

* Update worlds/mmbn3/__init__.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

* Update worlds/mmbn3/__init__.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

* Update worlds/mmbn3/__init__.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

* Replaces can_reach generic with can_reach_region or can_reach_location, where applciable

* Unlocker is now a progression item, Excluded Locations is now a Set

* Missed a merge marker

* Excluded locations is no longer a set since you can't append to a set with +=

* Excluded locations is now a set again since you apparent can append to a set with |=

* Replaces more lists with sets. Fixes wording in option descriptions

* Update worlds/mmbn3/__init__.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
This commit is contained in:
digiholic 2025-01-17 06:41:12 -07:00 committed by GitHub
parent 3d5c277c31
commit d218dec826
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 165 additions and 99 deletions

View File

@ -85,7 +85,7 @@ keyItemList: typing.List[ItemData] = [
] ]
subChipList: typing.List[ItemData] = [ subChipList: typing.List[ItemData] = [
ItemData(0xB31018, ItemName.Unlocker, ItemClassification.useful, ItemType.SubChip, 117), ItemData(0xB31018, ItemName.Unlocker, ItemClassification.progression, ItemType.SubChip, 117),
ItemData(0xB31019, ItemName.Untrap, ItemClassification.filler, ItemType.SubChip, 115), ItemData(0xB31019, ItemName.Untrap, ItemClassification.filler, ItemType.SubChip, 115),
ItemData(0xB3101A, ItemName.LockEnmy, ItemClassification.filler, ItemType.SubChip, 116), ItemData(0xB3101A, ItemName.LockEnmy, ItemClassification.filler, ItemType.SubChip, 116),
ItemData(0xB3101B, ItemName.MiniEnrg, ItemClassification.filler, ItemType.SubChip, 112), ItemData(0xB3101B, ItemName.MiniEnrg, ItemClassification.filler, ItemType.SubChip, 112),
@ -290,7 +290,9 @@ programList: typing.List[ItemData] = [
ItemData(0xB31099, ItemName.WpnLV_plus_Yellow, ItemClassification.filler, ItemType.Program, 35, ProgramColor.Yellow), 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(0xB3109A, ItemName.Press, ItemClassification.progression, ItemType.Program, 20, ProgramColor.White),
ItemData(0xB310B7, ItemName.UnderSht, ItemClassification.useful, ItemType.Program, 30, ProgramColor.White) ItemData(0xB310B7, ItemName.UnderSht, ItemClassification.useful, ItemType.Program, 30, ProgramColor.White),
ItemData(0xB310E0, ItemName.Humor, ItemClassification.progression, ItemType.Program, 45, ProgramColor.Pink),
ItemData(0xB310E1, ItemName.BlckMnd, ItemClassification.progression, ItemType.Program, 46, ProgramColor.White)
] ]
zennyList: typing.List[ItemData] = [ zennyList: typing.List[ItemData] = [
@ -338,8 +340,29 @@ item_frequencies: typing.Dict[str, int] = {
ItemName.zenny_800z: 2, ItemName.zenny_800z: 2,
ItemName.zenny_1000z: 2, ItemName.zenny_1000z: 2,
ItemName.zenny_1200z: 2, ItemName.zenny_1200z: 2,
ItemName.bugfrag_01: 5, ItemName.bugfrag_01: 10,
ItemName.bugfrag_10: 5
} }
item_groups: typing.Dict[str, typing.Set[str]] = {
"Key Items": {loc.itemName for loc in keyItemList},
"Subchips": {loc.itemName for loc in subChipList},
"Programs": {loc.itemName for loc in programList},
"BattleChips": {loc.itemName for loc in chipList},
"Zenny": {loc.itemName for loc in zennyList},
"BugFrags": {loc.itemName for loc in bugFragList},
"Navi Chips": {
ItemName.Roll_R, ItemName.RollV2_R, ItemName.RollV3_R, ItemName.GutsMan_G, ItemName.GutsManV2_G,
ItemName.GutsManV3_G, ItemName.ProtoMan_B, ItemName.ProtoManV2_B, ItemName.ProtoManV3_B, ItemName.FlashMan_F,
ItemName.FlashManV2_F, ItemName.FlashManV3_F, ItemName.BeastMan_B, ItemName.BeastManV2_B, ItemName.BeastManV3_B,
ItemName.BubblMan_B, ItemName.BubblManV2_B, ItemName.BubblManV3_B, ItemName.DesertMan_D, ItemName.DesertManV2_D,
ItemName.DesertManV3_D, ItemName.PlantMan_P, ItemName.PlantManV2_P, ItemName.PlantManV3_P, ItemName.FlamMan_F,
ItemName.FlamManV2_F, ItemName.FlamManV3_F, ItemName.DrillMan_D, ItemName.DrillManV2_D, ItemName.DrillManV3_D,
ItemName.MetalMan_M, ItemName.MetalManV2_M, ItemName.MetalManV3_M, ItemName.KingMan_K, ItemName.KingManV2_K,
ItemName.KingManV3_K, ItemName.BowlMan_B, ItemName.BowlManV2_B, ItemName.BowlManV3_B
}
}
all_items: typing.List[ItemData] = keyItemList + subChipList + chipList + programList + zennyList + bugFragList 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} 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} items_by_id: typing.Dict[int, ItemData] = {item.code: item for item in all_items}

View File

@ -221,7 +221,8 @@ overworlds = [
LocationData(LocationName.Hades_Boat_Dock, 0xb310ab, 0x200024c, 0x10, 0x7519B0, 223, [3]), 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_Control_Room_1_Screen, 0xb310ac, 0x200024d, 0x40, 0x7596C4, 222, [3, 4]),
LocationData(LocationName.WWW_Wilys_Desk, 0xb310ad, 0x200024d, 0x2, 0x759384, 229, [3]), LocationData(LocationName.WWW_Wilys_Desk, 0xb310ad, 0x200024d, 0x2, 0x759384, 229, [3]),
LocationData(LocationName.Undernet_4_Pillar_Prog, 0xb310ae, 0x2000161, 0x1, 0x7746C8, 191, [0, 1]) LocationData(LocationName.Undernet_4_Pillar_Prog, 0xb310ae, 0x2000161, 0x1, 0x7746C8, 191, [0, 1]),
LocationData(LocationName.Serenade, 0xb3110f, 0x2000178, 0x40, 0x7B3C74, 1, [0])
] ]
jobs = [ jobs = [
@ -240,7 +241,8 @@ jobs = [
# LocationData(LocationName.Gathering_Data, 0xb310bb, 0x2000300, 0x10, 0x739580, 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.Somebody_please_help, 0xb310bc, 0x2000301, 0x4, 0x73A14C, 193, [0]),
LocationData(LocationName.Looking_for_condor, 0xb310bd, 0x2000301, 0x2, 0x749444, 203, [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.Help_with_rehab, 0xb310be, 0x2000301, 0x1, 0x762CF0, 192, [0]),
LocationData(LocationName.Help_with_rehab_bonus, 0xb3110e, 0x2000301, 0x1, 0x762CF0, 192, [3]),
LocationData(LocationName.Old_Master, 0xb310bf, 0x2000302, 0x80, 0x760E80, 193, [0]), LocationData(LocationName.Old_Master, 0xb310bf, 0x2000302, 0x80, 0x760E80, 193, [0]),
LocationData(LocationName.Catching_gang_members, 0xb310c0, 0x2000302, 0x40, 0x76EAE4, 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.Please_adopt_a_virus, 0xb310c1, 0x2000302, 0x20, 0x76A4F4, 193, [0]),
@ -250,7 +252,7 @@ jobs = [
LocationData(LocationName.Hide_and_seek_Second_Child, 0xb310c5, 0x2000188, 0x2, 0x75ADA8, 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_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_Fourth_Child, 0xb310c7, 0x2000189, 0x80, 0x75BEB0, 191, [0]),
LocationData(LocationName.Hide_and_seek_Completion, 0xb310c8, 0x2000302, 0x8, 0x7406A0, 193, [0]), LocationData(LocationName.Hide_and_seek_Completion, 0xb310c8, 0x2000302, 0x8, 0x742D40, 193, [0]),
LocationData(LocationName.Finding_the_blue_Navi, 0xb310c9, 0x2000302, 0x4, 0x773700, 192, [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.Give_your_support, 0xb310ca, 0x2000302, 0x2, 0x752D80, 192, [0]),
LocationData(LocationName.Stamp_collecting, 0xb310cb, 0x2000302, 0x1, 0x756074, 193, [0]), LocationData(LocationName.Stamp_collecting, 0xb310cb, 0x2000302, 0x1, 0x756074, 193, [0]),
@ -329,10 +331,7 @@ chocolate_shop = [
LocationData(LocationName.Chocolate_Shop_32, 0xb3110d, 0x20001c3, 0x01, 0x73F8FC, 181, [0]), LocationData(LocationName.Chocolate_Shop_32, 0xb3110d, 0x20001c3, 0x01, 0x73F8FC, 181, [0]),
] ]
always_excluded_locations = [ secret_locations = {
LocationName.Undernet_7_PMD,
LocationName.Undernet_7_Northeast_BMD,
LocationName.Undernet_7_Northwest_BMD,
LocationName.Secret_1_Northwest_BMD, LocationName.Secret_1_Northwest_BMD,
LocationName.Secret_1_Northeast_BMD, LocationName.Secret_1_Northeast_BMD,
LocationName.Secret_1_South_BMD, LocationName.Secret_1_South_BMD,
@ -341,19 +340,23 @@ always_excluded_locations = [
LocationName.Secret_2_Island_BMD, LocationName.Secret_2_Island_BMD,
LocationName.Secret_3_Island_BMD, LocationName.Secret_3_Island_BMD,
LocationName.Secret_3_BugFrag_BMD, LocationName.Secret_3_BugFrag_BMD,
LocationName.Secret_3_South_BMD LocationName.Secret_3_South_BMD,
] LocationName.Serenade
}
location_groups: typing.Dict[str, typing.Set[str]] = {
"BMDs": {loc.name for loc in bmds},
"PMDs": {loc.name for loc in pmds},
"Jobs": {loc.name for loc in jobs},
"Number Trader": {loc.name for loc in number_traders},
"Bugfrag Trader": {loc.name for loc in chocolate_shop},
"Secret Area": {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, LocationName.Serenade},
}
all_locations: typing.List[LocationData] = bmds + pmds + overworlds + jobs + number_traders + chocolate_shop 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] 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_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} 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

@ -173,6 +173,8 @@ class ItemName():
WpnLV_plus_White = "WpnLV+1 (White)" WpnLV_plus_White = "WpnLV+1 (White)"
Press = "Press" Press = "Press"
UnderSht = "UnderSht" UnderSht = "UnderSht"
Humor = "Humor"
BlckMnd = "BlckMnd"
## Currency ## Currency
zenny_200z = "200z" zenny_200z = "200z"

View File

@ -210,6 +210,7 @@ class LocationName():
WWW_Control_Room_1_Screen = "WWW Control Room 1 Screen" WWW_Control_Room_1_Screen = "WWW Control Room 1 Screen"
WWW_Wilys_Desk = "WWW Wily's Desk" WWW_Wilys_Desk = "WWW Wily's Desk"
Undernet_4_Pillar_Prog = "Undernet 4 Pillar Prog" Undernet_4_Pillar_Prog = "Undernet 4 Pillar Prog"
Serenade = "Serenade"
## Numberman Codes ## Numberman Codes
Numberman_Code_01 = "Numberman Code 01" Numberman_Code_01 = "Numberman Code 01"
@ -261,6 +262,7 @@ class LocationName():
Somebody_please_help = "Job: Somebody, please help!" Somebody_please_help = "Job: Somebody, please help!"
Looking_for_condor = "Job: Looking for condor" Looking_for_condor = "Job: Looking for condor"
Help_with_rehab = "Job: Help with rehab" Help_with_rehab = "Job: Help with rehab"
Help_with_rehab_bonus = "Job: Help with rehab bonus"
Old_Master = "Job: Old Master" Old_Master = "Job: Old Master"
Catching_gang_members = "Job: Catching gang members" Catching_gang_members = "Job: Catching gang members"
Please_adopt_a_virus = "Job: Please adopt a virus!" Please_adopt_a_virus = "Job: Please adopt a virus!"

View File

@ -1,5 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from Options import Choice, Range, DefaultOnToggle, PerGameCommonOptions from Options import Choice, Range, DefaultOnToggle, Toggle, PerGameCommonOptions
class ExtraRanks(Range): class ExtraRanks(Range):
@ -17,10 +17,17 @@ class ExtraRanks(Range):
class IncludeJobs(DefaultOnToggle): class IncludeJobs(DefaultOnToggle):
""" """
Whether Jobs can be included in logic. Whether Jobs can contain progression or useful items.
""" """
display_name = "Include Jobs" display_name = "Include Jobs"
class IncludeSecretArea(Toggle):
"""
Whether the Secret Area (including Serenade) can contain progression or useful items.
"""
display_name = "Include Secret Area"
# Possible logic options: # Possible logic options:
# - Include Number Trader # - Include Number Trader
# - Include Secret Area # - Include Secret Area
@ -46,5 +53,6 @@ class TradeQuestHinting(Choice):
class MMBN3Options(PerGameCommonOptions): class MMBN3Options(PerGameCommonOptions):
extra_ranks: ExtraRanks extra_ranks: ExtraRanks
include_jobs: IncludeJobs include_jobs: IncludeJobs
include_secret: IncludeSecretArea
trade_quest_hinting: TradeQuestHinting trade_quest_hinting: TradeQuestHinting

View File

@ -135,6 +135,7 @@ regions = [
LocationName.Somebody_please_help, LocationName.Somebody_please_help,
LocationName.Looking_for_condor, LocationName.Looking_for_condor,
LocationName.Help_with_rehab, LocationName.Help_with_rehab,
LocationName.Help_with_rehab_bonus,
LocationName.Old_Master, LocationName.Old_Master,
LocationName.Catching_gang_members, LocationName.Catching_gang_members,
LocationName.Please_adopt_a_virus, LocationName.Please_adopt_a_virus,
@ -349,6 +350,7 @@ regions = [
LocationName.Secret_2_Upper_BMD, LocationName.Secret_2_Upper_BMD,
LocationName.Secret_3_Island_BMD, LocationName.Secret_3_Island_BMD,
LocationName.Secret_3_South_BMD, LocationName.Secret_3_South_BMD,
LocationName.Secret_3_BugFrag_BMD LocationName.Secret_3_BugFrag_BMD,
LocationName.Serenade
]) ])
] ]

View File

@ -9,14 +9,14 @@ from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification, Region,
from worlds.AutoWorld import WebWorld, World from worlds.AutoWorld import WebWorld, World
from .Rom import MMBN3DeltaPatch, LocalRom, get_base_rom_path 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 .Items import MMBN3Item, ItemData, item_table, all_items, item_frequencies, items_by_id, ItemType, item_groups
from .Locations import Location, MMBN3Location, all_locations, location_table, location_data_table, \ from .Locations import Location, MMBN3Location, all_locations, location_table, location_data_table, \
always_excluded_locations, jobs secret_locations, jobs, location_groups
from .Options import MMBN3Options from .Options import MMBN3Options
from .Regions import regions, RegionName from .Regions import regions, RegionName
from .Names.ItemName import ItemName from .Names.ItemName import ItemName
from .Names.LocationName import LocationName from .Names.LocationName import LocationName
from worlds.generic.Rules import add_item_rule from worlds.generic.Rules import add_item_rule, add_rule
class MMBN3Settings(settings.Group): class MMBN3Settings(settings.Group):
@ -57,12 +57,16 @@ class MMBN3World(World):
settings: typing.ClassVar[MMBN3Settings] settings: typing.ClassVar[MMBN3Settings]
topology_present = False topology_present = False
item_name_to_id = {name: data.code for name, data in item_table.items()} 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} location_name_to_id = {loc_data.name: loc_data.id for loc_data in all_locations}
excluded_locations: typing.List[str] excluded_locations: typing.Set[str]
item_frequencies: typing.Dict[str, int] item_frequencies: typing.Dict[str, int]
location_name_groups = location_groups
item_name_groups = item_groups
web = MMBN3Web() web = MMBN3Web()
def generate_early(self) -> None: def generate_early(self) -> None:
@ -74,10 +78,11 @@ class MMBN3World(World):
if self.options.extra_ranks > 0: if self.options.extra_ranks > 0:
self.item_frequencies[ItemName.Progressive_Undernet_Rank] = 8 + self.options.extra_ranks self.item_frequencies[ItemName.Progressive_Undernet_Rank] = 8 + self.options.extra_ranks
self.excluded_locations = set()
if not self.options.include_secret:
self.excluded_locations |= secret_locations
if not self.options.include_jobs: if not self.options.include_jobs:
self.excluded_locations = always_excluded_locations + [job.name for job in jobs] self.excluded_locations |= {job.name for job in jobs}
else:
self.excluded_locations = always_excluded_locations
def create_regions(self) -> None: def create_regions(self) -> None:
""" """
@ -140,19 +145,19 @@ class MMBN3World(World):
if connection == RegionName.SciLab_Cyberworld: if connection == RegionName.SciLab_Cyberworld:
entrance.access_rule = lambda state: \ entrance.access_rule = lambda state: \
state.has(ItemName.CSciPas, self.player) or \ state.has(ItemName.CSciPas, self.player) or \
state.can_reach(RegionName.SciLab_Overworld, "Region", self.player) state.can_reach_region(RegionName.SciLab_Overworld, self.player)
self.multiworld.register_indirect_condition(self.get_region(RegionName.SciLab_Overworld), entrance) self.multiworld.register_indirect_condition(self.get_region(RegionName.SciLab_Overworld), entrance)
if connection == RegionName.Yoka_Cyberworld: if connection == RegionName.Yoka_Cyberworld:
entrance.access_rule = lambda state: \ entrance.access_rule = lambda state: \
state.has(ItemName.CYokaPas, self.player) or \ state.has(ItemName.CYokaPas, self.player) or \
( (
state.can_reach(RegionName.SciLab_Overworld, "Region", self.player) and state.can_reach_region(RegionName.SciLab_Overworld, self.player) and
state.has(ItemName.Press, self.player) state.has(ItemName.Press, self.player)
) )
self.multiworld.register_indirect_condition(self.get_region(RegionName.SciLab_Overworld), entrance) self.multiworld.register_indirect_condition(self.get_region(RegionName.SciLab_Overworld), entrance)
if connection == RegionName.Beach_Cyberworld: if connection == RegionName.Beach_Cyberworld:
entrance.access_rule = lambda state: state.has(ItemName.CBeacPas, self.player) and\ entrance.access_rule = lambda state: state.has(ItemName.CBeacPas, self.player) and\
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) state.can_reach_region(RegionName.Yoka_Overworld, self.player)
self.multiworld.register_indirect_condition(self.get_region(RegionName.Yoka_Overworld), entrance) self.multiworld.register_indirect_condition(self.get_region(RegionName.Yoka_Overworld), entrance)
if connection == RegionName.Undernet: if connection == RegionName.Undernet:
entrance.access_rule = lambda state: self.explore_score(state) > 8 and\ entrance.access_rule = lambda state: self.explore_score(state) > 8 and\
@ -198,122 +203,138 @@ class MMBN3World(World):
# Set WWW ID requirements # Set WWW ID requirements
def has_www_id(state): return state.has(ItemName.WWW_ID, self.player) 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 add_rule(self.multiworld.get_location(LocationName.ACDC_1_PMD, self.player), has_www_id)
self.multiworld.get_location(LocationName.SciLab_1_WWW_BMD, self.player).access_rule = has_www_id add_rule(self.multiworld.get_location(LocationName.SciLab_1_WWW_BMD, self.player), has_www_id)
self.multiworld.get_location(LocationName.Yoka_1_WWW_BMD, self.player).access_rule = has_www_id add_rule(self.multiworld.get_location(LocationName.Yoka_1_WWW_BMD, self.player), has_www_id)
self.multiworld.get_location(LocationName.Undernet_1_WWW_BMD, self.player).access_rule = has_www_id add_rule(self.multiworld.get_location(LocationName.Undernet_1_WWW_BMD, self.player), has_www_id)
# Set Press Program requirements # Set Press Program requirements
def has_press(state): return state.has(ItemName.Press, self.player) 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 add_rule(self.multiworld.get_location(LocationName.Yoka_1_PMD, self.player), has_press)
self.multiworld.get_location(LocationName.Yoka_2_Upper_BMD, self.player).access_rule = has_press add_rule(self.multiworld.get_location(LocationName.Yoka_2_Upper_BMD, self.player), has_press)
self.multiworld.get_location(LocationName.Beach_2_East_BMD, self.player).access_rule = has_press add_rule(self.multiworld.get_location(LocationName.Beach_2_East_BMD, self.player), has_press)
self.multiworld.get_location(LocationName.Hades_South_BMD, self.player).access_rule = has_press add_rule(self.multiworld.get_location(LocationName.Hades_South_BMD, self.player), has_press)
self.multiworld.get_location(LocationName.Secret_3_BugFrag_BMD, self.player).access_rule = has_press add_rule(self.multiworld.get_location(LocationName.Secret_3_BugFrag_BMD, self.player), has_press)
self.multiworld.get_location(LocationName.Secret_3_Island_BMD, self.player).access_rule = has_press add_rule(self.multiworld.get_location(LocationName.Secret_3_Island_BMD, self.player), has_press)
# Set Purple Mystery Data Unlocker access
def can_unlock(state): return state.can_reach_region(RegionName.SciLab_Overworld, self.player) or \
state.can_reach_region(RegionName.SciLab_Cyberworld, self.player) or \
state.can_reach_region(RegionName.Yoka_Cyberworld, self.player) or \
state.has(ItemName.Unlocker, self.player, 8) # There are 8 PMDs that aren't in one of the above areas
add_rule(self.multiworld.get_location(LocationName.ACDC_1_PMD, self.player), can_unlock)
add_rule(self.multiworld.get_location(LocationName.Yoka_1_PMD, self.player), can_unlock)
add_rule(self.multiworld.get_location(LocationName.Beach_1_PMD, self.player), can_unlock)
add_rule(self.multiworld.get_location(LocationName.Undernet_7_PMD, self.player), can_unlock)
add_rule(self.multiworld.get_location(LocationName.Mayls_HP_PMD, self.player), can_unlock)
add_rule(self.multiworld.get_location(LocationName.SciLab_Dads_Computer_PMD, self.player), can_unlock)
add_rule(self.multiworld.get_location(LocationName.Zoo_Panda_PMD, self.player), can_unlock)
add_rule(self.multiworld.get_location(LocationName.Beach_DNN_Security_Panel_PMD, self.player), can_unlock)
add_rule(self.multiworld.get_location(LocationName.Beach_DNN_Main_Console_PMD, self.player), can_unlock)
add_rule(self.multiworld.get_location(LocationName.Tamakos_HP_PMD, self.player), can_unlock)
# Set Job additional area access # Set Job additional area access
self.multiworld.get_location(LocationName.Please_deliver_this, self.player).access_rule = \ self.multiworld.get_location(LocationName.Please_deliver_this, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.ACDC_Overworld, self.player) and \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) state.can_reach_region(RegionName.ACDC_Cyberworld, self.player)
self.multiworld.get_location(LocationName.My_Navi_is_sick, self.player).access_rule =\ self.multiworld.get_location(LocationName.My_Navi_is_sick, self.player).access_rule =\
lambda state: \ lambda state: \
state.has(ItemName.Recov30_star, self.player) state.has(ItemName.Recov30_star, self.player)
self.multiworld.get_location(LocationName.Help_me_with_my_son, self.player).access_rule =\ self.multiworld.get_location(LocationName.Help_me_with_my_son, self.player).access_rule =\
lambda state:\ lambda state:\
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.Yoka_Overworld, self.player) and \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) state.can_reach_region(RegionName.ACDC_Cyberworld, self.player)
self.multiworld.get_location(LocationName.Transmission_error, self.player).access_rule = \ self.multiworld.get_location(LocationName.Transmission_error, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) state.can_reach_region(RegionName.Yoka_Overworld, self.player)
self.multiworld.get_location(LocationName.Chip_Prices, self.player).access_rule = \ self.multiworld.get_location(LocationName.Chip_Prices, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) and \ state.can_reach_region(RegionName.ACDC_Cyberworld, self.player) and \
state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player) state.can_reach_region(RegionName.SciLab_Cyberworld, self.player)
self.multiworld.get_location(LocationName.Im_broke, self.player).access_rule = \ self.multiworld.get_location(LocationName.Im_broke, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.Yoka_Overworld, self.player) and \
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player) state.can_reach_region(RegionName.Yoka_Cyberworld, self.player)
self.multiworld.get_location(LocationName.Rare_chips_for_cheap, self.player).access_rule = \ self.multiworld.get_location(LocationName.Rare_chips_for_cheap, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) state.can_reach_region(RegionName.ACDC_Overworld, self.player)
self.multiworld.get_location(LocationName.Be_my_boyfriend, self.player).access_rule =\ self.multiworld.get_location(LocationName.Be_my_boyfriend, self.player).access_rule =\
lambda state: \ lambda state: \
state.can_reach(RegionName.Beach_Cyberworld, "Region", self.player) state.can_reach_region(RegionName.Beach_Cyberworld, self.player)
self.multiworld.get_location(LocationName.Will_you_deliver, self.player).access_rule=\ self.multiworld.get_location(LocationName.Will_you_deliver, self.player).access_rule=\
lambda state: \ lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.Yoka_Overworld, self.player) and \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.Beach_Overworld, self.player) and \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) state.can_reach_region(RegionName.ACDC_Cyberworld, self.player)
self.multiworld.get_location(LocationName.Somebody_please_help, self.player).access_rule = \ self.multiworld.get_location(LocationName.Somebody_please_help, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) state.can_reach_region(RegionName.ACDC_Overworld, self.player)
self.multiworld.get_location(LocationName.Looking_for_condor, self.player).access_rule = \ self.multiworld.get_location(LocationName.Looking_for_condor, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.Yoka_Overworld, self.player) and \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.Beach_Overworld, self.player) and \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) state.can_reach_region(RegionName.ACDC_Overworld, self.player)
self.multiworld.get_location(LocationName.Help_with_rehab, self.player).access_rule = \ self.multiworld.get_location(LocationName.Help_with_rehab, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) state.can_reach_region(RegionName.Beach_Overworld, self.player)
self.multiworld.get_location(LocationName.Old_Master, self.player).access_rule = \ self.multiworld.get_location(LocationName.Old_Master, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.ACDC_Overworld, self.player) and \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) state.can_reach_region(RegionName.Beach_Overworld, self.player)
self.multiworld.get_location(LocationName.Catching_gang_members, self.player).access_rule = \ self.multiworld.get_location(LocationName.Catching_gang_members, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player) and \ state.can_reach_region(RegionName.Yoka_Cyberworld, self.player) and \
state.has(ItemName.Press, self.player) state.has(ItemName.Press, self.player)
self.multiworld.get_location(LocationName.Please_adopt_a_virus, self.player).access_rule = \ self.multiworld.get_location(LocationName.Please_adopt_a_virus, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player) state.can_reach_region(RegionName.SciLab_Cyberworld, self.player)
self.multiworld.get_location(LocationName.Legendary_Tomes, self.player).access_rule = \ self.multiworld.get_location(LocationName.Legendary_Tomes, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.Beach_Overworld, self.player) and \
state.can_reach(RegionName.Undernet, "Region", self.player) and \ state.can_reach_region(RegionName.Undernet, self.player) and \
state.can_reach(RegionName.Deep_Undernet, "Region", self.player) and \ state.can_reach_region(RegionName.Deep_Undernet, self.player) and \
state.has_all({ItemName.Press, ItemName.Magnum1_A}, self.player) state.has_all({ItemName.Press, ItemName.Magnum1_A}, self.player)
self.multiworld.get_location(LocationName.Legendary_Tomes_Treasure, self.player).access_rule = \ self.multiworld.get_location(LocationName.Legendary_Tomes_Treasure, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.ACDC_Overworld, self.player) and \
state.can_reach(LocationName.Legendary_Tomes, "Location", self.player) state.can_reach_location(LocationName.Legendary_Tomes, self.player)
self.multiworld.get_location(LocationName.Hide_and_seek_First_Child, self.player).access_rule = \ self.multiworld.get_location(LocationName.Hide_and_seek_First_Child, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) state.can_reach_region(RegionName.Yoka_Overworld, self.player)
self.multiworld.get_location(LocationName.Hide_and_seek_Second_Child, self.player).access_rule = \ self.multiworld.get_location(LocationName.Hide_and_seek_Second_Child, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) state.can_reach_region(RegionName.Yoka_Overworld, self.player)
self.multiworld.get_location(LocationName.Hide_and_seek_Third_Child, self.player).access_rule = \ self.multiworld.get_location(LocationName.Hide_and_seek_Third_Child, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) state.can_reach_region(RegionName.Yoka_Overworld, self.player)
self.multiworld.get_location(LocationName.Hide_and_seek_Fourth_Child, self.player).access_rule = \ self.multiworld.get_location(LocationName.Hide_and_seek_Fourth_Child, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) state.can_reach_region(RegionName.Yoka_Overworld, self.player)
self.multiworld.get_location(LocationName.Hide_and_seek_Completion, self.player).access_rule = \ self.multiworld.get_location(LocationName.Hide_and_seek_Completion, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) state.can_reach_region(RegionName.Yoka_Overworld, self.player)
self.multiworld.get_location(LocationName.Finding_the_blue_Navi, self.player).access_rule = \ self.multiworld.get_location(LocationName.Finding_the_blue_Navi, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Undernet, "Region", self.player) state.can_reach_region(RegionName.Undernet, self.player)
self.multiworld.get_location(LocationName.Give_your_support, self.player).access_rule = \ self.multiworld.get_location(LocationName.Give_your_support, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) state.can_reach_region(RegionName.Beach_Overworld, self.player)
self.multiworld.get_location(LocationName.Stamp_collecting, self.player).access_rule = \ self.multiworld.get_location(LocationName.Stamp_collecting, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.Beach_Overworld, self.player) and \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) and \ state.can_reach_region(RegionName.ACDC_Cyberworld, self.player) and \
state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player) and \ state.can_reach_region(RegionName.SciLab_Cyberworld, self.player) and \
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player) and \ state.can_reach_region(RegionName.Yoka_Cyberworld, self.player) and \
state.can_reach(RegionName.Beach_Cyberworld, "Region", self.player) state.can_reach_region(RegionName.Beach_Cyberworld, self.player)
self.multiworld.get_location(LocationName.Help_with_a_will, self.player).access_rule = \ self.multiworld.get_location(LocationName.Help_with_a_will, self.player).access_rule = \
lambda state: \ lambda state: \
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.ACDC_Overworld, self.player) and \
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) and \ state.can_reach_region(RegionName.ACDC_Cyberworld, self.player) and \
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.Yoka_Overworld, self.player) and \
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player) and \ state.can_reach_region(RegionName.Yoka_Cyberworld, self.player) and \
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \ state.can_reach_region(RegionName.Beach_Overworld, self.player) and \
state.can_reach(RegionName.Undernet, "Region", self.player) state.can_reach_region(RegionName.Undernet, self.player)
# Set Trade quests # Set Trade quests
self.multiworld.get_location(LocationName.ACDC_SonicWav_W_Trade, self.player).access_rule =\ self.multiworld.get_location(LocationName.ACDC_SonicWav_W_Trade, self.player).access_rule =\
@ -390,6 +411,11 @@ class MMBN3World(World):
self.multiworld.get_location(LocationName.Numberman_Code_31, self.player).access_rule =\ self.multiworld.get_location(LocationName.Numberman_Code_31, self.player).access_rule =\
lambda state: self.explore_score(state) > 10 lambda state: self.explore_score(state) > 10
#miscellaneous locations with extra requirements
add_rule(self.multiworld.get_location(LocationName.Comedian, self.player),
lambda state: state.has(ItemName.Humor, self.player))
add_rule(self.multiworld.get_location(LocationName.Villain, self.player),
lambda state: state.has(ItemName.BlckMnd, self.player))
def not_undernet(item): return item.code != item_table[ItemName.Progressive_Undernet_Rank].code or item.player != self.player 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_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_1_East_BMD, self.player).item_rule = not_undernet
@ -500,24 +526,24 @@ class MMBN3World(World):
Determine roughly how much of the game you can explore to make certain checks not restrict much movement Determine roughly how much of the game you can explore to make certain checks not restrict much movement
""" """
score = 0 score = 0
if state.can_reach(RegionName.WWW_Island, "Region", self.player): if state.can_reach_region(RegionName.WWW_Island, self.player):
return 999 return 999
if state.can_reach(RegionName.SciLab_Overworld, "Region", self.player): if state.can_reach_region(RegionName.SciLab_Overworld, self.player):
score += 3 score += 3
if state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player): if state.can_reach_region(RegionName.SciLab_Cyberworld, self.player):
score += 1 score += 1
if state.can_reach(RegionName.Yoka_Overworld, "Region", self.player): if state.can_reach_region(RegionName.Yoka_Overworld, self.player):
score += 2 score += 2
if state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player): if state.can_reach_region(RegionName.Yoka_Cyberworld, self.player):
score += 1 score += 1
if state.can_reach(RegionName.Beach_Overworld, "Region", self.player): if state.can_reach_region(RegionName.Beach_Overworld, self.player):
score += 3 score += 3
if state.can_reach(RegionName.Beach_Cyberworld, "Region", self.player): if state.can_reach_region(RegionName.Beach_Cyberworld, self.player):
score += 1 score += 1
if state.can_reach(RegionName.Undernet, "Region", self.player): if state.can_reach_region(RegionName.Undernet, self.player):
score += 2 score += 2
if state.can_reach(RegionName.Deep_Undernet, "Region", self.player): if state.can_reach_region(RegionName.Deep_Undernet, self.player):
score += 1 score += 1
if state.can_reach(RegionName.Secret_Area, "Region", self.player): if state.can_reach_region(RegionName.Secret_Area, self.player):
score += 1 score += 1
return score return score