Files
Grinch-AP/worlds/mm2/test/test_weakness.py
Silvris 0e6e359747 Mega Man 2: Implement New Game (#3256)
* initial (broken) commit

* small work on init

* Update Items.py

* beginning work, some rom patches

* commit progress from bh branch

* deathlink, fix soft-reset kill, e-tank loss

* begin work on targeting new bhclient

* write font

* definitely didn't forget to add the other two hashes no

* update to modern options, begin colors

* fix 6th letter bug

* palette shuffle + logic rewrite

* fix a bunch of pointers

* fix color changes, deathlink, and add wily 5 req

* adjust weapon weakness generation

* Update Rules.py

* attempt wily 5 softlock fix

* add explicit test for rbm weaknesses

* fix difficulty and hard reset

* fix connect deathlink and off by one item color

* fix atomic fire again

* de-jank deathlink

* rewrite wily5 rule

* fix rare solo-gen fill issue, hopefully

* Update Client.py

* fix wily 5 requirements

* undo fill hook

* fix picopico-kun rules

* for real this time

* update minimum damage requirement

* begin move to procedure patch

* finish move to APPP, allow rando boobeam, color updates

* fix color bug, UT support?

* what do you mean I forgot the procedure

* fix UT?

* plando weakness and fixes

* sfx when item received, more time stopper edge cases

* Update test_weakness.py

* fix rules and color bug

* fix color bug, support reduced flashing

* major world overhaul

* Update Locations.py

* fix first found bugs

* mypy cleanup

* headerless roms

* Update Rom.py

* further cleanup

* work on energylink

* el fixes

* update to energylink 2.0 packet

* energylink balancing

* potentially break other clients, more balancing

* Update Items.py

* remove startup change from basepatch

we write that in patch, since we also need to clean the area before applying

* el balancing and feedback

* hopefully less test failures?

* implement world version check

* add weapon/health option

* Update Rom.py

* x/x2

* specials

* Update Color.py

* Update Options.py

* finally apply location groups

* bump minor version number instead

* fix duplicate stage sends

* validate wily 5, tests

* see if renaming fixes

* add shuffled weakness

* remove passwords

* refresh rbm select, fix wily 5 validation

* forgot we can't check 0

* oops I broke the basepatch (remove failing test later)

* fix solo gen fill error?

* fix webhost patch recognition

* fix imports, basepatch

* move to flexibility metric for boss validation

* special case boobeam trap

* block strobe on stage select init

* more energylink balancing

* bump world version

* wily HP inaccurate in validation

* fix validation edge case

* save last completed wily to data storage

* mypy and pep8 cleanup

* fix file browse validation

* fix test failure, add enemy weakness

* remove test seed

* update enemy damage

* inno setup

* Update en_Mega Man 2.md

* setup guide

* Update en_Mega Man 2.md

* finish plando weakness section

* starting rbm edge case

* remove * imports

* properly wrap later weakness additions in regen playthrough

* fix import

* forgot readme

* remove time stopper special casing

since we moved to proper wily 5 validation, this special casing is no longer important

* properly type added locations

* Update CODEOWNERS

* add animation reduction

* deprioritize Time Stopper in rush checks

* special case wily phase 1

* fix key error

* forgot the test

* music and general cleanup

* the great rename

* fix import

* thanks pycharm

* reorder palette shuffle

* account for alien on shuffled weakness

* apply suggestions

* fix seedbleed

* fix invalid buster passthrough

* fix weakness landing beneath required amount

* fix failsafe

* finish music

* fix Time Stopper on Flash/Alien

* asar pls

* Apply suggestions from code review

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

* world helpers

* init cleanup

* apostrophes

* clearer wording

* mypy and cleanup

* options doc cleanup

* Update rom.py

* rules cleanup

* Update __init__.py

* Update __init__.py

* move to defaultdict

* cleanup world helpers

* Update __init__.py

* remove unnecessary line from fill hook

* forgot the other one

* apply code review

* remove collect

* Update rules.py

* forgot another

---------

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
2024-08-20 04:59:29 +02:00

94 lines
3.7 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
boss_health = {boss: 0x1C for boss in [*list(range(8)), 12]}
weapon_costs = {
0: 0,
1: 10,
2: 2,
3: 3,
4: 0.5,
5: 0.125,
6: 4,
7: 0.25,
8: 7,
}
weapon_energy = {key: float(0x1C * 2) if key == 12 else float(0x1C) for key in weapon_costs}
weapon_boss = {boss: {weapon: weapon_damage[weapon][boss] for weapon in weapon_damage}
for boss in [*list(range(8)), 12]}
flexibility = [(sum(1 if weapon_boss[boss][weapon] > 0 else 0 for weapon in range(9)) *
sum(weapon_boss[boss].values()), boss) for boss in weapon_boss if boss != 12]
for _, boss in [*sorted(flexibility), (0, 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]}
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]:
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]
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)