summary refs log tree commit diff stats
path: root/apworld
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2025-08-27 17:14:57 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2025-08-27 17:14:57 -0400
commit4ae885f392261b939e6f21b47d545f609c6e1965 (patch)
tree7c6c4266db9a1aff3718f21d097974d0e4625a2e /apworld
parentb548329f740e7f548864e62e4998b59b2d76f86a (diff)
downloadlingo2-archipelago-4ae885f392261b939e6f21b47d545f609c6e1965.tar.gz
lingo2-archipelago-4ae885f392261b939e6f21b47d545f609c6e1965.tar.bz2
lingo2-archipelago-4ae885f392261b939e6f21b47d545f609c6e1965.zip
Set apworld victory condition
Diffstat (limited to 'apworld')
-rw-r--r--apworld/__init__.py5
-rw-r--r--apworld/options.py21
-rw-r--r--apworld/player_logic.py60
-rw-r--r--apworld/regions.py18
4 files changed, 76 insertions, 28 deletions
diff --git a/apworld/__init__.py b/apworld/__init__.py index 07e3982..14bb4bc 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py
@@ -24,6 +24,8 @@ class Lingo2World(World):
24 game = "Lingo 2" 24 game = "Lingo 2"
25 web = Lingo2WebWorld() 25 web = Lingo2WebWorld()
26 26
27 topology_present = True
28
27 options_dataclass = Lingo2Options 29 options_dataclass = Lingo2Options
28 options: Lingo2Options 30 options: Lingo2Options
29 31
@@ -57,3 +59,6 @@ class Lingo2World(World):
57 def create_item(self, name: str) -> Item: 59 def create_item(self, name: str) -> Item:
58 return Lingo2Item(name, ItemClassification.filler if name == "Nothing" else ItemClassification.progression, 60 return Lingo2Item(name, ItemClassification.filler if name == "Nothing" else ItemClassification.progression,
59 self.item_name_to_id.get(name), self.player) 61 self.item_name_to_id.get(name), self.player)
62
63 def set_rules(self):
64 self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
diff --git a/apworld/options.py b/apworld/options.py index 77f0ae3..d984beb 100644 --- a/apworld/options.py +++ b/apworld/options.py
@@ -1,6 +1,6 @@
1from dataclasses import dataclass 1from dataclasses import dataclass
2 2
3from Options import PerGameCommonOptions, Toggle 3from Options import PerGameCommonOptions, Toggle, Choice
4 4
5 5
6class ShuffleDoors(Toggle): 6class ShuffleDoors(Toggle):
@@ -8,6 +8,25 @@ class ShuffleDoors(Toggle):
8 display_name = "Shuffle Doors" 8 display_name = "Shuffle Doors"
9 9
10 10
11class VictoryCondition(Choice):
12 """Victory condition."""
13 display_name = "Victory Condition"
14 option_gray_ending = 0
15 option_purple_ending = 1
16 option_mint_ending = 2
17 option_black_ending = 3
18 option_blue_ending = 4
19 option_cyan_ending = 5
20 option_red_ending = 6
21 option_plum_ending = 7
22 option_orange_ending = 8
23 option_gold_ending = 9
24 option_yellow_ending = 10
25 option_green_ending = 11
26 option_white_ending = 12
27
28
11@dataclass 29@dataclass
12class Lingo2Options(PerGameCommonOptions): 30class Lingo2Options(PerGameCommonOptions):
13 shuffle_doors: ShuffleDoors 31 shuffle_doors: ShuffleDoors
32 victory_condition: VictoryCondition
diff --git a/apworld/player_logic.py b/apworld/player_logic.py index edf8c4f..83c16f2 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py
@@ -63,6 +63,8 @@ class Lingo2PlayerLogic:
63 world: "Lingo2World" 63 world: "Lingo2World"
64 64
65 locations_by_room: dict[int, list[PlayerLocation]] 65 locations_by_room: dict[int, list[PlayerLocation]]
66 event_loc_item_by_room: dict[int, dict[str, str]]
67
66 item_by_door: dict[int, str] 68 item_by_door: dict[int, str]
67 69
68 panel_reqs: dict[int, AccessRequirements] 70 panel_reqs: dict[int, AccessRequirements]
@@ -74,6 +76,7 @@ class Lingo2PlayerLogic:
74 def __init__(self, world: "Lingo2World"): 76 def __init__(self, world: "Lingo2World"):
75 self.world = world 77 self.world = world
76 self.locations_by_room = {} 78 self.locations_by_room = {}
79 self.event_loc_item_by_room = {}
77 self.item_by_door = {} 80 self.item_by_door = {}
78 self.panel_reqs = dict() 81 self.panel_reqs = dict()
79 self.proxy_reqs = dict() 82 self.proxy_reqs = dict()
@@ -105,6 +108,14 @@ class Lingo2PlayerLogic:
105 self.locations_by_room.setdefault(ending.room_id, []).append(PlayerLocation(ending.ap_id, 108 self.locations_by_room.setdefault(ending.room_id, []).append(PlayerLocation(ending.ap_id,
106 AccessRequirements())) 109 AccessRequirements()))
107 110
111 event_name = f"{ending.name.capitalize()} Ending (Achieved)"
112 item_name = event_name
113
114 if world.options.victory_condition.current_key.removesuffix("_ending").upper() == ending.name:
115 item_name = "Victory"
116
117 self.event_loc_item_by_room.setdefault(ending.room_id, {})[event_name] = item_name
118
108 def get_panel_reqs(self, panel_id: int, answer: str | None) -> AccessRequirements: 119 def get_panel_reqs(self, panel_id: int, answer: str | None) -> AccessRequirements:
109 if answer is None: 120 if answer is None:
110 if panel_id not in self.panel_reqs: 121 if panel_id not in self.panel_reqs:
@@ -131,6 +142,12 @@ class Lingo2PlayerLogic:
131 proxy_reqs.add_solution(proxy.answer) 142 proxy_reqs.add_solution(proxy.answer)
132 143
133 reqs.or_logic.append([proxy_reqs]) 144 reqs.or_logic.append([proxy_reqs])
145
146 if not any(proxy.answer == panel.answer for proxy in panel.proxies):
147 proxy_reqs = AccessRequirements()
148 proxy_reqs.add_solution(panel.answer)
149
150 reqs.or_logic.append([proxy_reqs])
134 else: 151 else:
135 reqs.add_solution(panel.answer) 152 reqs.add_solution(panel.answer)
136 153
@@ -158,32 +175,25 @@ class Lingo2PlayerLogic:
158 door = self.world.static_logic.objects.doors[door_id] 175 door = self.world.static_logic.objects.doors[door_id]
159 reqs = AccessRequirements() 176 reqs = AccessRequirements()
160 177
161 use_item = False 178 # TODO: complete_at, control_center_color, switches, keyholders
162 if door.type in [data_pb2.DoorType.STANDARD, data_pb2.DoorType.ITEM_ONLY] and self.world.options.shuffle_doors: 179 if not door.HasField("complete_at") or door.complete_at == 0:
163 use_item = True 180 for proxy in door.panels:
164 181 panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None)
165 if use_item: 182 reqs.merge(panel_reqs)
166 reqs.items.add(self.world.static_logic.get_door_item_name(door.id)) 183 elif door.complete_at == 1:
184 reqs.or_logic.append([self.get_panel_reqs(proxy.panel,
185 proxy.answer if proxy.HasField("answer") else None)
186 for proxy in door.panels])
167 else: 187 else:
168 # TODO: complete_at, control_center_color, switches, keyholders 188 # TODO: Handle complete_at > 1
169 if not door.HasField("complete_at") or door.complete_at == 0: 189 pass
170 for proxy in door.panels: 190
171 panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) 191 for room in door.rooms:
172 reqs.merge(panel_reqs) 192 reqs.rooms.add(self.world.static_logic.get_room_region_name(room))
173 elif door.complete_at == 1: 193
174 reqs.or_logic.append([self.get_panel_reqs(proxy.panel, 194 for sub_door_id in door.doors:
175 proxy.answer if proxy.HasField("answer") else None) 195 sub_reqs = self.get_door_open_reqs(sub_door_id)
176 for proxy in door.panels]) 196 reqs.merge(sub_reqs)
177 else:
178 # TODO: Handle complete_at > 1
179 pass
180
181 for room in door.rooms:
182 reqs.rooms.add(self.world.static_logic.get_room_region_name(room))
183
184 for sub_door_id in door.doors:
185 sub_reqs = self.get_door_open_reqs(sub_door_id)
186 reqs.merge(sub_reqs)
187 197
188 return reqs 198 return reqs
189 199
diff --git a/apworld/regions.py b/apworld/regions.py index 1c8e672..fe2c99b 100644 --- a/apworld/regions.py +++ b/apworld/regions.py
@@ -1,6 +1,7 @@
1from typing import TYPE_CHECKING 1from typing import TYPE_CHECKING
2 2
3from BaseClasses import Region 3from BaseClasses import Region, ItemClassification, Entrance
4from .items import Lingo2Item
4from .locations import Lingo2Location 5from .locations import Lingo2Location
5from .player_logic import AccessRequirements 6from .player_logic import AccessRequirements
6from .rules import make_location_lambda 7from .rules import make_location_lambda
@@ -18,6 +19,12 @@ def create_region(room, world: "Lingo2World") -> Region:
18 new_location.access_rule = make_location_lambda(location.reqs, world) 19 new_location.access_rule = make_location_lambda(location.reqs, world)
19 new_region.locations.append(new_location) 20 new_region.locations.append(new_location)
20 21
22 for event_name, item_name in world.player_logic.event_loc_item_by_room.get(room.id, {}).items():
23 new_location = Lingo2Location(world.player, event_name, None, new_region)
24 event_item = Lingo2Item(item_name, ItemClassification.progression, None, world.player)
25 new_location.place_locked_item(event_item)
26 new_region.locations.append(new_location)
27
21 return new_region 28 return new_region
22 29
23 30
@@ -73,6 +80,13 @@ def create_regions(world: "Lingo2World"):
73 connection_name = f"{connection_name} (via panel {panel.name})" 80 connection_name = f"{connection_name} (via panel {panel.name})"
74 81
75 if from_region in regions and to_region in regions: 82 if from_region in regions and to_region in regions:
76 regions[from_region].connect(regions[to_region], connection_name, make_location_lambda(reqs, world)) 83 connection = Entrance(world.player, connection_name, regions[from_region])
84 connection.access_rule = make_location_lambda(reqs, world)
85
86 regions[from_region].exits.append(connection)
87 connection.connect(regions[to_region])
88
89 for region in reqs.rooms:
90 world.multiworld.register_indirect_condition(regions[region], connection)
77 91
78 world.multiworld.regions += regions.values() 92 world.multiworld.regions += regions.values()