diff options
author | Star Rauchenberger <fefferburbia@gmail.com> | 2025-08-27 17:14:57 -0400 |
---|---|---|
committer | Star Rauchenberger <fefferburbia@gmail.com> | 2025-08-27 17:14:57 -0400 |
commit | 4ae885f392261b939e6f21b47d545f609c6e1965 (patch) | |
tree | 7c6c4266db9a1aff3718f21d097974d0e4625a2e /apworld | |
parent | b548329f740e7f548864e62e4998b59b2d76f86a (diff) | |
download | lingo2-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__.py | 5 | ||||
-rw-r--r-- | apworld/options.py | 21 | ||||
-rw-r--r-- | apworld/player_logic.py | 60 | ||||
-rw-r--r-- | apworld/regions.py | 18 |
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 @@ | |||
1 | from dataclasses import dataclass | 1 | from dataclasses import dataclass |
2 | 2 | ||
3 | from Options import PerGameCommonOptions, Toggle | 3 | from Options import PerGameCommonOptions, Toggle, Choice |
4 | 4 | ||
5 | 5 | ||
6 | class ShuffleDoors(Toggle): | 6 | class ShuffleDoors(Toggle): |
@@ -8,6 +8,25 @@ class ShuffleDoors(Toggle): | |||
8 | display_name = "Shuffle Doors" | 8 | display_name = "Shuffle Doors" |
9 | 9 | ||
10 | 10 | ||
11 | class 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 |
12 | class Lingo2Options(PerGameCommonOptions): | 30 | class 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 @@ | |||
1 | from typing import TYPE_CHECKING | 1 | from typing import TYPE_CHECKING |
2 | 2 | ||
3 | from BaseClasses import Region | 3 | from BaseClasses import Region, ItemClassification, Entrance |
4 | from .items import Lingo2Item | ||
4 | from .locations import Lingo2Location | 5 | from .locations import Lingo2Location |
5 | from .player_logic import AccessRequirements | 6 | from .player_logic import AccessRequirements |
6 | from .rules import make_location_lambda | 7 | from .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() |