Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mega Man 2: Implement New Game #3256

Merged
merged 172 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
172 commits
Select commit Hold shift + click to select a range
b3bafd1
initial (broken) commit
Silvris May 17, 2023
b77a168
small work on init
Silvris May 21, 2023
92367b3
Update Items.py
Silvris May 21, 2023
25ec499
beginning work, some rom patches
Silvris Jul 3, 2023
44c28e6
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Jul 7, 2023
9a0c9c4
commit progress from bh branch
Silvris Aug 16, 2023
3d7b22f
Merge branch 'main' into mm2
Silvris Aug 16, 2023
beabf37
deathlink, fix soft-reset kill, e-tank loss
Silvris Aug 21, 2023
b8c6a5d
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Oct 3, 2023
b7f3dac
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Oct 3, 2023
4a02645
begin work on targeting new bhclient
Silvris Oct 4, 2023
6d989da
write font
Silvris Oct 9, 2023
994f33c
Merge branch 'ArchipelagoMW:main' into mm2
Silvris Nov 29, 2023
a79d66d
definitely didn't forget to add the other two hashes no
Silvris Dec 2, 2023
9eb6ae9
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Jan 7, 2024
b9bfd56
update to modern options, begin colors
Silvris Jan 7, 2024
e6f0a03
fix 6th letter bug
Silvris Jan 18, 2024
0688aa1
palette shuffle + logic rewrite
Silvris Feb 18, 2024
058cf42
fix a bunch of pointers
Silvris Feb 19, 2024
758e0ee
fix color changes, deathlink, and add wily 5 req
Silvris Feb 22, 2024
e40918c
adjust weapon weakness generation
Silvris Feb 25, 2024
de81bc2
Update Rules.py
Silvris Mar 2, 2024
78b7f9e
attempt wily 5 softlock fix
Silvris Mar 7, 2024
44aaccc
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Mar 7, 2024
6fb7daf
add explicit test for rbm weaknesses
Silvris Mar 7, 2024
858a6f3
fix difficulty and hard reset
Silvris Mar 8, 2024
3c49e1a
fix connect deathlink and off by one item color
Silvris Mar 8, 2024
636bf8d
fix atomic fire again
Silvris Mar 9, 2024
9144f26
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Mar 9, 2024
33bd75d
de-jank deathlink
Silvris Mar 9, 2024
2446bac
rewrite wily5 rule
Silvris Mar 10, 2024
5cd2b05
fix rare solo-gen fill issue, hopefully
Silvris Mar 15, 2024
956266c
Update Client.py
Silvris Mar 15, 2024
9cd54ff
fix wily 5 requirements
Silvris Mar 16, 2024
633f59f
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Mar 16, 2024
ed145a8
undo fill hook
Silvris Mar 16, 2024
736d9d9
fix picopico-kun rules
Silvris Mar 20, 2024
4d05c5e
for real this time
Silvris Mar 20, 2024
8cb7ddd
update minimum damage requirement
Silvris Mar 29, 2024
3184954
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Mar 29, 2024
40410d7
begin move to procedure patch
Silvris Mar 29, 2024
1d1f129
finish move to APPP, allow rando boobeam, color updates
Silvris Apr 3, 2024
8e3d39b
fix color bug, UT support?
Silvris Apr 3, 2024
3a667d5
what do you mean I forgot the procedure
Silvris Apr 3, 2024
43071a4
fix UT?
Silvris Apr 4, 2024
0179ea3
plando weakness and fixes
Silvris Apr 4, 2024
946c0b6
sfx when item received, more time stopper edge cases
Silvris Apr 4, 2024
5c16f80
Update test_weakness.py
Silvris Apr 7, 2024
ec6ba7e
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Apr 7, 2024
2f1a626
fix rules and color bug
Silvris Apr 7, 2024
04694a5
fix color bug, support reduced flashing
Silvris Apr 8, 2024
a621f8c
major world overhaul
Silvris Apr 11, 2024
baececd
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Apr 11, 2024
17025d8
Update Locations.py
Silvris Apr 11, 2024
cc91cc8
fix first found bugs
Silvris Apr 11, 2024
8821412
mypy cleanup
Silvris Apr 12, 2024
78b25f1
headerless roms
Silvris Apr 12, 2024
8410049
Update Rom.py
Silvris Apr 12, 2024
4be1122
further cleanup
Silvris Apr 12, 2024
5836410
work on energylink
Silvris Apr 13, 2024
a56aee5
el fixes
Silvris Apr 14, 2024
f0f5b6f
update to energylink 2.0 packet
Silvris Apr 14, 2024
fdddb5d
energylink balancing
Silvris Apr 14, 2024
2f6301a
potentially break other clients, more balancing
Silvris Apr 14, 2024
e3c21e9
Update Items.py
Silvris Apr 14, 2024
476b08e
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Apr 14, 2024
75abfe3
remove startup change from basepatch
Silvris Apr 14, 2024
a7cf6dd
el balancing and feedback
Silvris Apr 15, 2024
c92188e
hopefully less test failures?
Silvris Apr 15, 2024
2042127
implement world version check
Silvris Apr 15, 2024
ac37226
add weapon/health option
Silvris Apr 15, 2024
b47c480
Update Rom.py
Silvris Apr 15, 2024
d872e33
x/x2
Silvris Apr 15, 2024
ae6fc2f
specials
Silvris Apr 15, 2024
d8f9430
Update Color.py
Silvris Apr 15, 2024
495b9e3
Update Options.py
Silvris Apr 15, 2024
d00eea0
finally apply location groups
Silvris Apr 15, 2024
89f2cbe
bump minor version number instead
Silvris Apr 15, 2024
c4bde5e
fix duplicate stage sends
Silvris Apr 17, 2024
05155ec
validate wily 5, tests
Silvris Apr 17, 2024
4d80bae
see if renaming fixes
Silvris Apr 17, 2024
465828f
add shuffled weakness
Silvris Apr 17, 2024
5dec7ac
remove passwords
Silvris Apr 18, 2024
9e7d9ab
refresh rbm select, fix wily 5 validation
Silvris Apr 18, 2024
96e3900
forgot we can't check 0
Silvris Apr 18, 2024
5674610
oops I broke the basepatch (remove failing test later)
Silvris Apr 18, 2024
b103b1c
fix solo gen fill error?
Silvris Apr 20, 2024
fb28d87
fix webhost patch recognition
Silvris Apr 20, 2024
c39ddc9
fix imports, basepatch
Silvris Apr 20, 2024
1d5b3e3
move to flexibility metric for boss validation
Silvris Apr 20, 2024
272ba87
special case boobeam trap
Silvris Apr 20, 2024
4e05fc3
block strobe on stage select init
Silvris Apr 20, 2024
37cf6f6
more energylink balancing
Silvris Apr 21, 2024
26768c2
bump world version
Silvris Apr 21, 2024
556a77b
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Apr 21, 2024
e47f4a0
wily HP inaccurate in validation
Silvris Apr 21, 2024
c363320
fix validation edge case
Silvris Apr 21, 2024
5625a09
save last completed wily to data storage
Silvris Apr 21, 2024
a4108cd
mypy and pep8 cleanup
Silvris Apr 21, 2024
fd62563
fix file browse validation
Silvris Apr 26, 2024
6ff1c63
fix test failure, add enemy weakness
Silvris Apr 27, 2024
2479fa4
remove test seed
Silvris Apr 27, 2024
6e09cc8
update enemy damage
Silvris Apr 29, 2024
9315b33
inno setup
Silvris Apr 30, 2024
c457b77
Update en_Mega Man 2.md
Silvris Apr 30, 2024
9c1fe48
setup guide
Silvris Apr 30, 2024
7d761ad
Update en_Mega Man 2.md
Silvris Apr 30, 2024
dc31fa7
finish plando weakness section
Silvris Apr 30, 2024
b881df0
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Apr 30, 2024
01963b8
starting rbm edge case
Silvris May 2, 2024
efa6bb4
Merge remote-tracking branch 'upstream/main' into mm2
Silvris May 3, 2024
99f2d38
remove * imports
Silvris May 3, 2024
781be64
properly wrap later weakness additions in regen playthrough
Silvris May 3, 2024
03fd1ec
fix import
Silvris May 3, 2024
d5b9056
forgot readme
Silvris May 3, 2024
0346ef5
remove time stopper special casing
Silvris May 3, 2024
5536277
properly type added locations
Silvris May 3, 2024
b08446a
Merge branch 'main' into mm2
Silvris May 5, 2024
3e528ff
Update CODEOWNERS
Silvris May 6, 2024
09069f4
Merge branch 'main' into mm2
Silvris May 6, 2024
60dac37
Merge branch 'main' into mm2
Silvris May 18, 2024
1658e9c
add animation reduction
Silvris May 21, 2024
bac14d9
Merge branch 'main' into mm2
Silvris May 28, 2024
d5db9ea
deprioritize Time Stopper in rush checks
Silvris May 31, 2024
a5c400d
special case wily phase 1
Silvris May 31, 2024
d9bf59e
fix key error
Silvris May 31, 2024
c5a4035
forgot the test
Silvris May 31, 2024
5a82c82
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Jun 1, 2024
c8769c3
Merge branch 'ArchipelagoMW:main' into mm2
Silvris Jul 4, 2024
c7285a7
music and general cleanup
Silvris Jul 25, 2024
02934a7
the great rename
Silvris Jul 25, 2024
093dc5c
fix import
Silvris Jul 25, 2024
4984adc
thanks pycharm
Silvris Jul 25, 2024
dcca27e
reorder palette shuffle
Silvris Jul 25, 2024
6f09bcb
Merge remote-tracking branch 'upstream/main' into mm2
Silvris Jul 25, 2024
87d5dd8
account for alien on shuffled weakness
Silvris Jul 25, 2024
e1e8186
apply suggestions
Silvris Jul 25, 2024
fd94e40
fix seedbleed
Silvris Jul 25, 2024
f196854
fix invalid buster passthrough
Silvris Jul 25, 2024
a17ca2d
fix weakness landing beneath required amount
Silvris Jul 25, 2024
d96b0fb
fix failsafe
Silvris Jul 25, 2024
1050496
finish music
Silvris Jul 25, 2024
94d4ae6
Merge branch 'main' into mm2
Silvris Aug 8, 2024
ce87e56
Merge branch 'main' into mm2
Silvris Aug 9, 2024
36e1cef
fix Time Stopper on Flash/Alien
Silvris Aug 9, 2024
0272133
asar pls
Silvris Aug 10, 2024
712a172
Apply suggestions from code review
Silvris Aug 15, 2024
353d750
world helpers
Silvris Aug 16, 2024
123d5f7
init cleanup
Silvris Aug 16, 2024
7537947
apostrophes
Silvris Aug 16, 2024
3c46e76
clearer wording
Silvris Aug 16, 2024
ba132d9
mypy and cleanup
Silvris Aug 16, 2024
5f004cf
options doc cleanup
Silvris Aug 16, 2024
c8ac1ba
Update rom.py
Silvris Aug 16, 2024
89a58bd
rules cleanup
Silvris Aug 16, 2024
d209e30
Update __init__.py
Silvris Aug 16, 2024
7af5a6f
Update __init__.py
Silvris Aug 16, 2024
02070b8
move to defaultdict
Silvris Aug 16, 2024
2faefd0
cleanup world helpers
Silvris Aug 16, 2024
4d01321
Merge branch 'main' into mm2
Silvris Aug 16, 2024
948fd09
Update __init__.py
Silvris Aug 16, 2024
b14c02e
Merge branch 'mm2' of https://github.com/Silvris/Archipelago into mm2
Silvris Aug 16, 2024
ac96ce6
Merge branch 'main' into mm2
Silvris Aug 19, 2024
ea1da22
remove unnecessary line from fill hook
Silvris Aug 19, 2024
5348438
Merge branch 'mm2' of https://github.com/Silvris/Archipelago into mm2
Silvris Aug 19, 2024
2d31b24
forgot the other one
Silvris Aug 19, 2024
7d7b838
apply code review
Silvris Aug 19, 2024
0cf3549
remove collect
Silvris Aug 19, 2024
39e5985
Update rules.py
Silvris Aug 19, 2024
8633866
forgot another
Silvris Aug 19, 2024
47cd820
Merge branch 'main' into mm2
Silvris Aug 19, 2024
29ee838
Merge branch 'main' into mm2
Silvris Aug 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Currently, the following games are supported:
* A Hat in Time
* Old School Runescape
* Kingdom Hearts 1
* Mega Man 2

