From 4979314825c4498bbaa16409ce1a4275c80bbf4b Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 17 Dec 2023 06:08:40 +0100 Subject: [PATCH 1/4] Webhost: open graph support for /room (#2580) * WebHost: add Open Graph metadata to /room * WebHost: Open Graph cleanup --- WebHostLib/templates/hostRoom.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/WebHostLib/templates/hostRoom.html b/WebHostLib/templates/hostRoom.html index ba15d64aca..2981c41452 100644 --- a/WebHostLib/templates/hostRoom.html +++ b/WebHostLib/templates/hostRoom.html @@ -3,6 +3,16 @@ {% block head %} Multiworld {{ room.id|suuid }} {% if should_refresh %}{% endif %} + + + + {% if room.seed.slots|length < 2 %} + + {% else %} + + {% endif %} {% endblock %} From a549af8304de03c3a6814c53d589a7b2f4a9c3f4 Mon Sep 17 00:00:00 2001 From: Zach Parks Date: Sun, 17 Dec 2023 10:11:40 -0600 Subject: [PATCH 2/4] Hollow Knight: Add additional DeathLink option and add ExtraPlatforms option. (#2545) --- worlds/hk/Options.py | 47 ++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/worlds/hk/Options.py b/worlds/hk/Options.py index fcc938474d..ef7fbd0dfe 100644 --- a/worlds/hk/Options.py +++ b/worlds/hk/Options.py @@ -2,7 +2,7 @@ import typing from .ExtractedData import logic_options, starts, pool_options from .Rules import cost_terms -from Options import Option, DefaultOnToggle, Toggle, Choice, Range, OptionDict, NamedRange +from Options import Option, DefaultOnToggle, Toggle, Choice, Range, OptionDict, NamedRange, DeathLink from .Charms import vanilla_costs, names as charm_names if typing.TYPE_CHECKING: @@ -402,22 +402,34 @@ class WhitePalace(Choice): default = 0 -class DeathLink(Choice): +class ExtraPlatforms(DefaultOnToggle): + """Places additional platforms to make traveling throughout Hallownest more convenient.""" + + +class DeathLinkShade(Choice): + """Sets whether to create a shade when you are killed by a DeathLink and how to handle your existing shade, if any. + + vanilla: DeathLink deaths function like any other death and overrides your existing shade (including geo), if any. + shadeless: DeathLink deaths do not spawn shades. Your existing shade (including geo), if any, is untouched. + shade: DeathLink deaths spawn a shade if you do not have an existing shade. Otherwise, it acts like shadeless. + + * This option has no effect if DeathLink is disabled. + ** Self-death shade behavior is not changed; if a self-death normally creates a shade in vanilla, it will override + your existing shade, if any. """ - When you die, everyone dies. Of course the reverse is true too. - When enabled, choose how incoming deathlinks are handled: - vanilla: DeathLink kills you and is just like any other death. RIP your previous shade and geo. - shadeless: DeathLink kills you, but no shade spawns and no geo is lost. Your previous shade, if any, is untouched. - shade: DeathLink functions like a normal death if you do not already have a shade, shadeless otherwise. - """ - option_off = 0 - alias_no = 0 - alias_true = 1 - alias_on = 1 - alias_yes = 1 + option_vanilla = 0 option_shadeless = 1 - option_vanilla = 2 - option_shade = 3 + option_shade = 2 + default = 2 + + +class DeathLinkBreaksFragileCharms(Toggle): + """Sets if fragile charms break when you are killed by a DeathLink. + + * This option has no effect if DeathLink is disabled. + ** Self-death fragile charm behavior is not changed; if a self-death normally breaks fragile charms in vanilla, it + will continue to do so. + """ class StartingGeo(Range): @@ -476,7 +488,8 @@ hollow_knight_options: typing.Dict[str, type(Option)] = { **{ option.__name__: option for option in ( - StartLocation, Goal, WhitePalace, StartingGeo, DeathLink, + StartLocation, Goal, WhitePalace, ExtraPlatforms, StartingGeo, + DeathLink, DeathLinkShade, DeathLinkBreaksFragileCharms, MinimumGeoPrice, MaximumGeoPrice, MinimumGrubPrice, MaximumGrubPrice, MinimumEssencePrice, MaximumEssencePrice, @@ -488,7 +501,7 @@ hollow_knight_options: typing.Dict[str, type(Option)] = { LegEaterShopSlots, GrubfatherRewardSlots, SeerRewardSlots, ExtraShopSlots, SplitCrystalHeart, SplitMothwingCloak, SplitMantisClaw, - CostSanity, CostSanityHybridChance, + CostSanity, CostSanityHybridChance ) }, **cost_sanity_weights From c8adadb08bbd23d1d11c114333fdb95477eb4fbf Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Mon, 18 Dec 2023 10:39:04 -0500 Subject: [PATCH 3/4] =?UTF-8?q?Pok=C3=A9mon=20R/B:=20Fix=20Flash=20learnab?= =?UTF-8?q?le=20logic=20(#2615)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- worlds/pokemon_rb/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/pokemon_rb/__init__.py b/worlds/pokemon_rb/__init__.py index d9bd6dde76..ee0f0052e1 100644 --- a/worlds/pokemon_rb/__init__.py +++ b/worlds/pokemon_rb/__init__.py @@ -414,7 +414,7 @@ class PokemonRedBlueWorld(World): > 7) or (self.multiworld.door_shuffle[self.player] not in ("off", "simple")))): intervene_move = "Cut" elif ((not logic.can_learn_hm(test_state, "Flash", self.player)) and self.multiworld.dark_rock_tunnel_logic[self.player] - and (((self.multiworld.accessibility[self.player] != "minimal" and + and (((self.multiworld.accessibility[self.player] != "minimal" or (self.multiworld.trainersanity[self.player] or self.multiworld.extra_key_items[self.player])) or self.multiworld.door_shuffle[self.player]))): intervene_move = "Flash" From 817197c14dabf8a90cf81464ce4c7cc1fb477493 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Mon, 18 Dec 2023 10:46:24 -0500 Subject: [PATCH 4/4] Lingo: Tests no longer disable forced good item (#2602) The static class with the "disable forced good item" field is gone. Now, certain tests that want to check for specific access progression can run a method that removes the forced good item and adds it back to the pool. Tests that don't care about this will collect the forced good item like normal. This should prevent the intermittent fill failures on complex doors unit tests, since the forced good item should provide enough locations to fill in. --- worlds/lingo/__init__.py | 1 - worlds/lingo/player_logic.py | 3 +-- worlds/lingo/test/TestDoors.py | 10 ++++++++++ worlds/lingo/test/TestOrangeTower.py | 4 ++++ worlds/lingo/test/TestProgressive.py | 6 ++++++ worlds/lingo/test/__init__.py | 8 ++++++-- worlds/lingo/testing.py | 2 -- 7 files changed, 27 insertions(+), 7 deletions(-) delete mode 100644 worlds/lingo/testing.py diff --git a/worlds/lingo/__init__.py b/worlds/lingo/__init__.py index f22d344c8f..0889674450 100644 --- a/worlds/lingo/__init__.py +++ b/worlds/lingo/__init__.py @@ -11,7 +11,6 @@ from .options import LingoOptions from .player_logic import LingoPlayerLogic from .regions import create_regions from .static_logic import Room, RoomEntrance -from .testing import LingoTestOptions class LingoWebWorld(WebWorld): diff --git a/worlds/lingo/player_logic.py b/worlds/lingo/player_logic.py index b046f1cfe3..fa497c59bd 100644 --- a/worlds/lingo/player_logic.py +++ b/worlds/lingo/player_logic.py @@ -6,7 +6,6 @@ from .options import LocationChecks, ShuffleDoors, VictoryCondition from .static_logic import DOORS_BY_ROOM, Door, PAINTINGS, PAINTINGS_BY_ROOM, PAINTING_ENTRANCES, PAINTING_EXITS, \ PANELS_BY_ROOM, PROGRESSION_BY_ROOM, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS, RoomAndDoor, \ RoomAndPanel -from .testing import LingoTestOptions if TYPE_CHECKING: from . import LingoWorld @@ -224,7 +223,7 @@ class LingoPlayerLogic: "kind of logic error.") if door_shuffle != ShuffleDoors.option_none and location_classification != LocationClassification.insanity \ - and not early_color_hallways and LingoTestOptions.disable_forced_good_item is False: + and not early_color_hallways is False: # If shuffle doors is on, force a useful item onto the HI panel. This may not necessarily get you out of BK, # but the goal is to allow you to reach at least one more check. The non-painting ones are hardcoded right # now. We only allow the entrance to the Pilgrim Room if color shuffle is off, because otherwise there are diff --git a/worlds/lingo/test/TestDoors.py b/worlds/lingo/test/TestDoors.py index f496c5f578..49a0f9c490 100644 --- a/worlds/lingo/test/TestDoors.py +++ b/worlds/lingo/test/TestDoors.py @@ -8,6 +8,8 @@ class TestRequiredRoomLogic(LingoTestBase): } def test_pilgrim_first(self) -> None: + self.remove_forced_good_item() + self.assertFalse(self.multiworld.state.can_reach("The Seeker", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Pilgrim Antechamber", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Pilgrim Room", "Region", self.player)) @@ -28,6 +30,8 @@ class TestRequiredRoomLogic(LingoTestBase): self.assertTrue(self.can_reach_location("The Seeker - Achievement")) def test_hidden_first(self) -> None: + self.remove_forced_good_item() + self.assertFalse(self.multiworld.state.can_reach("The Seeker", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Pilgrim Room", "Region", self.player)) self.assertFalse(self.can_reach_location("The Seeker - Achievement")) @@ -55,6 +59,8 @@ class TestRequiredDoorLogic(LingoTestBase): } def test_through_rhyme(self) -> None: + self.remove_forced_good_item() + self.assertFalse(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall")) self.collect_by_name("Starting Room - Rhyme Room Entrance") @@ -64,6 +70,8 @@ class TestRequiredDoorLogic(LingoTestBase): self.assertTrue(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall")) def test_through_hidden(self) -> None: + self.remove_forced_good_item() + self.assertFalse(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall")) self.collect_by_name("Starting Room - Rhyme Room Entrance") @@ -83,6 +91,8 @@ class TestSimpleDoors(LingoTestBase): } def test_requirement(self): + self.remove_forced_good_item() + self.assertFalse(self.multiworld.state.can_reach("Outside The Wanderer", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player)) diff --git a/worlds/lingo/test/TestOrangeTower.py b/worlds/lingo/test/TestOrangeTower.py index 7b0c3bb525..9170de108a 100644 --- a/worlds/lingo/test/TestOrangeTower.py +++ b/worlds/lingo/test/TestOrangeTower.py @@ -8,6 +8,8 @@ class TestProgressiveOrangeTower(LingoTestBase): } def test_from_welcome_back(self) -> None: + self.remove_forced_good_item() + self.assertFalse(self.multiworld.state.can_reach("Orange Tower First Floor", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower Second Floor", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player)) @@ -83,6 +85,8 @@ class TestProgressiveOrangeTower(LingoTestBase): self.assertTrue(self.multiworld.state.can_reach("Orange Tower Seventh Floor", "Region", self.player)) def test_from_hub_room(self) -> None: + self.remove_forced_good_item() + self.assertFalse(self.multiworld.state.can_reach("Orange Tower First Floor", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower Second Floor", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player)) diff --git a/worlds/lingo/test/TestProgressive.py b/worlds/lingo/test/TestProgressive.py index 917c6e7e89..8edc7ce6cc 100644 --- a/worlds/lingo/test/TestProgressive.py +++ b/worlds/lingo/test/TestProgressive.py @@ -7,6 +7,8 @@ class TestComplexProgressiveHallwayRoom(LingoTestBase): } def test_item(self): + self.remove_forced_good_item() + self.assertFalse(self.multiworld.state.can_reach("Outside The Agreeable", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Hallway Room (2)", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Hallway Room (3)", "Region", self.player)) @@ -58,6 +60,8 @@ class TestSimpleHallwayRoom(LingoTestBase): } def test_item(self): + self.remove_forced_good_item() + self.assertFalse(self.multiworld.state.can_reach("Outside The Agreeable", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Hallway Room (2)", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Hallway Room (3)", "Region", self.player)) @@ -86,6 +90,8 @@ class TestProgressiveArtGallery(LingoTestBase): } def test_item(self): + self.remove_forced_good_item() + self.assertFalse(self.multiworld.state.can_reach("Art Gallery", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player)) diff --git a/worlds/lingo/test/__init__.py b/worlds/lingo/test/__init__.py index ffbf9032b6..7ff456d8fc 100644 --- a/worlds/lingo/test/__init__.py +++ b/worlds/lingo/test/__init__.py @@ -1,7 +1,6 @@ from typing import ClassVar from test.bases import WorldTestBase -from .. import LingoTestOptions class LingoTestBase(WorldTestBase): @@ -9,5 +8,10 @@ class LingoTestBase(WorldTestBase): player: ClassVar[int] = 1 def world_setup(self, *args, **kwargs): - LingoTestOptions.disable_forced_good_item = True super().world_setup(*args, **kwargs) + + def remove_forced_good_item(self): + location = self.multiworld.get_location("Second Room - Good Luck", self.player) + self.remove(location.item) + self.multiworld.itempool.append(location.item) + self.multiworld.state.events.add(location) diff --git a/worlds/lingo/testing.py b/worlds/lingo/testing.py deleted file mode 100644 index 22fafea0fc..0000000000 --- a/worlds/lingo/testing.py +++ /dev/null @@ -1,2 +0,0 @@ -class LingoTestOptions: - disable_forced_good_item: bool = False