| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  | """
 | 
					
						
							|  |  |  | Author: Louis M | 
					
						
							|  |  |  | Date: Fri, 15 Mar 2024 18:41:40 +0000 | 
					
						
							|  |  |  | Description: Main module for Aquaria game multiworld randomizer | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from typing import List, Dict, ClassVar, Any | 
					
						
							| 
									
										
										
										
											2024-05-20 02:58:44 -04:00
										 |  |  | from worlds.AutoWorld import World, WebWorld | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  | from BaseClasses import Tutorial, MultiWorld, ItemClassification | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  | from .Items import item_table, AquariaItem, ItemType, ItemGroup, ItemNames | 
					
						
							|  |  |  | from .Locations import location_table, AquariaLocationNames | 
					
						
							|  |  |  | from .Options import (AquariaOptions, IngredientRandomizer, TurtleRandomizer, EarlyBindSong, EarlyEnergyForm, | 
					
						
							|  |  |  |                       UnconfineHomeWater, Objective) | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  | from .Regions import AquariaRegions | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class AquariaWeb(WebWorld): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Class used to generate the Aquaria Game Web pages (setup, tutorial, etc.) | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     theme = "ocean" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bug_report_page = "https://github.com/tioui/Aquaria_Randomizer/issues" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setup = Tutorial( | 
					
						
							|  |  |  |         "Multiworld Setup Guide", | 
					
						
							|  |  |  |         "A guide to setting up Aquaria for MultiWorld.", | 
					
						
							|  |  |  |         "English", | 
					
						
							|  |  |  |         "setup_en.md", | 
					
						
							|  |  |  |         "setup/en", | 
					
						
							|  |  |  |         ["Tioui"] | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setup_fr = Tutorial( | 
					
						
							|  |  |  |         "Guide de configuration Multimonde", | 
					
						
							|  |  |  |         "Un guide pour configurer Aquaria MultiWorld", | 
					
						
							|  |  |  |         "Français", | 
					
						
							|  |  |  |         "setup_fr.md", | 
					
						
							|  |  |  |         "setup/fr", | 
					
						
							|  |  |  |         ["Tioui"] | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tutorials = [setup, setup_fr] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class AquariaWorld(World): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Aquaria is a side-scrolling action-adventure game. It follows Naija, an | 
					
						
							|  |  |  |     aquatic humanoid woman, as she explores the underwater world of Aquaria. | 
					
						
							|  |  |  |     Along her journey, she learns about the history of the world she inhabits | 
					
						
							|  |  |  |     as well as her own past. The gameplay focuses on a combination of swimming, | 
					
						
							|  |  |  |     singing, and combat, through which Naija can interact with the world. Her | 
					
						
							|  |  |  |     songs can move items, affect plants and animals, and change her physical | 
					
						
							|  |  |  |     appearance into other forms that have different abilities, like firing | 
					
						
							|  |  |  |     projectiles at hostile creatures, or passing through barriers inaccessible | 
					
						
							|  |  |  |     to her in her natural form. | 
					
						
							|  |  |  |     From: https://en.wikipedia.org/wiki/Aquaria_(video_game) | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     game: str = "Aquaria" | 
					
						
							|  |  |  |     "The name of the game" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     topology_present = True | 
					
						
							|  |  |  |     "show path to required location checks in spoiler" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     web: WebWorld = AquariaWeb() | 
					
						
							|  |  |  |     "The web page generation informations" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |     item_name_to_id: ClassVar[Dict[str, int]] = \ | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |         {name: data.id for name, data in item_table.items()} | 
					
						
							|  |  |  |     "The name and associated ID of each item of the world" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     item_name_groups = { | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |         "Damage": {ItemNames.ENERGY_FORM, ItemNames.NATURE_FORM, ItemNames.BEAST_FORM, | 
					
						
							|  |  |  |                    ItemNames.LI_AND_LI_SONG, ItemNames.BABY_NAUTILUS, ItemNames.BABY_PIRANHA, | 
					
						
							|  |  |  |                    ItemNames.BABY_BLASTER}, | 
					
						
							|  |  |  |         "Light": {ItemNames.SUN_FORM, ItemNames.BABY_DUMBO} | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |     } | 
					
						
							|  |  |  |     """Grouping item make it easier to find them""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     location_name_to_id = location_table | 
					
						
							|  |  |  |     "The name and associated ID of each location of the world" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     base_id = 698000 | 
					
						
							|  |  |  |     "The starting ID of the items and locations of the world" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ingredients_substitution: List[int] | 
					
						
							|  |  |  |     "Used to randomize ingredient drop" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     options_dataclass = AquariaOptions | 
					
						
							|  |  |  |     "Used to manage world options" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     options: AquariaOptions | 
					
						
							|  |  |  |     "Every options of the world" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-13 14:09:39 -05:00
										 |  |  |     regions: AquariaRegions | None | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |     "Used to manage Regions" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     exclude: List[str] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, multiworld: MultiWorld, player: int): | 
					
						
							|  |  |  |         """Initialisation of the Aquaria World""" | 
					
						
							|  |  |  |         super(AquariaWorld, self).__init__(multiworld, player) | 
					
						
							| 
									
										
										
										
											2025-01-13 14:09:39 -05:00
										 |  |  |         self.regions = None | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |         self.ingredients_substitution = [] | 
					
						
							|  |  |  |         self.exclude = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-13 14:09:39 -05:00
										 |  |  |     def generate_early(self) -> None: | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Run before any general steps of the MultiWorld other than options. Useful for getting and adjusting option | 
					
						
							|  |  |  |         results and determining layouts for entrance rando etc. start inventory gets pushed after this step. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self.regions = AquariaRegions(self.multiworld, self.player) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |     def create_regions(self) -> None: | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Create every Region in `regions` | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self.regions.add_regions_to_world() | 
					
						
							|  |  |  |         self.regions.connect_regions() | 
					
						
							|  |  |  |         self.regions.add_event_locations() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_item(self, name: str) -> AquariaItem: | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-05-20 02:58:44 -04:00
										 |  |  |         Create an AquariaItem using 'name' as item name. | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         result: AquariaItem | 
					
						
							| 
									
										
										
										
											2024-11-21 19:43:37 +00:00
										 |  |  |         data = item_table[name] | 
					
						
							|  |  |  |         classification: ItemClassification = ItemClassification.useful | 
					
						
							|  |  |  |         if data.type == ItemType.JUNK: | 
					
						
							|  |  |  |             classification = ItemClassification.filler | 
					
						
							|  |  |  |         elif data.type == ItemType.PROGRESSION: | 
					
						
							|  |  |  |             classification = ItemClassification.progression | 
					
						
							|  |  |  |         result = AquariaItem(name, classification, data.id, self.player) | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-15 17:22:58 -04:00
										 |  |  |     def __pre_fill_item(self, item_name: str, location_name: str, precollected, | 
					
						
							|  |  |  |                         itemClassification: ItemClassification = ItemClassification.useful) -> None: | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |         """Pre-assign an item to a location""" | 
					
						
							|  |  |  |         if item_name not in precollected: | 
					
						
							|  |  |  |             self.exclude.append(item_name) | 
					
						
							|  |  |  |             data = item_table[item_name] | 
					
						
							| 
									
										
										
										
											2024-10-15 17:22:58 -04:00
										 |  |  |             item = AquariaItem(item_name, itemClassification, data.id, self.player) | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |             self.multiworld.get_location(location_name, self.player).place_locked_item(item) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_filler_item_name(self): | 
					
						
							|  |  |  |         """Getting a random ingredient item as filler""" | 
					
						
							|  |  |  |         ingredients = [] | 
					
						
							|  |  |  |         for name, data in item_table.items(): | 
					
						
							|  |  |  |             if data.group == ItemGroup.INGREDIENT: | 
					
						
							|  |  |  |                 ingredients.append(name) | 
					
						
							|  |  |  |         filler_item_name = self.random.choice(ingredients) | 
					
						
							|  |  |  |         return filler_item_name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_items(self) -> None: | 
					
						
							|  |  |  |         """Create every item in the world""" | 
					
						
							|  |  |  |         precollected = [item.name for item in self.multiworld.precollected_items[self.player]] | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |         if self.options.turtle_randomizer.value != TurtleRandomizer.option_none: | 
					
						
							|  |  |  |             if self.options.turtle_randomizer.value == TurtleRandomizer.option_all_except_final: | 
					
						
							|  |  |  |                 self.__pre_fill_item(ItemNames.TRANSTURTLE_BODY, AquariaLocationNames.FINAL_BOSS_AREA_TRANSTURTLE, | 
					
						
							|  |  |  |                                      precollected) | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |             self.__pre_fill_item(ItemNames.TRANSTURTLE_VEIL_TOP_LEFT, | 
					
						
							|  |  |  |                                  AquariaLocationNames.THE_VEIL_TOP_LEFT_AREA_TRANSTURTLE, precollected) | 
					
						
							|  |  |  |             self.__pre_fill_item(ItemNames.TRANSTURTLE_VEIL_TOP_RIGHT, | 
					
						
							|  |  |  |                                  AquariaLocationNames.THE_VEIL_TOP_RIGHT_AREA_TRANSTURTLE, precollected) | 
					
						
							|  |  |  |             self.__pre_fill_item(ItemNames.TRANSTURTLE_OPEN_WATERS, | 
					
						
							|  |  |  |                                  AquariaLocationNames.OPEN_WATERS_TOP_RIGHT_AREA_TRANSTURTLE, | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |                                  precollected) | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |             self.__pre_fill_item(ItemNames.TRANSTURTLE_KELP_FOREST, | 
					
						
							|  |  |  |                                  AquariaLocationNames.KELP_FOREST_BOTTOM_LEFT_AREA_TRANSTURTLE, | 
					
						
							|  |  |  |                                  precollected) | 
					
						
							|  |  |  |             self.__pre_fill_item(ItemNames.TRANSTURTLE_HOME_WATERS, AquariaLocationNames.HOME_WATERS_TRANSTURTLE, | 
					
						
							|  |  |  |                                  precollected) | 
					
						
							|  |  |  |             self.__pre_fill_item(ItemNames.TRANSTURTLE_ABYSS, AquariaLocationNames.ABYSS_RIGHT_AREA_TRANSTURTLE, | 
					
						
							|  |  |  |                                  precollected) | 
					
						
							|  |  |  |             self.__pre_fill_item(ItemNames.TRANSTURTLE_BODY, AquariaLocationNames.FINAL_BOSS_AREA_TRANSTURTLE, | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |                                  precollected) | 
					
						
							|  |  |  |             # The last two are inverted because in the original game, they are special turtle that communicate directly | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |             self.__pre_fill_item(ItemNames.TRANSTURTLE_SIMON_SAYS, AquariaLocationNames.ARNASSI_RUINS_TRANSTURTLE, | 
					
						
							|  |  |  |                                  precollected, ItemClassification.progression) | 
					
						
							|  |  |  |             self.__pre_fill_item(ItemNames.TRANSTURTLE_ARNASSI_RUINS, AquariaLocationNames.SIMON_SAYS_AREA_TRANSTURTLE, | 
					
						
							|  |  |  |                                  precollected) | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |         for name, data in item_table.items(): | 
					
						
							| 
									
										
										
										
											2024-06-11 18:59:46 -04:00
										 |  |  |             if name not in self.exclude: | 
					
						
							|  |  |  |                 for i in range(data.count): | 
					
						
							|  |  |  |                     item = self.create_item(name) | 
					
						
							|  |  |  |                     self.multiworld.itempool.append(item) | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def set_rules(self) -> None: | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Launched when the Multiworld generator is ready to generate rules | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |         if self.options.early_energy_form == EarlyEnergyForm.option_early: | 
					
						
							|  |  |  |             self.multiworld.early_items[self.player][ItemNames.ENERGY_FORM] = 1 | 
					
						
							|  |  |  |         elif self.options.early_energy_form == EarlyEnergyForm.option_early_and_local: | 
					
						
							|  |  |  |             self.multiworld.local_early_items[self.player][ItemNames.ENERGY_FORM] = 1 | 
					
						
							|  |  |  |         if self.options.early_bind_song == EarlyBindSong.option_early: | 
					
						
							|  |  |  |             self.multiworld.early_items[self.player][ItemNames.BIND_SONG] = 1 | 
					
						
							|  |  |  |         elif self.options.early_bind_song == EarlyBindSong.option_early_and_local: | 
					
						
							|  |  |  |             self.multiworld.local_early_items[self.player][ItemNames.BIND_SONG] = 1 | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |         self.regions.adjusting_rules(self.options) | 
					
						
							|  |  |  |         self.multiworld.completion_condition[self.player] = lambda \ | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |                 state: state.has(ItemNames.VICTORY, self.player) | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def generate_basic(self) -> None: | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Player-specific randomization that does not affect logic. | 
					
						
							|  |  |  |         Used to fill then `ingredients_substitution` list | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         simple_ingredients_substitution = [i for i in range(27)] | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |         if self.options.ingredient_randomizer.value > IngredientRandomizer.option_off: | 
					
						
							|  |  |  |             if self.options.ingredient_randomizer.value == IngredientRandomizer.option_common_ingredients: | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |                 simple_ingredients_substitution.pop(-1) | 
					
						
							|  |  |  |                 simple_ingredients_substitution.pop(-1) | 
					
						
							|  |  |  |                 simple_ingredients_substitution.pop(-1) | 
					
						
							|  |  |  |             self.random.shuffle(simple_ingredients_substitution) | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |             if self.options.ingredient_randomizer.value == IngredientRandomizer.option_common_ingredients: | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |                 simple_ingredients_substitution.extend([24, 25, 26]) | 
					
						
							|  |  |  |         dishes_substitution = [i for i in range(27, 76)] | 
					
						
							|  |  |  |         if self.options.dish_randomizer: | 
					
						
							|  |  |  |             self.random.shuffle(dishes_substitution) | 
					
						
							|  |  |  |         self.ingredients_substitution.clear() | 
					
						
							|  |  |  |         self.ingredients_substitution.extend(simple_ingredients_substitution) | 
					
						
							|  |  |  |         self.ingredients_substitution.extend(dishes_substitution) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def fill_slot_data(self) -> Dict[str, Any]: | 
					
						
							|  |  |  |         return {"ingredientReplacement": self.ingredients_substitution, | 
					
						
							| 
									
										
										
										
											2024-07-05 16:40:26 -04:00
										 |  |  |                 "aquarian_translate": bool(self.options.aquarian_translation.value), | 
					
						
							|  |  |  |                 "blind_goal": bool(self.options.blind_goal.value), | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |                 "secret_needed": | 
					
						
							|  |  |  |                     self.options.objective.value == Objective.option_obtain_secrets_and_kill_the_creator, | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |                 "minibosses_to_kill": self.options.mini_bosses_to_beat.value, | 
					
						
							|  |  |  |                 "bigbosses_to_kill": self.options.big_bosses_to_beat.value, | 
					
						
							|  |  |  |                 "skip_first_vision": bool(self.options.skip_first_vision.value), | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |                 "unconfine_home_water_energy_door": | 
					
						
							|  |  |  |                     self.options.unconfine_home_water.value == UnconfineHomeWater.option_via_energy_door | 
					
						
							|  |  |  |                     or self.options.unconfine_home_water.value == UnconfineHomeWater.option_via_both, | 
					
						
							|  |  |  |                 "unconfine_home_water_transturtle": | 
					
						
							|  |  |  |                     self.options.unconfine_home_water.value == UnconfineHomeWater.option_via_transturtle | 
					
						
							|  |  |  |                     or self.options.unconfine_home_water.value == UnconfineHomeWater.option_via_both, | 
					
						
							| 
									
										
										
										
											2024-10-14 12:53:20 -04:00
										 |  |  |                 "bind_song_needed_to_get_under_rock_bulb": bool(self.options.bind_song_needed_to_get_under_rock_bulb), | 
					
						
							|  |  |  |                 "no_progression_hard_or_hidden_locations": bool(self.options.no_progression_hard_or_hidden_locations), | 
					
						
							|  |  |  |                 "light_needed_to_get_to_dark_places": bool(self.options.light_needed_to_get_to_dark_places), | 
					
						
							| 
									
										
										
										
											2024-12-08 20:18:00 -05:00
										 |  |  |                 "turtle_randomizer": self.options.turtle_randomizer.value | 
					
						
							| 
									
										
										
										
											2024-05-17 06:29:00 -04:00
										 |  |  |                 } |