mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
Lingo: Add panels mode door shuffle (#3163)
* Created panels mode door shuffle * Added some panel door item names * Remove RUNT TURN panel door Not really useful. * Fix logic with First SIX related stuff * Add group_doors to slot data * Fix LEVEL 2 behavior with panels mode * Fixed unit tests * Fixed duplicate IDs from merge * Just regenerated new IDs * Fixed duplication of color and door group items * Removed unnecessary unit test option * Fix The Seeker being achievable without entrance door * Fix The Observant being achievable without locked panels * Added some more panel doors * Added Progressive Suits Area * Lingo: Fix Basement access with THE MASTER * Added indirect conditions for MASTER-blocked entrances * Fixed Incomparable achievement access * Fix STAIRS panel logic * Fix merge error with good items * Is this clearer? * DREAD and TURN LEARN * Allow a weird edge case for reduced locations Panels mode door shuffle + grouped doors + color shuffle + pilgrimage enabled is exactly the right number of items for reduced locations. Removing color shuffle also allows for disabling pilgrimage, adding sunwarp locking, or both, with a couple of locations left over. * Prevent small sphere one on panels mode * Added shuffle_doors aliases for old options * Fixed a unit test * Updated datafile * Tweaked requirements for reduced locations * Added player name to OptionError messages * Update generated.dat
This commit is contained in:

committed by
GitHub

parent
d030a698a6
commit
cc22161644
@@ -7,8 +7,8 @@ from .items import ALL_ITEM_TABLE, ItemType
|
||||
from .locations import ALL_LOCATION_TABLE, LocationClassification
|
||||
from .options import LocationChecks, ShuffleDoors, SunwarpAccess, VictoryCondition
|
||||
from .static_logic import DOORS_BY_ROOM, PAINTINGS, PAINTING_ENTRANCES, PAINTING_EXITS, \
|
||||
PANELS_BY_ROOM, PROGRESSION_BY_ROOM, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS, \
|
||||
SUNWARP_ENTRANCES, SUNWARP_EXITS
|
||||
PANELS_BY_ROOM, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS, PROGRESSIVE_DOORS_BY_ROOM, \
|
||||
PANEL_DOORS_BY_ROOM, PROGRESSIVE_PANELS_BY_ROOM, SUNWARP_ENTRANCES, SUNWARP_EXITS
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import LingoWorld
|
||||
@@ -18,6 +18,8 @@ class AccessRequirements:
|
||||
rooms: Set[str]
|
||||
doors: Set[RoomAndDoor]
|
||||
colors: Set[str]
|
||||
items: Set[str]
|
||||
progression: Dict[str, int]
|
||||
the_master: bool
|
||||
postgame: bool
|
||||
|
||||
@@ -25,6 +27,8 @@ class AccessRequirements:
|
||||
self.rooms = set()
|
||||
self.doors = set()
|
||||
self.colors = set()
|
||||
self.items = set()
|
||||
self.progression = dict()
|
||||
self.the_master = False
|
||||
self.postgame = False
|
||||
|
||||
@@ -32,12 +36,17 @@ class AccessRequirements:
|
||||
self.rooms |= other.rooms
|
||||
self.doors |= other.doors
|
||||
self.colors |= other.colors
|
||||
self.items |= other.items
|
||||
self.the_master |= other.the_master
|
||||
self.postgame |= other.postgame
|
||||
|
||||
for progression, index in other.progression.items():
|
||||
if progression not in self.progression or index > self.progression[progression]:
|
||||
self.progression[progression] = index
|
||||
|
||||
def __str__(self):
|
||||
return f"AccessRequirements(rooms={self.rooms}, doors={self.doors}, colors={self.colors}," \
|
||||
f" the_master={self.the_master}, postgame={self.postgame})"
|
||||
return f"AccessRequirements(rooms={self.rooms}, doors={self.doors}, colors={self.colors}, items={self.items}," \
|
||||
f" progression={self.progression}), the_master={self.the_master}, postgame={self.postgame}"
|
||||
|
||||
|
||||
class PlayerLocation(NamedTuple):
|
||||
@@ -117,15 +126,15 @@ class LingoPlayerLogic:
|
||||
self.item_by_door.setdefault(room, {})[door] = item
|
||||
|
||||
def handle_non_grouped_door(self, room_name: str, door_data: Door, world: "LingoWorld"):
|
||||
if room_name in PROGRESSION_BY_ROOM and door_data.name in PROGRESSION_BY_ROOM[room_name]:
|
||||
progression_name = PROGRESSION_BY_ROOM[room_name][door_data.name].item_name
|
||||
if room_name in PROGRESSIVE_DOORS_BY_ROOM and door_data.name in PROGRESSIVE_DOORS_BY_ROOM[room_name]:
|
||||
progression_name = PROGRESSIVE_DOORS_BY_ROOM[room_name][door_data.name].item_name
|
||||
progression_handling = should_split_progression(progression_name, world)
|
||||
|
||||
if progression_handling == ProgressiveItemBehavior.SPLIT:
|
||||
self.set_door_item(room_name, door_data.name, door_data.item_name)
|
||||
self.real_items.append(door_data.item_name)
|
||||
elif progression_handling == ProgressiveItemBehavior.PROGRESSIVE:
|
||||
progressive_item_name = PROGRESSION_BY_ROOM[room_name][door_data.name].item_name
|
||||
progressive_item_name = PROGRESSIVE_DOORS_BY_ROOM[room_name][door_data.name].item_name
|
||||
self.set_door_item(room_name, door_data.name, progressive_item_name)
|
||||
self.real_items.append(progressive_item_name)
|
||||
else:
|
||||
@@ -156,17 +165,31 @@ class LingoPlayerLogic:
|
||||
victory_condition = world.options.victory_condition
|
||||
early_color_hallways = world.options.early_color_hallways
|
||||
|
||||
if location_checks == LocationChecks.option_reduced and door_shuffle != ShuffleDoors.option_none:
|
||||
raise OptionError("You cannot have reduced location checks when door shuffle is on, because there would not"
|
||||
" be enough locations for all of the door items.")
|
||||
if location_checks == LocationChecks.option_reduced:
|
||||
if door_shuffle == ShuffleDoors.option_doors:
|
||||
raise OptionError(f"Slot \"{world.player_name}\" cannot have reduced location checks when door shuffle"
|
||||
f" is on, because there would not be enough locations for all of the door items.")
|
||||
if door_shuffle == ShuffleDoors.option_panels:
|
||||
if not world.options.group_doors:
|
||||
raise OptionError(f"Slot \"{world.player_name}\" cannot have reduced location checks when ungrouped"
|
||||
f" panels mode door shuffle is on, because there would not be enough locations for"
|
||||
f" all of the panel items.")
|
||||
if color_shuffle:
|
||||
raise OptionError(f"Slot \"{world.player_name}\" cannot have reduced location checks with both"
|
||||
f" panels mode door shuffle and color shuffle because there would not be enough"
|
||||
f" locations for all of the items.")
|
||||
if world.options.sunwarp_access >= SunwarpAccess.option_individual:
|
||||
raise OptionError(f"Slot \"{world.player_name}\" cannot have reduced location checks with both"
|
||||
f" panels mode door shuffle and individual or progressive sunwarp access because"
|
||||
f" there would not be enough locations for all of the items.")
|
||||
|
||||
# Create door items, where needed.
|
||||
door_groups: Set[str] = set()
|
||||
for room_name, room_data in DOORS_BY_ROOM.items():
|
||||
for door_name, door_data in room_data.items():
|
||||
if door_data.skip_item is False and door_data.event is False:
|
||||
if door_data.type == DoorType.NORMAL and door_shuffle != ShuffleDoors.option_none:
|
||||
if door_data.door_group is not None and door_shuffle == ShuffleDoors.option_simple:
|
||||
if door_data.type == DoorType.NORMAL and door_shuffle == ShuffleDoors.option_doors:
|
||||
if door_data.door_group is not None and world.options.group_doors:
|
||||
# Grouped doors are handled differently if shuffle doors is on simple.
|
||||
self.set_door_item(room_name, door_name, door_data.door_group)
|
||||
door_groups.add(door_data.door_group)
|
||||
@@ -188,7 +211,29 @@ class LingoPlayerLogic:
|
||||
self.real_items.append(door_data.item_name)
|
||||
|
||||
self.real_items += door_groups
|
||||
|
||||
|
||||
# Create panel items, where needed.
|
||||
if world.options.shuffle_doors == ShuffleDoors.option_panels:
|
||||
panel_groups: Set[str] = set()
|
||||
|
||||
for room_name, room_data in PANEL_DOORS_BY_ROOM.items():
|
||||
for panel_door_name, panel_door_data in room_data.items():
|
||||
if panel_door_data.panel_group is not None and world.options.group_doors:
|
||||
panel_groups.add(panel_door_data.panel_group)
|
||||
elif room_name in PROGRESSIVE_PANELS_BY_ROOM \
|
||||
and panel_door_name in PROGRESSIVE_PANELS_BY_ROOM[room_name]:
|
||||
progression_obj = PROGRESSIVE_PANELS_BY_ROOM[room_name][panel_door_name]
|
||||
progression_handling = should_split_progression(progression_obj.item_name, world)
|
||||
|
||||
if progression_handling == ProgressiveItemBehavior.SPLIT:
|
||||
self.real_items.append(panel_door_data.item_name)
|
||||
elif progression_handling == ProgressiveItemBehavior.PROGRESSIVE:
|
||||
self.real_items.append(progression_obj.item_name)
|
||||
else:
|
||||
self.real_items.append(panel_door_data.item_name)
|
||||
|
||||
self.real_items += panel_groups
|
||||
|
||||
# Create color items, if needed.
|
||||
if color_shuffle:
|
||||
self.real_items += [name for name, item in ALL_ITEM_TABLE.items() if item.type == ItemType.COLOR]
|
||||
@@ -244,7 +289,7 @@ class LingoPlayerLogic:
|
||||
elif location_checks == LocationChecks.option_insanity:
|
||||
location_classification = LocationClassification.insanity
|
||||
|
||||
if door_shuffle != ShuffleDoors.option_none and not early_color_hallways:
|
||||
if door_shuffle == ShuffleDoors.option_doors and not early_color_hallways:
|
||||
location_classification |= LocationClassification.small_sphere_one
|
||||
|
||||
for location_name, location_data in ALL_LOCATION_TABLE.items():
|
||||
@@ -286,7 +331,7 @@ class LingoPlayerLogic:
|
||||
"iterations. This is very unlikely to happen on its own, and probably indicates some "
|
||||
"kind of logic error.")
|
||||
|
||||
if door_shuffle != ShuffleDoors.option_none and location_checks != LocationChecks.option_insanity \
|
||||
if door_shuffle == ShuffleDoors.option_doors and location_checks != LocationChecks.option_insanity \
|
||||
and not early_color_hallways and world.multiworld.players > 1:
|
||||
# Under the combination of door shuffle, normal location checks, and no early color hallways, sphere 1 is
|
||||
# only three checks. In a multiplayer situation, this can be frustrating for the player because they are
|
||||
@@ -301,19 +346,19 @@ class LingoPlayerLogic:
|
||||
# Starting Room - Exit Door gives access to OPEN and TRACE.
|
||||
good_item_options: List[str] = ["Starting Room - Back Right Door", "Second Room - Exit Door"]
|
||||
|
||||
if not color_shuffle and not world.options.enable_pilgrimage:
|
||||
# HOT CRUST and THIS.
|
||||
good_item_options.append("Pilgrim Room - Sun Painting")
|
||||
|
||||
if not color_shuffle:
|
||||
if door_shuffle == ShuffleDoors.option_simple:
|
||||
if not world.options.enable_pilgrimage:
|
||||
# HOT CRUST and THIS.
|
||||
good_item_options.append("Pilgrim Room - Sun Painting")
|
||||
|
||||
if world.options.group_doors:
|
||||
# WELCOME BACK, CLOCKWISE, and DRAWL + RUNS.
|
||||
good_item_options.append("Welcome Back Doors")
|
||||
else:
|
||||
# WELCOME BACK and CLOCKWISE.
|
||||
good_item_options.append("Welcome Back Area - Shortcut to Starting Room")
|
||||
|
||||
if door_shuffle == ShuffleDoors.option_simple:
|
||||
if world.options.group_doors:
|
||||
# Color hallways access (NOTE: reconsider when sunwarp shuffling exists).
|
||||
good_item_options.append("Rhyme Room Doors")
|
||||
|
||||
@@ -359,13 +404,11 @@ class LingoPlayerLogic:
|
||||
def randomize_paintings(self, world: "LingoWorld") -> bool:
|
||||
self.painting_mapping.clear()
|
||||
|
||||
door_shuffle = world.options.shuffle_doors
|
||||
|
||||
# First, assign mappings to the required-exit paintings. We ensure that req-blocked paintings do not lead to
|
||||
# required paintings.
|
||||
req_exits = []
|
||||
required_painting_rooms = REQUIRED_PAINTING_ROOMS
|
||||
if door_shuffle == ShuffleDoors.option_none:
|
||||
if world.options.shuffle_doors != ShuffleDoors.option_doors:
|
||||
required_painting_rooms += REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS
|
||||
req_exits = [painting_id for painting_id, painting in PAINTINGS.items() if painting.required_when_no_doors]
|
||||
|
||||
@@ -432,7 +475,7 @@ class LingoPlayerLogic:
|
||||
for painting_id, painting in PAINTINGS.items():
|
||||
if painting_id not in self.painting_mapping.values() \
|
||||
and (painting.required or (painting.required_when_no_doors and
|
||||
door_shuffle == ShuffleDoors.option_none)):
|
||||
world.options.shuffle_doors != ShuffleDoors.option_doors)):
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -447,12 +490,31 @@ class LingoPlayerLogic:
|
||||
access_reqs = AccessRequirements()
|
||||
panel_object = PANELS_BY_ROOM[room][panel]
|
||||
|
||||
if world.options.shuffle_doors == ShuffleDoors.option_panels and panel_object.panel_door is not None:
|
||||
panel_door_room = panel_object.panel_door.room
|
||||
panel_door_name = panel_object.panel_door.panel_door
|
||||
panel_door = PANEL_DOORS_BY_ROOM[panel_door_room][panel_door_name]
|
||||
|
||||
if panel_door.panel_group is not None and world.options.group_doors:
|
||||
access_reqs.items.add(panel_door.panel_group)
|
||||
elif panel_door_room in PROGRESSIVE_PANELS_BY_ROOM\
|
||||
and panel_door_name in PROGRESSIVE_PANELS_BY_ROOM[panel_door_room]:
|
||||
progression_obj = PROGRESSIVE_PANELS_BY_ROOM[panel_door_room][panel_door_name]
|
||||
progression_handling = should_split_progression(progression_obj.item_name, world)
|
||||
|
||||
if progression_handling == ProgressiveItemBehavior.SPLIT:
|
||||
access_reqs.items.add(panel_door.item_name)
|
||||
elif progression_handling == ProgressiveItemBehavior.PROGRESSIVE:
|
||||
access_reqs.progression[progression_obj.item_name] = progression_obj.index
|
||||
else:
|
||||
access_reqs.items.add(panel_door.item_name)
|
||||
|
||||
for req_room in panel_object.required_rooms:
|
||||
access_reqs.rooms.add(req_room)
|
||||
|
||||
for req_door in panel_object.required_doors:
|
||||
door_object = DOORS_BY_ROOM[room if req_door.room is None else req_door.room][req_door.door]
|
||||
if door_object.event or world.options.shuffle_doors == ShuffleDoors.option_none:
|
||||
if door_object.event or world.options.shuffle_doors != ShuffleDoors.option_doors:
|
||||
sub_access_reqs = self.calculate_door_requirements(
|
||||
room if req_door.room is None else req_door.room, req_door.door, world)
|
||||
access_reqs.merge(sub_access_reqs)
|
||||
@@ -522,11 +584,14 @@ class LingoPlayerLogic:
|
||||
continue
|
||||
|
||||
# We won't coalesce any panels that have requirements beyond colors. To simplify things for now, we will
|
||||
# only coalesce single-color panels. Chains/stacks/combo puzzles will be separate. THE MASTER has
|
||||
# special access rules and is handled separately.
|
||||
# only coalesce single-color panels. Chains/stacks/combo puzzles will be separate. Panel door locked
|
||||
# puzzles will be separate if panels mode is on. THE MASTER has special access rules and is handled
|
||||
# separately.
|
||||
if len(panel_data.required_panels) > 0 or len(panel_data.required_doors) > 0\
|
||||
or len(panel_data.required_rooms) > 0\
|
||||
or (world.options.shuffle_colors and len(panel_data.colors) > 1)\
|
||||
or (world.options.shuffle_doors == ShuffleDoors.option_panels
|
||||
and panel_data.panel_door is not None)\
|
||||
or panel_name == "THE MASTER":
|
||||
self.counting_panel_reqs.setdefault(room_name, []).append(
|
||||
(self.calculate_panel_requirements(room_name, panel_name, world), 1))
|
||||
|
Reference in New Issue
Block a user