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

HK: Overflow approach to fix remove on split cloak #4024

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 26 additions & 10 deletions worlds/hk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -613,24 +613,40 @@ def create_vanilla_location(self, location: str, item: Item):
def collect(self, state, item: HKItem) -> bool:
change = super(HKWorld, self).collect(state, item)
if change:
prog_items = state.prog_items
for effect_name, effect_value in item_effects.get(item.name, {}).items():
state.prog_items[item.player][effect_name] += effect_value
if item.name in {"Left_Mothwing_Cloak", "Right_Mothwing_Cloak"}:
if state.prog_items[item.player].get('RIGHTDASH', 0) and \
state.prog_items[item.player].get('LEFTDASH', 0):
(state.prog_items[item.player]["RIGHTDASH"], state.prog_items[item.player]["LEFTDASH"]) = \
([max(state.prog_items[item.player]["RIGHTDASH"], state.prog_items[item.player]["LEFTDASH"])] * 2)
prog_items[self.player][effect_name] += effect_value

# a directional overflow in dash grants an upgrade for the other side
if prog_items[self.player]["RIGHTDASH"] > prog_items[self.player]["LEFTDASH"] > 0:
prog_items[self.player]["OVERFLOWLEFTDASH"] += 1
prog_items[self.player]["LEFTDASH"] += 1
assert item.name in ("Left_Mothwing_Cloak", "Right_Mothwing_Cloak",)
elif prog_items[self.player]["LEFTDASH"] > prog_items[self.player]["RIGHTDASH"] > 0:
prog_items[self.player]["OVERFLOWRIGHTDASH"] += 1
prog_items[self.player]["RIGHTDASH"] += 1
assert item.name in ("Left_Mothwing_Cloak", "Right_Mothwing_Cloak",)

return change

def remove(self, state, item: HKItem) -> bool:
change = super(HKWorld, self).remove(state, item)

if change:
prog_items = state.prog_items

# a directional overflow in dash grants an upgrade for the other side
if item.name in ("Left_Mothwing_Cloak", "Right_Mothwing_Cloak",) and \
prog_items[self.player]["RIGHTDASH"] == prog_items[self.player]["LEFTDASH"] > 0:
if prog_items[self.player]["OVERFLOWLEFTDASH"]:
prog_items[self.player]["LEFTDASH"] -= 1
prog_items[self.player]["OVERFLOWLEFTDASH"] -= 1
if prog_items[self.player]["OVERFLOWRIGHTDASH"]:
prog_items[self.player]["RIGHTDASH"] -= 1
prog_items[self.player]["OVERFLOWRIGHTDASH"] -= 1

for effect_name, effect_value in item_effects.get(item.name, {}).items():
if state.prog_items[item.player][effect_name] == effect_value:
del state.prog_items[item.player][effect_name]
else:
state.prog_items[item.player][effect_name] -= effect_value
prog_items[self.player][effect_name] -= effect_value

return change

Expand Down
69 changes: 69 additions & 0 deletions worlds/hk/test/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from test.bases import WorldTestBase
import typing
from BaseClasses import CollectionState # , Item, MultiWorld
from .. import HKWorld


class HKTestBase(WorldTestBase):
game = "Hollow Knight"
world: HKWorld

def assertAccessIndependency(
self,
locations: typing.List[str],
possible_items: typing.Iterable[typing.Iterable[str]],
only_check_listed: bool = False) -> None:
"""Asserts that the provided locations can't be reached without
the listed items but can be reached with any
one of the provided combinations"""
all_items = [
item_name for
item_names in
possible_items for
item_name in
item_names
]

state = CollectionState(self.multiworld)

for item_names in possible_items:
items = self.get_items_by_name(item_names)
for item in items:
self.collect_all_but(item.name)
for location in locations:
self.assertTrue(state.can_reach(location, "Location", 1),
f"{location} not reachable with {item_names}")
for item in items:
state.remove(item)

def assertAccessWithout(
self,
locations: typing.List[str],
possible_items: typing.Iterable[typing.Iterable[str]]) -> None:
"""Asserts that the provided locations can't be reached without the
listed items but can be reached with any
one of the provided combinations"""
all_items = [
item_name for
item_names in
possible_items for
item_name in
item_names
]

state = CollectionState(self.multiworld)
self.collect_all_but(all_items, state)
for location in locations:
self.assertTrue(
state.can_reach(location, "Location", 1),
f"{location} is not reachable without {all_items}")


class selectSeedHK(WorldTestBase):
game = "Hollow Knight"
# player: typing.ClassVar[int] = 1
seed = 0
world: HKWorld

def world_setup(self, *args, **kwargs):
super().world_setup(self.seed)
235 changes: 235 additions & 0 deletions worlds/hk/test/test_Settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
from . import HKTestBase, selectSeedHK


class TestGoal_any(HKTestBase):
options = {
"Goal": "any",
}


class TestGoal_hollowknight(HKTestBase):
options = {
"Goal": "hollowknight",
}


class TestGoal_siblings(HKTestBase):
options = {
"Goal": "siblings",
}


class TestGoal_radiance(HKTestBase):
options = {
"Goal": "radiance",
}


class TestGoal_godhome(HKTestBase):
options = {
"Goal": "godhome",
}


class TestGoal_godhome_flower(HKTestBase):
options = {
"Goal": "godhome_flower",
}


