about summary refs log tree commit diff stats
path: root/apworld/player_logic.py
diff options
context:
space:
mode:
Diffstat (limited to 'apworld/player_logic.py')
-rw-r--r--apworld/player_logic.py43
1 files changed, 39 insertions, 4 deletions
diff --git a/apworld/player_logic.py b/apworld/player_logic.py index 42b36e6..d435bbc 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py
@@ -30,6 +30,11 @@ class AccessRequirements:
30 # This is an AND of ORs. 30 # This is an AND of ORs.
31 or_logic: list[list["AccessRequirements"]] 31 or_logic: list[list["AccessRequirements"]]
32 32
33 # When complete_at is set, at least that many of the requirements in possibilities must be accessible. This should
34 # only be used for doors with complete_at > 1, as or_logic is more efficient for complete_at == 1.
35 complete_at: int | None
36 possibilities: list["AccessRequirements"]
37
33 def __init__(self): 38 def __init__(self):
34 self.items = set() 39 self.items = set()
35 self.progressives = dict() 40 self.progressives = dict()
@@ -37,6 +42,8 @@ class AccessRequirements:
37 self.letters = dict() 42 self.letters = dict()
38 self.cyans = False 43 self.cyans = False
39 self.or_logic = list() 44 self.or_logic = list()
45 self.complete_at = None
46 self.possibilities = list()
40 47
41 def merge(self, other: "AccessRequirements"): 48 def merge(self, other: "AccessRequirements"):
42 for item in other.items: 49 for item in other.items:
@@ -56,9 +63,32 @@ class AccessRequirements:
56 for disjunction in other.or_logic: 63 for disjunction in other.or_logic:
57 self.or_logic.append(disjunction) 64 self.or_logic.append(disjunction)
58 65
66 if other.complete_at is not None:
67 # Merging multiple requirements that use complete_at sucks, and is part of why we want to minimize use of
68 # it. If both requirements use complete_at, we will cheat by using the or_logic field, which supports
69 # conjunctions of requirements.
70 if self.complete_at is not None:
71 print("Merging requirements with complete_at > 1. This is messy and should be avoided!")
72
73 left_req = AccessRequirements()
74 left_req.complete_at = self.complete_at
75 left_req.possibilities = self.possibilities
76 self.or_logic.append([left_req])
77
78 self.complete_at = None
79 self.possibilities = list()
80
81 right_req = AccessRequirements()
82 right_req.complete_at = other.complete_at
83 right_req.possibilities = other.possibilities
84 self.or_logic.append([right_req])
85 else:
86 self.complete_at = other.complete_at
87 self.possibilities = other.possibilities
88
59 def is_empty(self) -> bool: 89 def is_empty(self) -> bool:
60 return (len(self.items) == 0 and len(self.progressives) == 0 and len(self.rooms) == 0 and len(self.letters) == 0 90 return (len(self.items) == 0 and len(self.progressives) == 0 and len(self.rooms) == 0 and len(self.letters) == 0
61 and not self.cyans and len(self.or_logic) == 0) 91 and not self.cyans and len(self.or_logic) == 0 and self.complete_at is not None)
62 92
63 def __repr__(self): 93 def __repr__(self):
64 parts = [] 94 parts = []
@@ -74,6 +104,10 @@ class AccessRequirements:
74 parts.append(f"cyans=True") 104 parts.append(f"cyans=True")
75 if len(self.or_logic) > 0: 105 if len(self.or_logic) > 0:
76 parts.append(f"or_logic={self.or_logic}") 106 parts.append(f"or_logic={self.or_logic}")
107 if self.complete_at is not None:
108 parts.append(f"complete_at={self.complete_at}")
109 if len(self.possibilities) > 0:
110 parts.append(f"possibilities={self.possibilities}")
77 return f"AccessRequirements({", ".join(parts)})" 111 return f"AccessRequirements({", ".join(parts)})"
78 112
79 113
@@ -306,7 +340,6 @@ class Lingo2PlayerLogic:
306 door = self.world.static_logic.objects.doors[door_id] 340 door = self.world.static_logic.objects.doors[door_id]
307 reqs = AccessRequirements() 341 reqs = AccessRequirements()
308 342
309 # TODO: lavender_cubes, endings
310 if not door.HasField("complete_at") or door.complete_at == 0: 343 if not door.HasField("complete_at") or door.complete_at == 0:
311 for proxy in door.panels: 344 for proxy in door.panels:
312 panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) 345 panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None)
@@ -323,8 +356,10 @@ class Lingo2PlayerLogic:
323 if len(disjunction) > 0: 356 if len(disjunction) > 0:
324 reqs.or_logic.append(disjunction) 357 reqs.or_logic.append(disjunction)
325 else: 358 else:
326 # TODO: Handle complete_at > 1 359 reqs.complete_at = door.complete_at
327 pass 360 for proxy in door.panels:
361 panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None)
362 reqs.possibilities.append(panel_reqs)
328 363
329 if door.HasField("control_center_color"): 364 if door.HasField("control_center_color"):
330 # TODO: Logic for ensuring two CC states aren't needed at once. 365 # TODO: Logic for ensuring two CC states aren't needed at once.