MM2: minor bugfixes (#4190)
* move special cases to be outside strict * Update text.py * fix wily machine edge case, incorrect weapons, and time stopper failsafe * bump world version * weakness checking is inclusive * Update __init__.py * add air shooter to edge case validation
This commit is contained in:
parent
baf291d7a2
commit
bd5c8ec172
|
@ -96,13 +96,13 @@ class MM2World(World):
|
||||||
location_name_groups = location_groups
|
location_name_groups = location_groups
|
||||||
web = MM2WebWorld()
|
web = MM2WebWorld()
|
||||||
rom_name: bytearray
|
rom_name: bytearray
|
||||||
world_version: Tuple[int, int, int] = (0, 3, 1)
|
world_version: Tuple[int, int, int] = (0, 3, 2)
|
||||||
wily_5_weapons: Dict[int, List[int]]
|
wily_5_weapons: Dict[int, List[int]]
|
||||||
|
|
||||||
def __init__(self, world: MultiWorld, player: int):
|
def __init__(self, multiworld: MultiWorld, player: int):
|
||||||
self.rom_name = bytearray()
|
self.rom_name = bytearray()
|
||||||
self.rom_name_available_event = threading.Event()
|
self.rom_name_available_event = threading.Event()
|
||||||
super().__init__(world, player)
|
super().__init__(multiworld, player)
|
||||||
self.weapon_damage = deepcopy(weapon_damage)
|
self.weapon_damage = deepcopy(weapon_damage)
|
||||||
self.wily_5_weapons = {}
|
self.wily_5_weapons = {}
|
||||||
|
|
||||||
|
|
|
@ -133,28 +133,6 @@ def set_rules(world: "MM2World") -> None:
|
||||||
# Wily Machine needs all three weaknesses present, so allow
|
# Wily Machine needs all three weaknesses present, so allow
|
||||||
elif 4 > world.weapon_damage[weapon][i] > 0:
|
elif 4 > world.weapon_damage[weapon][i] > 0:
|
||||||
world.weapon_damage[weapon][i] = 0
|
world.weapon_damage[weapon][i] = 0
|
||||||
# handle special cases
|
|
||||||
for boss in range(14):
|
|
||||||
for weapon in (1, 3, 6, 8):
|
|
||||||
if (0 < world.weapon_damage[weapon][boss] < minimum_weakness_requirement[weapon] and
|
|
||||||
not any(world.weapon_damage[i][boss] > 0 for i in range(1, 8) if i != weapon)):
|
|
||||||
# Weapon does not have enough possible ammo to kill the boss, raise the damage
|
|
||||||
if boss == 9:
|
|
||||||
if weapon != 3:
|
|
||||||
# Atomic Fire and Crash Bomber cannot be Picopico-kun's only weakness
|
|
||||||
world.weapon_damage[weapon][boss] = 0
|
|
||||||
weakness = world.random.choice((2, 3, 4, 5, 7, 8))
|
|
||||||
world.weapon_damage[weakness][boss] = minimum_weakness_requirement[weakness]
|
|
||||||
elif boss == 11:
|
|
||||||
if weapon == 1:
|
|
||||||
# Atomic Fire cannot be Boobeam Trap's only weakness
|
|
||||||
world.weapon_damage[weapon][boss] = 0
|
|
||||||
weakness = world.random.choice((2, 3, 4, 5, 6, 7, 8))
|
|
||||||
world.weapon_damage[weakness][boss] = minimum_weakness_requirement[weakness]
|
|
||||||
else:
|
|
||||||
world.weapon_damage[weapon][boss] = minimum_weakness_requirement[weapon]
|
|
||||||
starting = world.options.starting_robot_master.value
|
|
||||||
world.weapon_damage[0][starting] = 1
|
|
||||||
|
|
||||||
for p_boss in world.options.plando_weakness:
|
for p_boss in world.options.plando_weakness:
|
||||||
for p_weapon in world.options.plando_weakness[p_boss]:
|
for p_weapon in world.options.plando_weakness[p_boss]:
|
||||||
|
@ -168,6 +146,28 @@ def set_rules(world: "MM2World") -> None:
|
||||||
world.weapon_damage[weapons_to_id[p_weapon]][bosses[p_boss]] \
|
world.weapon_damage[weapons_to_id[p_weapon]][bosses[p_boss]] \
|
||||||
= world.options.plando_weakness[p_boss][p_weapon]
|
= world.options.plando_weakness[p_boss][p_weapon]
|
||||||
|
|
||||||
|
# handle special cases
|
||||||
|
for boss in range(14):
|
||||||
|
for weapon in (1, 2, 3, 6, 8):
|
||||||
|
if (0 < world.weapon_damage[weapon][boss] < minimum_weakness_requirement[weapon] and
|
||||||
|
not any(world.weapon_damage[i][boss] >= minimum_weakness_requirement[weapon]
|
||||||
|
for i in range(9) if i != weapon)):
|
||||||
|
# Weapon does not have enough possible ammo to kill the boss, raise the damage
|
||||||
|
if boss == 9:
|
||||||
|
if weapon in (1, 6):
|
||||||
|
# Atomic Fire and Crash Bomber cannot be Picopico-kun's only weakness
|
||||||
|
world.weapon_damage[weapon][boss] = 0
|
||||||
|
weakness = world.random.choice((2, 3, 4, 5, 7, 8))
|
||||||
|
world.weapon_damage[weakness][boss] = minimum_weakness_requirement[weakness]
|
||||||
|
elif boss == 11:
|
||||||
|
if weapon == 1:
|
||||||
|
# Atomic Fire cannot be Boobeam Trap's only weakness
|
||||||
|
world.weapon_damage[weapon][boss] = 0
|
||||||
|
weakness = world.random.choice((2, 3, 4, 5, 6, 7, 8))
|
||||||
|
world.weapon_damage[weakness][boss] = minimum_weakness_requirement[weakness]
|
||||||
|
else:
|
||||||
|
world.weapon_damage[weapon][boss] = minimum_weakness_requirement[weapon]
|
||||||
|
|
||||||
if world.weapon_damage[0][world.options.starting_robot_master.value] < 1:
|
if world.weapon_damage[0][world.options.starting_robot_master.value] < 1:
|
||||||
world.weapon_damage[0][world.options.starting_robot_master.value] = weapon_damage[0][world.options.starting_robot_master.value]
|
world.weapon_damage[0][world.options.starting_robot_master.value] = weapon_damage[0][world.options.starting_robot_master.value]
|
||||||
|
|
||||||
|
@ -209,11 +209,11 @@ def set_rules(world: "MM2World") -> None:
|
||||||
continue
|
continue
|
||||||
highest, wp = max(zip(weapon_weight.values(), weapon_weight.keys()))
|
highest, wp = max(zip(weapon_weight.values(), weapon_weight.keys()))
|
||||||
uses = weapon_energy[wp] // weapon_costs[wp]
|
uses = weapon_energy[wp] // weapon_costs[wp]
|
||||||
used_weapons[boss].add(wp)
|
|
||||||
if int(uses * boss_damage[wp]) > boss_health[boss]:
|
if int(uses * boss_damage[wp]) > boss_health[boss]:
|
||||||
used = ceil(boss_health[boss] / boss_damage[wp])
|
used = ceil(boss_health[boss] / boss_damage[wp])
|
||||||
weapon_energy[wp] -= weapon_costs[wp] * used
|
weapon_energy[wp] -= weapon_costs[wp] * used
|
||||||
boss_health[boss] = 0
|
boss_health[boss] = 0
|
||||||
|
used_weapons[boss].add(wp)
|
||||||
elif highest <= 0:
|
elif highest <= 0:
|
||||||
# we are out of weapons that can actually damage the boss
|
# we are out of weapons that can actually damage the boss
|
||||||
# so find the weapon that has the most uses, and apply that as an additional weakness
|
# so find the weapon that has the most uses, and apply that as an additional weakness
|
||||||
|
@ -221,18 +221,21 @@ def set_rules(world: "MM2World") -> None:
|
||||||
# Quick Boomerang and no other, it would only be 28 off from defeating all 9, which Metal Blade should
|
# Quick Boomerang and no other, it would only be 28 off from defeating all 9, which Metal Blade should
|
||||||
# be able to cover
|
# be able to cover
|
||||||
wp, max_uses = max((weapon, weapon_energy[weapon] // weapon_costs[weapon]) for weapon in weapon_weight
|
wp, max_uses = max((weapon, weapon_energy[weapon] // weapon_costs[weapon]) for weapon in weapon_weight
|
||||||
if weapon != 0)
|
if weapon != 0 and (weapon != 8 or boss != 12))
|
||||||
|
# Wily Machine cannot under any circumstances take damage from Time Stopper, prevent this
|
||||||
world.weapon_damage[wp][boss] = minimum_weakness_requirement[wp]
|
world.weapon_damage[wp][boss] = minimum_weakness_requirement[wp]
|
||||||
used = min(int(weapon_energy[wp] // weapon_costs[wp]),
|
used = min(int(weapon_energy[wp] // weapon_costs[wp]),
|
||||||
ceil(boss_health[boss] // minimum_weakness_requirement[wp]))
|
ceil(boss_health[boss] / minimum_weakness_requirement[wp]))
|
||||||
weapon_energy[wp] -= weapon_costs[wp] * used
|
weapon_energy[wp] -= weapon_costs[wp] * used
|
||||||
boss_health[boss] -= int(used * minimum_weakness_requirement[wp])
|
boss_health[boss] -= int(used * minimum_weakness_requirement[wp])
|
||||||
weapon_weight.pop(wp)
|
weapon_weight.pop(wp)
|
||||||
|
used_weapons[boss].add(wp)
|
||||||
else:
|
else:
|
||||||
# drain the weapon and continue
|
# drain the weapon and continue
|
||||||
boss_health[boss] -= int(uses * boss_damage[wp])
|
boss_health[boss] -= int(uses * boss_damage[wp])
|
||||||
weapon_energy[wp] -= weapon_costs[wp] * uses
|
weapon_energy[wp] -= weapon_costs[wp] * uses
|
||||||
weapon_weight.pop(wp)
|
weapon_weight.pop(wp)
|
||||||
|
used_weapons[boss].add(wp)
|
||||||
|
|
||||||
world.wily_5_weapons = {boss: sorted(used_weapons[boss]) for boss in used_weapons}
|
world.wily_5_weapons = {boss: sorted(used_weapons[boss]) for boss in used_weapons}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from typing import DefaultDict
|
from typing import DefaultDict
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
MM2_WEAPON_ENCODING: DefaultDict[str, int] = defaultdict(lambda x: 0x6F, {
|
MM2_WEAPON_ENCODING: DefaultDict[str, int] = defaultdict(lambda: 0x6F, {
|
||||||
' ': 0x40,
|
' ': 0x40,
|
||||||
'A': 0x41,
|
'A': 0x41,
|
||||||
'B': 0x42,
|
'B': 0x42,
|
||||||
|
|
Loading…
Reference in New Issue