class TestRandomize_All(HKTestBase):
options = {
"Goal": "any",

"RandomizeDreamers": True,
"RandomizeSkills": True,
"RandomizeFocus": True,
"RandomizeSwim": True,
"RandomizeCharms": True,
"RandomizeKeys": True,
"RandomizeMaskShards": True,
"RandomizeVesselFragments": True,
"RandomizeCharmNotches": True,
"RandomizePaleOre": True,
"RandomizeGeoChests": True,
"RandomizeJunkPitChests": True,
"RandomizeRancidEggs": True,
"RandomizeRelics": True,
"RandomizeWhisperingRoots": True,
"RandomizeBossEssence": True,
"RandomizeGrubs": True,
"RandomizeMimics": True,
"RandomizeMaps": True,
"RandomizeStags": True,
"RandomizeLifebloodCocoons": True,
"RandomizeGrimmkinFlames": True,
"RandomizeJournalEntries": True,
"RandomizeNail": True,
"RandomizeGeoRocks": True,
"RandomizeBossGeo": True,
"RandomizeSoulTotems": True,
"RandomizeLoreTablets": True,
"RandomizeElevatorPass": True,
}


class TestRandomize_None(HKTestBase):
options = {
"Goal": "any",

"RandomizeDreamers": False,
"RandomizeSkills": False,
"RandomizeFocus": False,
"RandomizeSwim": False,
"RandomizeCharms": False,
"RandomizeKeys": False,
"RandomizeMaskShards": False,
"RandomizeVesselFragments": False,
"RandomizeCharmNotches": False,
"RandomizePaleOre": False,
"RandomizeGeoChests": False,
"RandomizeJunkPitChests": False,
"RandomizeRancidEggs": False,
"RandomizeRelics": False,
"RandomizeWhisperingRoots": False,
"RandomizeBossEssence": False,
"RandomizeGrubs": False,
"RandomizeMimics": False,
"RandomizeMaps": False,
"RandomizeStags": False,
"RandomizeLifebloodCocoons": False,
"RandomizeGrimmkinFlames": False,
"RandomizeJournalEntries": False,
"RandomizeNail": False,
"RandomizeGeoRocks": False,
"RandomizeBossGeo": False,
"RandomizeSoulTotems": False,
"RandomizeLoreTablets": False,
"RandomizeElevatorPass": False,
}


class TestSplit_All(HKTestBase):
options = {
"Goal": "any",

"SplitCrystalHeart": True,
"SplitMothwingCloak": True,
"SplitMantisClaw": True,
}


class TestCosts_All(HKTestBase):
options = {
"Goal": "any",

"EggShopSlots": 9,
"SlyShopSlots": 9,
"SlyKeyShopSlots": 9,
"IseldaShopSlots": 9,
"SalubraShopSlots": 9,
"SalubraCharmShopSlots": 9,
"LegEaterShopSlots": 9,
"GrubfatherRewardSlots": 9,
"SeerRewardSlots": 9,
"ExtraShopSlots": 9,
"CostSanity": True,
"CostSanityHybridChance": 1,
"CostSanityEggWeight": 1,
"CostSanityGrubWeight": 1,
"CostSanityEssenceWeight": 1,
"CostSanityCharmWeight": 1,
"CostSanityGeoWeight": 1,
}

# RandomizeDreamers,
# RandomizeSkills,
# RandomizeFocus,
# RandomizeSwim,
# RandomizeCharms,
# RandomizeKeys,
# RandomizeMaskShards,
# RandomizeVesselFragments,
# RandomizeCharmNotches,
# RandomizePaleOre,
# RandomizeGeoChests,
# RandomizeJunkPitChests,
# RandomizeRancidEggs,
# RandomizeRelics,
# RandomizeWhisperingRoots,
# RandomizeBossEssence,
# RandomizeGrubs,
# RandomizeMimics,
# RandomizeMaps,
# RandomizeStags,
# RandomizeLifebloodCocoons,
# RandomizeGrimmkinFlames,
# RandomizeJournalEntries,
# RandomizeNail,
# RandomizeGeoRocks,
# RandomizeBossGeo,
# RandomizeSoulTotems,
# RandomizeLoreTablets,
# RandomizeElevatorPass,
# StartLocation,
# Goal,
# WhitePalace,
# SplitCrystalHeart,
# SplitMothwingCloak,
# SplitMantisClaw,
# AddUnshuffledLocations,


# # not useful without more asserts
# PreciseMovement,
# ProficientCombat,
# BackgroundObjectPogos,
# EnemyPogos,
# ObscureSkips,
# ShadeSkips,
# InfectionSkips,
# FireballSkips,
# SpikeTunnels,
# AcidSkips,
# DamageBoosts,
# DangerousSkips,
# DarkRooms,
# ComplexSkips,
# DifficultSkips,
# RemoveSpellUpgrades,
# MinimumGeoPrice,
# MaximumGeoPrice,
# MinimumGrubPrice,
# MaximumGrubPrice,
# MinimumEssencePrice,
# MaximumEssencePrice,
# MinimumCharmPrice,
# MaximumCharmPrice,
# RandomCharmCosts,
# PlandoCharmCosts,
# MinimumEggPrice,
# MaximumEggPrice,
# EggShopSlots,
# SlyShopSlots,
# SlyKeyShopSlots,
# IseldaShopSlots,
# SalubraShopSlots,
# SalubraCharmShopSlots,
# LegEaterShopSlots,
# GrubfatherRewardSlots,
# SeerRewardSlots,
# ExtraShopSlots,
# CostSanity,
# CostSanityHybridChance,
# CostSanityEggWeight,
# CostSanityGrubWeight,
# CostSanityEssenceWeight,
# CostSanityCharmWeight,
# CostSanityGeoWeight,

# # should be ok not testing
# StartingGeo,
# DeathLink,
# DeathLinkShade,
# DeathLinkBreaksFragileCharms,
# ExtraPlatforms,
Loading
Loading