For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled
Expand Down
3 changes: 3 additions & 0 deletions docs/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@
# Minecraft
/worlds/minecraft/ @KonoTyran @espeon65536

# Mega Man 2
/worlds/mm2/ @Silvris

# MegaMan Battle Network 3
/worlds/mmbn3/ @digiholic

Expand Down
5 changes: 5 additions & 0 deletions inno_setup.iss
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ Root: HKCR; Subkey: "{#MyAppName}cv64patch"; ValueData: "Arc
Root: HKCR; Subkey: "{#MyAppName}cv64patch\DefaultIcon"; ValueData: "{app}\ArchipelagoBizHawkClient.exe,0"; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}cv64patch\shell\open\command"; ValueData: """{app}\ArchipelagoBizHawkClient.exe"" ""%1"""; ValueType: string; ValueName: "";

Root: HKCR; Subkey: ".apmm2"; ValueData: "{#MyAppName}mm2patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mm2patch"; ValueData: "Archipelago Mega Man 2 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mm2patch\DefaultIcon"; ValueData: "{app}\ArchipelagoBizHawkClient.exe,0"; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mm2patch\shell\open\command"; ValueData: """{app}\ArchipelagoBizHawkClient.exe"" ""%1"""; ValueType: string; ValueName: "";

Root: HKCR; Subkey: ".apladx"; ValueData: "{#MyAppName}ladxpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}ladxpatch"; ValueData: "Archipelago Links Awakening DX Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}ladxpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoLinksAwakeningClient.exe,0"; ValueType: string; ValueName: "";
Expand Down
290 changes: 290 additions & 0 deletions worlds/mm2/__init__.py
Silvris marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
import hashlib
import logging
from copy import deepcopy
from typing import Dict, Any, TYPE_CHECKING, Optional, Sequence, Tuple, ClassVar, List

from BaseClasses import Tutorial, ItemClassification, MultiWorld, Item, Location
from worlds.AutoWorld import World, WebWorld
from .names import (dr_wily, heat_man_stage, air_man_stage, wood_man_stage, bubble_man_stage, quick_man_stage,
flash_man_stage, metal_man_stage, crash_man_stage)
from .items import (item_table, item_names, MM2Item, filler_item_weights, robot_master_weapon_table,
stage_access_table, item_item_table, lookup_item_to_id)
from .locations import (MM2Location, mm2_regions, MM2Region, energy_pickups, etank_1ups, lookup_location_to_id,
location_groups)
from .rom import patch_rom, MM2ProcedurePatch, MM2LCHASH, PROTEUSHASH, MM2VCHASH, MM2NESHASH
from .options import MM2Options, Consumables
from .client import MegaMan2Client
from .rules import set_rules, weapon_damage, robot_masters, weapons_to_name, minimum_weakness_requirement
import os
import threading
import base64
import settings
logger = logging.getLogger("Mega Man 2")

if TYPE_CHECKING:
from BaseClasses import CollectionState


class MM2Settings(settings.Group):
class RomFile(settings.UserFilePath):
"""File name of the MM2 EN rom"""
description = "Mega Man 2 ROM File"
copy_to: Optional[str] = "Mega Man 2 (USA).nes"
md5s = [MM2NESHASH, MM2VCHASH, MM2LCHASH, PROTEUSHASH]

def browse(self: settings.T,
filetypes: Optional[Sequence[Tuple[str, Sequence[str]]]] = None,
**kwargs: Any) -> Optional[settings.T]:
if not filetypes:
file_types = [("NES", [".nes"]), ("Program", [".exe"])] # LC1 is only a windows executable, no linux
return super().browse(file_types, **kwargs)
else:
return super().browse(filetypes, **kwargs)

@classmethod
def validate(cls, path: str) -> None:
"""Try to open and validate file against hashes"""
with open(path, "rb", buffering=0) as f:
try:
f.seek(0)
if f.read(4) == b"NES\x1A":
f.seek(16)
else:
f.seek(0)
cls._validate_stream_hashes(f)
base_rom_bytes = f.read()
basemd5 = hashlib.md5()
basemd5.update(base_rom_bytes)
if basemd5.hexdigest() == PROTEUSHASH:
# we need special behavior here
cls.copy_to = None
except ValueError:
raise ValueError(f"File hash does not match for {path}")

rom_file: RomFile = RomFile(RomFile.copy_to)


class MM2WebWorld(WebWorld):
theme = "partyTime"
tutorials = [

Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the Mega Man 2 randomizer connected to an Archipelago Multiworld.",
"English",
"setup_en.md",
"setup/en",
["Silvris"]
)
]


class MM2World(World):
"""
In the year 200X, following his prior defeat by Mega Man, the evil Dr. Wily has returned to take over the world with
his own group of Robot Masters. Mega Man once again sets out to defeat the eight Robot Masters and stop Dr. Wily.

