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 %} 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 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 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"