about summary refs log tree commit diff stats
path: root/apworld/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'apworld/__init__.py')
-rw-r--r--apworld/__init__.py119
1 files changed, 114 insertions, 5 deletions
diff --git a/apworld/__init__.py b/apworld/__init__.py index 07e3982..96f6804 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py
@@ -1,18 +1,38 @@
1""" 1"""
2Archipelago init file for Lingo 2 2Archipelago init file for Lingo 2
3""" 3"""
4from BaseClasses import ItemClassification, Item 4from typing import ClassVar
5
6from BaseClasses import ItemClassification, Item, Tutorial
7from settings import Group, UserFilePath
5from worlds.AutoWorld import WebWorld, World 8from worlds.AutoWorld import WebWorld, World
6from .items import Lingo2Item 9from .items import Lingo2Item, ANTI_COLLECTABLE_TRAPS
7from .options import Lingo2Options 10from .options import Lingo2Options
8from .player_logic import Lingo2PlayerLogic 11from .player_logic import Lingo2PlayerLogic
9from .regions import create_regions 12from .regions import create_regions, shuffle_entrances, connect_ports_from_ut
10from .static_logic import Lingo2StaticLogic 13from .static_logic import Lingo2StaticLogic
14from ..LauncherComponents import Component, Type, components, launch as launch_component, icon_paths
11 15
12 16
13class Lingo2WebWorld(WebWorld): 17class Lingo2WebWorld(WebWorld):
14 rich_text_options_doc = True 18 rich_text_options_doc = True
15 theme = "grass" 19 theme = "grass"
20 tutorials = [Tutorial(
21 "Multiworld Setup Guide",
22 "A guide to playing Lingo 2 with Archipelago.",
23 "English",
24 "en_Lingo_2.md",
25 "setup/en",
26 ["hatkirby"]
27 )]
28
29
30class Lingo2Settings(Group):
31 class ExecutableFile(UserFilePath):
32 """Path to the Lingo 2 executable"""
33 is_exe = True
34
35 exe_file: ExecutableFile = ExecutableFile()
16 36
17 37
18class Lingo2World(World): 38class Lingo2World(World):
@@ -24,21 +44,43 @@ class Lingo2World(World):
24 game = "Lingo 2" 44 game = "Lingo 2"
25 web = Lingo2WebWorld() 45 web = Lingo2WebWorld()
26 46
47 settings: ClassVar[Lingo2Settings]
48 settings_key = "lingo2_options"
49
50 topology_present = True
51
27 options_dataclass = Lingo2Options 52 options_dataclass = Lingo2Options
28 options: Lingo2Options 53 options: Lingo2Options
29 54
30 static_logic = Lingo2StaticLogic() 55 static_logic = Lingo2StaticLogic()
31 item_name_to_id = static_logic.item_name_to_id 56 item_name_to_id = static_logic.item_name_to_id
32 location_name_to_id = static_logic.location_name_to_id 57 location_name_to_id = static_logic.location_name_to_id
58 item_name_groups = static_logic.item_name_groups
59 location_name_groups = static_logic.location_name_groups
60
61 for_tracker: ClassVar[bool] = False
33 62
34 player_logic: Lingo2PlayerLogic 63 player_logic: Lingo2PlayerLogic
35 64
65 port_pairings: dict[int, int]
66
36 def generate_early(self): 67 def generate_early(self):
37 self.player_logic = Lingo2PlayerLogic(self) 68 self.player_logic = Lingo2PlayerLogic(self)
69 self.port_pairings = {}
38 70
39 def create_regions(self): 71 def create_regions(self):
40 create_regions(self) 72 create_regions(self)
41 73
74 def connect_entrances(self):
75 if self.options.shuffle_worldports:
76 if hasattr(self.multiworld, "re_gen_passthrough") and "Lingo 2" in self.multiworld.re_gen_passthrough:
77 slot_value = self.multiworld.re_gen_passthrough["Lingo 2"]["port_pairings"]
78 self.port_pairings = {int(fp): int(tp) for fp, tp in slot_value.items()}
79
80 connect_ports_from_ut(self.port_pairings, self)
81 else:
82 shuffle_entrances(self)
83
42 from Utils import visualize_regions 84 from Utils import visualize_regions
43 85
44 visualize_regions(self.multiworld.get_region("Menu", self.player), "my_world.puml") 86 visualize_regions(self.multiworld.get_region("Menu", self.player), "my_world.puml")
@@ -49,11 +91,78 @@ class Lingo2World(World):
49 total_locations = sum(len(locs) for locs in self.player_logic.locations_by_room.values()) 91 total_locations = sum(len(locs) for locs in self.player_logic.locations_by_room.values())
50 92
51 item_difference = total_locations - len(pool) 93 item_difference = total_locations - len(pool)
94
95 if self.options.trap_percentage > 0:
96 num_traps = int(item_difference * self.options.trap_percentage / 100)
97 item_difference = item_difference - num_traps
98
99 trap_names = []
100 trap_weights = []
101 for letter_name, weight in self.static_logic.letter_weights.items():
102 trap_names.append(f"Anti {letter_name}")
103 trap_weights.append(weight)
104
105 bad_letters = self.random.choices(trap_names, weights=trap_weights, k=num_traps)
106 pool += [self.create_item(trap_name) for trap_name in bad_letters]
107
52 for i in range(0, item_difference): 108 for i in range(0, item_difference):
53 pool.append(self.create_item("Nothing")) 109 pool.append(self.create_item(self.get_filler_item_name()))
54 110
55 self.multiworld.itempool += pool 111 self.multiworld.itempool += pool
56 112
57 def create_item(self, name: str) -> Item: 113 def create_item(self, name: str) -> Item:
58 return Lingo2Item(name, ItemClassification.filler if name == "Nothing" else ItemClassification.progression, 114 return Lingo2Item(name, ItemClassification.filler if name == self.get_filler_item_name() else
115 ItemClassification.trap if name in ANTI_COLLECTABLE_TRAPS else
116 ItemClassification.progression,
59 self.item_name_to_id.get(name), self.player) 117 self.item_name_to_id.get(name), self.player)
118
119 def set_rules(self):
120 self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
121
122 def fill_slot_data(self):
123 slot_options = [
124 "cyan_door_behavior",
125 "daedalus_roof_access",
126 "keyholder_sanity",
127 "shuffle_control_center_colors",
128 "shuffle_doors",
129 "shuffle_gallery_paintings",
130 "shuffle_letters",
131 "shuffle_symbols",
132 "shuffle_worldports",
133 "strict_cyan_ending",
134 "strict_purple_ending",
135 "victory_condition",
136 ]
137
138 slot_data: dict[str, object] = {
139 **self.options.as_dict(*slot_options),
140 "version": self.static_logic.get_data_version(),
141 }
142
143 if self.options.shuffle_worldports:
144 slot_data["port_pairings"] = self.port_pairings
145
146 return slot_data
147
148 def get_filler_item_name(self) -> str:
149 return "A Job Well Done"
150
151 # for the universal tracker, doesn't get called in standard gen
152 # docs: https://github.com/FarisTheAncient/Archipelago/blob/tracker/worlds/tracker/docs/re-gen-passthrough.md
153 @staticmethod
154 def interpret_slot_data(slot_data: dict[str, object]) -> dict[str, object]:
155 # returning slot_data so it regens, giving it back in multiworld.re_gen_passthrough
156 # we are using re_gen_passthrough over modifying the world here due to complexities with ER
157 return slot_data
158
159
160def launch_client(*args):
161 from .context import client_main
162 launch_component(client_main, name="Lingo2Client", args=args)
163
164
165icon_paths["lingo2_ico"] = f"ap:{__name__}/logo.png"
166component = Component("Lingo 2 Client", component_type=Type.CLIENT, func=launch_client,
167 description="Open Lingo 2.", supports_uri=True, game_name="Lingo 2", icon="lingo2_ico")
168components.append(component)