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"