105 lines
4.1 KiB
Python
105 lines
4.1 KiB
Python
from math import ceil
|
|
|
|
from . import MM2TestBase
|
|
from ..options import bosses
|
|
|
|
|
|
# Need to figure out how this test should work
|
|
def validate_wily_5(base: MM2TestBase) -> None:
|
|
world = base.multiworld.worlds[base.player]
|
|
weapon_damage = world.weapon_damage
|
|
weapon_costs = {
|
|
0: 0,
|
|
1: 10,
|
|
2: 2,
|
|
3: 3,
|
|
4: 0.5,
|
|
5: 0.125,
|
|
6: 4,
|
|
7: 0.25,
|
|
8: 7,
|
|
}
|
|
boss_health = {boss: 0x1C if boss != 12 else 0x1C * 2 for boss in [*range(8), 12]}
|
|
weapon_energy = {key: float(0x1C) for key in weapon_costs}
|
|
weapon_boss = {boss: {weapon: world.weapon_damage[weapon][boss] for weapon in world.weapon_damage}
|
|
for boss in [*range(8), 12]}
|
|
flexibility = {
|
|
boss: (
|
|
sum(damage_value > 0 for damage_value in
|
|
weapon_damages.values()) # Amount of weapons that hit this boss
|
|
* sum(weapon_damages.values()) # Overall damage that those weapons do
|
|
)
|
|
for boss, weapon_damages in weapon_boss.items() if boss != 12
|
|
}
|
|
flexibility = sorted(flexibility, key=flexibility.get) # Fast way to sort dict by value
|
|
used_weapons = {i: set() for i in [*range(8), 12]}
|
|
for boss in [*flexibility, 12]:
|
|
boss_damage = weapon_boss[boss]
|
|
weapon_weight = {weapon: (weapon_energy[weapon] / damage) if damage else 0 for weapon, damage in
|
|
boss_damage.items() if weapon_energy[weapon] > 0}
|
|
if boss_damage[8]:
|
|
boss_damage[8] = 1.75 * boss_damage[8]
|
|
if any(boss_damage[i] > 0 for i in range(8)) and 8 in weapon_weight:
|
|
# We get exactly one use of Time Stopper during the rush
|
|
# So we want to make sure that use is absolutely needed
|
|
weapon_weight[8] = min(weapon_weight[8], 0.001)
|
|
while boss_health[boss] > 0:
|
|
if boss_damage[0] > 0:
|
|
boss_health[boss] = 0 # if we can buster, we should buster
|
|
continue
|
|
highest, wp = max(zip(weapon_weight.values(), weapon_weight.keys()))
|
|
uses = weapon_energy[wp] // weapon_costs[wp]
|
|
used_weapons[boss].add(wp)
|
|
if int(uses * boss_damage[wp]) > boss_health[boss]:
|
|
used = ceil(boss_health[boss] / boss_damage[wp])
|
|
weapon_energy[wp] -= weapon_costs[wp] * used
|
|
boss_health[boss] = 0
|
|
elif highest <= 0:
|
|
# we are out of weapons that can actually damage the boss
|
|
base.fail(f"Ran out of weapon energy to damage "
|
|
f"{next(name for name in bosses if bosses[name] == boss)}\n"
|
|
f"Seed: {base.multiworld.seed}\n"
|
|
f"Damage Table: {weapon_damage}")
|
|
else:
|
|
# drain the weapon and continue
|
|
boss_health[boss] -= int(uses * boss_damage[wp])
|
|
weapon_energy[wp] -= weapon_costs[wp] * uses
|
|
weapon_weight.pop(wp)
|
|
|
|
|
|
class StrictWeaknessTests(MM2TestBase):
|
|
options = {
|
|
"strict_weakness": True,
|
|
"yoku_jumps": True,
|
|
"enable_lasers": True
|
|
}
|
|
|
|
def test_that_every_boss_has_a_weakness(self) -> None:
|
|
world = self.multiworld.worlds[self.player]
|
|
weapon_damage = world.weapon_damage
|
|
for boss in range(14):
|
|
if not any(weapon_damage[weapon][boss] for weapon in range(9)):
|
|
self.fail(f"Boss {boss} generated without weakness! Seed: {self.multiworld.seed}")
|
|
|
|
def test_wily_5(self) -> None:
|
|
validate_wily_5(self)
|
|
|
|
|
|
class RandomStrictWeaknessTests(MM2TestBase):
|
|
options = {
|
|
"strict_weakness": True,
|
|
"random_weakness": "randomized",
|
|
"yoku_jumps": True,
|
|
"enable_lasers": True
|
|
}
|
|
|
|
def test_that_every_boss_has_a_weakness(self) -> None:
|
|
world = self.multiworld.worlds[self.player]
|
|
weapon_damage = world.weapon_damage
|
|
for boss in range(14):
|
|
if not any(weapon_damage[weapon][boss] for weapon in range(9)):
|
|
self.fail(f"Boss {boss} generated without weakness! Seed: {self.multiworld.seed}")
|
|
|
|
def test_wily_5(self) -> None:
|
|
validate_wily_5(self)
|