summary refs log tree commit diff stats
path: root/player_logic.py
diff options
context:
space:
mode:
Diffstat (limited to 'player_logic.py')
-rw-r--r--player_logic.py106
1 files changed, 60 insertions, 46 deletions
diff --git a/player_logic.py b/player_logic.py index 966f5a1..96e9869 100644 --- a/player_logic.py +++ b/player_logic.py
@@ -1,12 +1,13 @@
1from enum import Enum 1from enum import Enum
2from typing import Dict, List, NamedTuple, Optional, Set, Tuple, TYPE_CHECKING 2from typing import Dict, List, NamedTuple, Optional, Set, Tuple, TYPE_CHECKING
3 3
4from .datatypes import Door, RoomAndDoor, RoomAndPanel 4from .datatypes import Door, DoorType, RoomAndDoor, RoomAndPanel
5from .items import ALL_ITEM_TABLE, ItemData 5from .items import ALL_ITEM_TABLE, ItemType
6from .locations import ALL_LOCATION_TABLE, LocationClassification 6from .locations import ALL_LOCATION_TABLE, LocationClassification
7from .options import LocationChecks, ShuffleDoors, VictoryCondition 7from .options import LocationChecks, ShuffleDoors, SunwarpAccess, VictoryCondition
8from .static_logic import DOORS_BY_ROOM, PAINTINGS, PAINTING_ENTRANCES, PAINTING_EXITS, \ 8from .static_logic import DOORS_BY_ROOM, PAINTINGS, PAINTING_ENTRANCES, PAINTING_EXITS, \
9 PANELS_BY_ROOM, PROGRESSION_BY_ROOM, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS 9 PANELS_BY_ROOM, PROGRESSION_BY_ROOM, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS, \
10 SUNWARP_ENTRANCES, SUNWARP_EXITS
10 11
11if TYPE_CHECKING: 12if TYPE_CHECKING:
12 from . import LingoWorld 13 from . import LingoWorld
@@ -58,21 +59,6 @@ def should_split_progression(progression_name: str, world: "LingoWorld") -> Prog
58 return ProgressiveItemBehavior.PROGRESSIVE 59 return ProgressiveItemBehavior.PROGRESSIVE
59 60
60 61
61def should_include_item(item: ItemData, world: "LingoWorld") -> bool:
62 if item.mode == "colors":
63 return world.options.shuffle_colors > 0
64 elif item.mode == "doors":
65 return world.options.shuffle_doors != ShuffleDoors.option_none
66 elif item.mode == "complex door":
67 return world.options.shuffle_doors == ShuffleDoors.option_complex
68 elif item.mode == "door group":
69 return world.options.shuffle_doors == ShuffleDoors.option_simple
70 elif item.mode == "special":
71 return False
72 else:
73 return True
74
75
76class LingoPlayerLogic: 62class LingoPlayerLogic:
77 """ 63 """
78 Defines logic after a player's options have been applied 64 Defines logic after a player's options have been applied
@@ -99,6 +85,10 @@ class LingoPlayerLogic:
99 mastery_reqs: List[AccessRequirements] 85 mastery_reqs: List[AccessRequirements]
100 counting_panel_reqs: Dict[str, List[Tuple[AccessRequirements, int]]] 86 counting_panel_reqs: Dict[str, List[Tuple[AccessRequirements, int]]]
101 87
88 sunwarp_mapping: List[int]
89 sunwarp_entrances: List[str]
90 sunwarp_exits: List[str]
91
102 def add_location(self, room: str, name: str, code: Optional[int], panels: List[RoomAndPanel], world: "LingoWorld"): 92 def add_location(self, room: str, name: str, code: Optional[int], panels: List[RoomAndPanel], world: "LingoWorld"):
103 """ 93 """
104 Creates a location. This function determines the access requirements for the location by combining and 94 Creates a location. This function determines the access requirements for the location by combining and
@@ -132,6 +122,7 @@ class LingoPlayerLogic:
132 self.real_items.append(progressive_item_name) 122 self.real_items.append(progressive_item_name)
133 else: 123 else:
134 self.set_door_item(room_name, door_data.name, door_data.item_name) 124 self.set_door_item(room_name, door_data.name, door_data.item_name)
125 self.real_items.append(door_data.item_name)
135 126
136 def __init__(self, world: "LingoWorld"): 127 def __init__(self, world: "LingoWorld"):
137 self.item_by_door = {} 128 self.item_by_door = {}
@@ -148,6 +139,7 @@ class LingoPlayerLogic:
148 self.door_reqs = {} 139 self.door_reqs = {}
149 self.mastery_reqs = [] 140 self.mastery_reqs = []
150 self.counting_panel_reqs = {} 141 self.counting_panel_reqs = {}
142 self.sunwarp_mapping = []
151 143
152 door_shuffle = world.options.shuffle_doors 144 door_shuffle = world.options.shuffle_doors
153 color_shuffle = world.options.shuffle_colors 145 color_shuffle = world.options.shuffle_colors
@@ -161,15 +153,37 @@ class LingoPlayerLogic:
161 "be enough locations for all of the door items.") 153 "be enough locations for all of the door items.")
162 154
163 # Create door items, where needed. 155 # Create door items, where needed.
164 if door_shuffle != ShuffleDoors.option_none: 156 door_groups: Set[str] = set()
165 for room_name, room_data in DOORS_BY_ROOM.items(): 157 for room_name, room_data in DOORS_BY_ROOM.items():
166 for door_name, door_data in room_data.items(): 158 for door_name, door_data in room_data.items():
167 if door_data.skip_item is False and door_data.event is False: 159 if door_data.skip_item is False and door_data.event is False:
160 if door_data.type == DoorType.NORMAL and door_shuffle != ShuffleDoors.option_none:
168 if door_data.door_group is not None and door_shuffle == ShuffleDoors.option_simple: 161 if door_data.door_group is not None and door_shuffle == ShuffleDoors.option_simple:
169 # Grouped doors are handled differently if shuffle doors is on simple. 162 # Grouped doors are handled differently if shuffle doors is on simple.
170 self.set_door_item(room_name, door_name, door_data.door_group) 163 self.set_door_item(room_name, door_name, door_data.door_group)
164 door_groups.add(door_data.door_group)
171 else: 165 else:
172 self.handle_non_grouped_door(room_name, door_data, world) 166 self.handle_non_grouped_door(room_name, door_data, world)
167 elif door_data.type == DoorType.SUNWARP:
168 if world.options.sunwarp_access == SunwarpAccess.option_unlock:
169 self.set_door_item(room_name, door_name, "Sunwarps")
170 door_groups.add("Sunwarps")
171 elif world.options.sunwarp_access == SunwarpAccess.option_individual:
172 self.set_door_item(room_name, door_name, door_data.item_name)
173 self.real_items.append(door_data.item_name)
174 elif world.options.sunwarp_access == SunwarpAccess.option_progressive:
175 self.set_door_item(room_name, door_name, "Progressive Pilgrimage")
176 self.real_items.append("Progressive Pilgrimage")
177 elif door_data.type == DoorType.SUN_PAINTING:
178 if not world.options.enable_pilgrimage:
179 self.set_door_item(room_name, door_name, door_data.item_name)
180 self.real_items.append(door_data.item_name)
181
182 self.real_items += door_groups
183
184 # Create color items, if needed.
185 if color_shuffle:
186 self.real_items += [name for name, item in ALL_ITEM_TABLE.items() if item.type == ItemType.COLOR]
173 187
174 # Create events for each achievement panel, so that we can determine when THE MASTER is accessible. 188 # Create events for each achievement panel, so that we can determine when THE MASTER is accessible.
175 for room_name, room_data in PANELS_BY_ROOM.items(): 189 for room_name, room_data in PANELS_BY_ROOM.items():
@@ -206,6 +220,11 @@ class LingoPlayerLogic:
206 220
207 if world.options.level_2_requirement == 1: 221 if world.options.level_2_requirement == 1:
208 raise Exception("The Level 2 requirement must be at least 2 when LEVEL 2 is the victory condition.") 222 raise Exception("The Level 2 requirement must be at least 2 when LEVEL 2 is the victory condition.")
223 elif victory_condition == VictoryCondition.option_pilgrimage:
224 self.victory_condition = "Pilgrim Antechamber - PILGRIM"
225 self.add_location("Pilgrim Antechamber", "PILGRIM (Solved)", None,
226 [RoomAndPanel("Pilgrim Antechamber", "PILGRIM")], world)
227 self.event_loc_to_item["PILGRIM (Solved)"] = "Victory"
209 228
210 # Create groups of counting panel access requirements for the LEVEL 2 check. 229 # Create groups of counting panel access requirements for the LEVEL 2 check.
211 self.create_panel_hunt_events(world) 230 self.create_panel_hunt_events(world)
@@ -225,28 +244,22 @@ class LingoPlayerLogic:
225 self.add_location(location_data.room, location_name, location_data.code, location_data.panels, world) 244 self.add_location(location_data.room, location_name, location_data.code, location_data.panels, world)
226 self.real_locations.append(location_name) 245 self.real_locations.append(location_name)
227 246
228 # Instantiate all real items. 247 if world.options.enable_pilgrimage and world.options.sunwarp_access == SunwarpAccess.option_disabled:
229 for name, item in ALL_ITEM_TABLE.items(): 248 raise Exception("Sunwarps cannot be disabled when pilgrimage is enabled.")
230 if should_include_item(item, world): 249
231 self.real_items.append(name) 250 if world.options.shuffle_sunwarps:
232 251 if world.options.sunwarp_access == SunwarpAccess.option_disabled:
233 # Calculate the requirements for the fake pilgrimage. 252 raise Exception("Sunwarps cannot be shuffled if they are disabled.")
234 fake_pilgrimage = [ 253
235 ["Second Room", "Exit Door"], ["Crossroads", "Tower Entrance"], 254 self.sunwarp_mapping = list(range(0, 12))
236 ["Orange Tower Fourth Floor", "Hot Crusts Door"], ["Outside The Initiated", "Shortcut to Hub Room"], 255 world.random.shuffle(self.sunwarp_mapping)
237 ["Orange Tower First Floor", "Shortcut to Hub Room"], ["Directional Gallery", "Shortcut to The Undeterred"], 256
238 ["Orange Tower First Floor", "Salt Pepper Door"], ["Hub Room", "Crossroads Entrance"], 257 sunwarp_rooms = SUNWARP_ENTRANCES + SUNWARP_EXITS
239 ["Color Hunt", "Shortcut to The Steady"], ["The Bearer", "Entrance"], ["Art Gallery", "Exit"], 258 self.sunwarp_entrances = [sunwarp_rooms[i] for i in self.sunwarp_mapping[0:6]]
240 ["The Tenacious", "Shortcut to Hub Room"], ["Outside The Agreeable", "Tenacious Entrance"] 259 self.sunwarp_exits = [sunwarp_rooms[i] for i in self.sunwarp_mapping[6:12]]
241 ] 260 else:
242 pilgrimage_reqs = AccessRequirements() 261 self.sunwarp_entrances = SUNWARP_ENTRANCES
243 for door in fake_pilgrimage: 262 self.sunwarp_exits = SUNWARP_EXITS
244 door_object = DOORS_BY_ROOM[door[0]][door[1]]
245 if door_object.event or world.options.shuffle_doors == ShuffleDoors.option_none:
246 pilgrimage_reqs.merge(self.calculate_door_requirements(door[0], door[1], world))
247 else:
248 pilgrimage_reqs.doors.add(RoomAndDoor(door[0], door[1]))
249 self.door_reqs.setdefault("Pilgrim Antechamber", {})["Pilgrimage"] = pilgrimage_reqs
250 263
251 # Create the paintings mapping, if painting shuffle is on. 264 # Create the paintings mapping, if painting shuffle is on.
252 if painting_shuffle: 265 if painting_shuffle:
@@ -277,10 +290,11 @@ class LingoPlayerLogic:
277 # Starting Room - Exit Door gives access to OPEN and TRACE. 290 # Starting Room - Exit Door gives access to OPEN and TRACE.
278 good_item_options: List[str] = ["Starting Room - Back Right Door", "Second Room - Exit Door"] 291 good_item_options: List[str] = ["Starting Room - Back Right Door", "Second Room - Exit Door"]
279 292
280 if not color_shuffle: 293 if not color_shuffle and not world.options.enable_pilgrimage:
281 # HOT CRUST and THIS. 294 # HOT CRUST and THIS.
282 good_item_options.append("Pilgrim Room - Sun Painting") 295 good_item_options.append("Pilgrim Room - Sun Painting")
283 296
297 if not color_shuffle:
284 if door_shuffle == ShuffleDoors.option_simple: 298 if door_shuffle == ShuffleDoors.option_simple:
285 # WELCOME BACK, CLOCKWISE, and DRAWL + RUNS. 299 # WELCOME BACK, CLOCKWISE, and DRAWL + RUNS.
286 good_item_options.append("Welcome Back Doors") 300 good_item_options.append("Welcome Back Doors")