"""

game = "Mega Man 2"
settings: ClassVar[MM2Settings]
options_dataclass = MM2Options
options: MM2Options
item_name_to_id = lookup_item_to_id
location_name_to_id = lookup_location_to_id
item_name_groups = item_names
location_name_groups = location_groups
web = MM2WebWorld()
rom_name: bytearray
world_version: Tuple[int, int, int] = (0, 3, 1)
wily_5_weapons: Dict[int, List[int]]

def __init__(self, world: MultiWorld, player: int):
self.rom_name = bytearray()
self.rom_name_available_event = threading.Event()
super().__init__(world, player)
self.weapon_damage = deepcopy(weapon_damage)
self.wily_5_weapons = {}

def create_regions(self) -> None:
menu = MM2Region("Menu", self.player, self.multiworld)
self.multiworld.regions.append(menu)
for region in mm2_regions:
stage = MM2Region(region, self.player, self.multiworld)
required_items = mm2_regions[region][0]
locations = mm2_regions[region][1]
prev_stage = mm2_regions[region][2]
if prev_stage is None:
menu.connect(stage, f"To {region}",
lambda state, items=required_items: state.has_all(items, self.player))
else:
old_stage = self.get_region(prev_stage)
old_stage.connect(stage, f"To {region}",
lambda state, items=required_items: state.has_all(items, self.player))
stage.add_locations(locations, MM2Location)
for location in stage.get_locations():
if location.address is None and location.name != dr_wily:
location.place_locked_item(MM2Item(location.name, ItemClassification.progression,
None, self.player))
if region in etank_1ups and self.options.consumables in (Consumables.option_1up_etank,
Consumables.option_all):
stage.add_locations(etank_1ups[region], MM2Location)
if region in energy_pickups and self.options.consumables in (Consumables.option_weapon_health,
Consumables.option_all):
stage.add_locations(energy_pickups[region], MM2Location)
self.multiworld.regions.append(stage)

def create_item(self, name: str) -> MM2Item:
item = item_table[name]
classification = ItemClassification.filler
if item.progression:
classification = ItemClassification.progression_skip_balancing \
if item.skip_balancing else ItemClassification.progression
if item.useful:
classification |= ItemClassification.useful
return MM2Item(name, classification, item.code, self.player)

def get_filler_item_name(self) -> str:
return self.random.choices(list(filler_item_weights.keys()),
weights=list(filler_item_weights.values()))[0]

def create_items(self) -> None:
itempool = []
# grab first robot master
robot_master = self.item_id_to_name[0x880101 + self.options.starting_robot_master.value]
self.multiworld.push_precollected(self.create_item(robot_master))
itempool.extend([self.create_item(name) for name in stage_access_table.keys()
if name != robot_master])
itempool.extend([self.create_item(name) for name in robot_master_weapon_table.keys()])
itempool.extend([self.create_item(name) for name in item_item_table.keys()])
total_checks = 24
if self.options.consumables in (Consumables.option_1up_etank,
Consumables.option_all):
total_checks += 20
if self.options.consumables in (Consumables.option_weapon_health,
Consumables.option_all):
total_checks += 27
Silvris marked this conversation as resolved.
Show resolved Hide resolved
remaining = total_checks - len(itempool)
itempool.extend([self.create_item(name)
for name in self.random.choices(list(filler_item_weights.keys()),
weights=list(filler_item_weights.values()),
k=remaining)])
self.multiworld.itempool += itempool

set_rules = set_rules

def generate_early(self) -> None:
Silvris marked this conversation as resolved.
Show resolved Hide resolved
if (not self.options.yoku_jumps
and self.options.starting_robot_master == "heat_man") or \
(not self.options.enable_lasers
and self.options.starting_robot_master == "quick_man"):
robot_master_pool = [1, 2, 3, 5, 6, 7, ]
if self.options.yoku_jumps:
robot_master_pool.append(0)
if self.options.enable_lasers:
robot_master_pool.append(4)
self.options.starting_robot_master.value = self.random.choice(robot_master_pool)
logger.warning(
f"Mega Man 2 ({self.player_name}): "
f"Incompatible starting Robot Master, changing to "
f"{self.options.starting_robot_master.current_key.replace('_', ' ').title()}")

def generate_basic(self) -> None:
goal_location = self.get_location(dr_wily)
goal_location.place_locked_item(MM2Item("Victory", ItemClassification.progression, None, self.player))
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)

def fill_hook(self,
progitempool: List["Item"],
usefulitempool: List["Item"],
filleritempool: List["Item"],
fill_locations: List["Location"]) -> None:
# on a solo gen, fill can try to force Wily into sphere 2, but for most generations this is impossible
# since MM2 can have a 2 item sphere 1, and 3 items are required for Wily
if self.multiworld.players > 1:
return # Don't need to change anything on a multi gen, fill should be able to solve it with a 4 sphere 1
rbm_to_item = {
0: heat_man_stage,
1: air_man_stage,
2: wood_man_stage,
3: bubble_man_stage,
4: quick_man_stage,
5: flash_man_stage,
6: metal_man_stage,
7: crash_man_stage
}
Silvris marked this conversation as resolved.
Show resolved Hide resolved
affected_rbm = [2, 3] # Wood and Bubble will always have this happen
possible_rbm = [1, 5] # Air and Flash are always valid targets, due to Item 2/3 receive
if self.options.consumables:
possible_rbm.append(6) # Metal has 3 consumables
possible_rbm.append(7) # Crash has 3 consumables
if self.options.enable_lasers:
possible_rbm.append(4) # Quick has a lot of consumables, but needs logical time stopper if not enabled
else:
affected_rbm.extend([6, 7]) # only two checks on non consumables
if self.options.yoku_jumps:
possible_rbm.append(0) # Heat has 3 locations always, but might need 2 items logically
if self.options.starting_robot_master.value in affected_rbm:
rbm_names = list(map(lambda s: rbm_to_item[s], possible_rbm))
valid_second = [item for item in progitempool
if item.name in rbm_names
and item.player == self.player]
placed_item = self.random.choice(valid_second)
rbm_defeated = (f"{robot_masters[self.options.starting_robot_master.value].replace(' Defeated', '')}"
f" - Defeated")
rbm_location = self.get_location(rbm_defeated)
rbm_location.place_locked_item(placed_item)
progitempool.remove(placed_item)
fill_locations.remove(rbm_location)
target_rbm = (placed_item.code & 0xF) - 1
if self.options.strict_weakness or (self.options.random_weakness
and not (self.weapon_damage[0][target_rbm] > 0)):
# we need to find a weakness for this boss
weaknesses = [weapon for weapon in range(1, 9)
if self.weapon_damage[weapon][target_rbm] >= minimum_weakness_requirement[weapon]]
weapons = list(map(lambda s: weapons_to_name[s], weaknesses))
valid_weapons = [item for item in progitempool
if item.name in weapons
and item.player == self.player]
placed_weapon = self.random.choice(valid_weapons)
weapon_name = next(name for name, idx in lookup_location_to_id.items()
if idx == 0x880101 + self.options.starting_robot_master.value)
weapon_location = self.get_location(weapon_name)
weapon_location.place_locked_item(placed_weapon)
progitempool.remove(placed_weapon)
fill_locations.remove(weapon_location)

def generate_output(self, output_directory: str) -> None:
try:
patch = MM2ProcedurePatch(player=self.player, player_name=self.player_name)
patch_rom(self, patch)

self.rom_name = patch.name

patch.write(os.path.join(output_directory,
f"{self.multiworld.get_out_file_name_base(self.player)}{patch.patch_file_ending}"))
except Exception:
raise
finally:
self.rom_name_available_event.set() # make sure threading continues and errors are collected

def fill_slot_data(self) -> Dict[str, Any]:
return {
"death_link": self.options.death_link.value,
"weapon_damage": self.weapon_damage,
"wily_5_weapons": self.wily_5_weapons,
}

def interpret_slot_data(self, slot_data: Dict[str, Any]) -> Dict[str, Any]:
local_weapon = {int(key): value for key, value in slot_data["weapon_damage"].items()}
local_wily = {int(key): value for key, value in slot_data["wily_5_weapons"].items()}
return {"weapon_damage": local_weapon, "wily_5_weapons": local_wily}

def modify_multidata(self, multidata: Dict[str, Any]) -> None:
# wait for self.rom_name to be available.
self.rom_name_available_event.wait()
rom_name = getattr(self, "rom_name", None)
# we skip in case of error, so that the original error in the output thread is the one that gets raised
if rom_name:
new_name = base64.b64encode(bytes(self.rom_name)).decode()
multidata["connect_names"][new_name] = multidata["connect_names"][self.player_name]
Loading
Loading