| 
									
										
										
										
											2023-11-26 11:17:59 -05:00
										 |  |  | import yaml | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import zipfile | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  | import Utils | 
					
						
							| 
									
										
										
										
											2023-11-26 11:17:59 -05:00
										 |  |  | from copy import deepcopy | 
					
						
							|  |  |  | from .Regions import object_id_table | 
					
						
							| 
									
										
										
										
											2024-03-14 14:29:29 -07:00
										 |  |  | from worlds.Files import APPatch | 
					
						
							| 
									
										
										
										
											2023-11-26 11:17:59 -05:00
										 |  |  | import pkgutil | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  | settings_template = Utils.parse_yaml(pkgutil.get_data(__name__, "data/settings.yaml")) | 
					
						
							| 
									
										
										
										
											2023-11-26 11:17:59 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_output(self, output_directory): | 
					
						
							|  |  |  |     def output_item_name(item): | 
					
						
							|  |  |  |         if item.player == self.player: | 
					
						
							|  |  |  |             if item.code > 0x420000 + 256: | 
					
						
							|  |  |  |                 item_name = self.item_id_to_name[item.code - 256] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 item_name = item.name | 
					
						
							|  |  |  |             item_name = "".join(item_name.split("'")) | 
					
						
							|  |  |  |             item_name = "".join(item_name.split(" ")) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if item.advancement or item.useful or (item.trap and | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |                                                    self.random.randint(0, 1)): | 
					
						
							| 
									
										
										
										
											2023-11-26 11:17:59 -05:00
										 |  |  |                 item_name = "APItem" | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 item_name = "APItemFiller" | 
					
						
							|  |  |  |         return item_name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     item_placement = [] | 
					
						
							|  |  |  |     for location in self.multiworld.get_locations(self.player): | 
					
						
							|  |  |  |         if location.type != "Trigger": | 
					
						
							|  |  |  |             item_placement.append({"object_id": object_id_table[location.name], "type": location.type, "content": | 
					
						
							|  |  |  |                 output_item_name(location.item), "player": self.multiworld.player_name[location.item.player], | 
					
						
							|  |  |  |                                    "item_name": location.item.name}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def cc(option): | 
					
						
							| 
									
										
										
										
											2023-12-06 12:24:59 -05:00
										 |  |  |         return option.current_key.title().replace("_", "").replace("OverworldAndDungeons", | 
					
						
							|  |  |  |             "OverworldDungeons").replace("MobsAndBosses", "MobsBosses").replace("MobsBossesAndDarkKing", | 
					
						
							|  |  |  |             "MobsBossesDK").replace("BenjaminLevelPlus", "BenPlus").replace("BenjaminLevel", "BenPlus0").replace( | 
					
						
							|  |  |  |             "RandomCompanion", "Random") | 
					
						
							| 
									
										
										
										
											2023-11-26 11:17:59 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def tf(option): | 
					
						
							|  |  |  |         return True if option else False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     options = deepcopy(settings_template) | 
					
						
							|  |  |  |     options["name"] = self.multiworld.player_name[self.player] | 
					
						
							|  |  |  |     option_writes = { | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |         "enemies_density": cc(self.options.enemies_density), | 
					
						
							| 
									
										
										
										
											2023-12-06 12:24:59 -05:00
										 |  |  |         "chests_shuffle": "Include", | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |         "shuffle_boxes_content": self.options.brown_boxes == "shuffle", | 
					
						
							| 
									
										
										
										
											2023-12-06 12:24:59 -05:00
										 |  |  |         "npcs_shuffle": "Include", | 
					
						
							|  |  |  |         "battlefields_shuffle": "Include", | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |         "logic_options": cc(self.options.logic), | 
					
						
							|  |  |  |         "shuffle_enemies_position": tf(self.options.shuffle_enemies_position), | 
					
						
							|  |  |  |         "enemies_scaling_lower": cc(self.options.enemies_scaling_lower), | 
					
						
							|  |  |  |         "enemies_scaling_upper": cc(self.options.enemies_scaling_upper), | 
					
						
							|  |  |  |         "bosses_scaling_lower": cc(self.options.bosses_scaling_lower), | 
					
						
							|  |  |  |         "bosses_scaling_upper": cc(self.options.bosses_scaling_upper), | 
					
						
							|  |  |  |         "enemizer_attacks": cc(self.options.enemizer_attacks), | 
					
						
							|  |  |  |         "leveling_curve": cc(self.options.leveling_curve), | 
					
						
							|  |  |  |         "battles_quantity": cc(self.options.battlefields_battles_quantities) if | 
					
						
							|  |  |  |                                self.options.battlefields_battles_quantities.value < 5 else | 
					
						
							| 
									
										
										
										
											2023-12-06 12:24:59 -05:00
										 |  |  |                                "RandomLow" if | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |                                self.options.battlefields_battles_quantities.value == 5 else | 
					
						
							| 
									
										
										
										
											2023-12-06 12:24:59 -05:00
										 |  |  |                                "RandomHigh", | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |         "shuffle_battlefield_rewards": tf(self.options.shuffle_battlefield_rewards), | 
					
						
							| 
									
										
										
										
											2023-12-06 12:24:59 -05:00
										 |  |  |         "random_starting_weapon": True, | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |         "progressive_gear": tf(self.options.progressive_gear), | 
					
						
							|  |  |  |         "tweaked_dungeons": tf(self.options.tweak_frustrating_dungeons), | 
					
						
							|  |  |  |         "doom_castle_mode": cc(self.options.doom_castle_mode), | 
					
						
							|  |  |  |         "doom_castle_shortcut": tf(self.options.doom_castle_shortcut), | 
					
						
							|  |  |  |         "sky_coin_mode": cc(self.options.sky_coin_mode), | 
					
						
							|  |  |  |         "sky_coin_fragments_qty": cc(self.options.shattered_sky_coin_quantity), | 
					
						
							| 
									
										
										
										
											2023-12-06 12:24:59 -05:00
										 |  |  |         "enable_spoilers": False, | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |         "progressive_formations": cc(self.options.progressive_formations), | 
					
						
							|  |  |  |         "map_shuffling": cc(self.options.map_shuffle), | 
					
						
							|  |  |  |         "crest_shuffle": tf(self.options.crest_shuffle), | 
					
						
							|  |  |  |         "enemizer_groups": cc(self.options.enemizer_groups), | 
					
						
							|  |  |  |         "shuffle_res_weak_type": tf(self.options.shuffle_res_weak_types), | 
					
						
							|  |  |  |         "companion_leveling_type": cc(self.options.companion_leveling_type), | 
					
						
							|  |  |  |         "companion_spellbook_type": cc(self.options.companion_spellbook_type), | 
					
						
							|  |  |  |         "starting_companion": cc(self.options.starting_companion), | 
					
						
							| 
									
										
										
										
											2023-12-06 12:24:59 -05:00
										 |  |  |         "available_companions": ["Zero", "One", "Two", | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |                                  "Three", "Four"][self.options.available_companions.value], | 
					
						
							|  |  |  |         "companions_locations": cc(self.options.companions_locations), | 
					
						
							|  |  |  |         "kaelis_mom_fight_minotaur": tf(self.options.kaelis_mom_fight_minotaur), | 
					
						
							| 
									
										
										
										
											2023-12-06 12:24:59 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-26 11:17:59 -05:00
										 |  |  |     for option, data in option_writes.items(): | 
					
						
							|  |  |  |         options["Final Fantasy Mystic Quest"][option][data] = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |     rom_name = f'MQ{Utils.__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed_name:11}'[:21] | 
					
						
							| 
									
										
										
										
											2023-11-26 11:17:59 -05:00
										 |  |  |     self.rom_name = bytearray(rom_name, | 
					
						
							|  |  |  |                               'utf8') | 
					
						
							|  |  |  |     self.rom_name_available_event.set() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-06 12:24:59 -05:00
										 |  |  |     setup = {"version": "1.5", "name": self.multiworld.player_name[self.player], "romname": rom_name, "seed": | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |              hex(self.random.randint(0, 0xFFFFFFFF)).split("0x")[1].upper()} | 
					
						
							| 
									
										
										
										
											2023-11-26 11:17:59 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     starting_items = [output_item_name(item) for item in self.multiworld.precollected_items[self.player]] | 
					
						
							| 
									
										
										
										
											2024-07-24 07:46:14 -04:00
										 |  |  |     if self.options.sky_coin_mode == "shattered_sky_coin": | 
					
						
							| 
									
										
										
										
											2023-11-26 11:17:59 -05:00
										 |  |  |         starting_items.append("SkyCoin") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     file_path = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.apmq") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     APMQ = APMQFile(file_path, player=self.player, player_name=self.multiworld.player_name[self.player]) | 
					
						
							|  |  |  |     with zipfile.ZipFile(file_path, mode="w", compression=zipfile.ZIP_DEFLATED, | 
					
						
							|  |  |  |                          compresslevel=9) as zf: | 
					
						
							|  |  |  |         zf.writestr("itemplacement.yaml", yaml.dump(item_placement)) | 
					
						
							|  |  |  |         zf.writestr("flagset.yaml", yaml.dump(options)) | 
					
						
							|  |  |  |         zf.writestr("startingitems.yaml", yaml.dump(starting_items)) | 
					
						
							|  |  |  |         zf.writestr("setup.yaml", yaml.dump(setup)) | 
					
						
							|  |  |  |         zf.writestr("rooms.yaml", yaml.dump(self.rooms)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         APMQ.write_contents(zf) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-14 14:29:29 -07:00
										 |  |  | class APMQFile(APPatch): | 
					
						
							| 
									
										
										
										
											2023-11-26 11:17:59 -05:00
										 |  |  |     game = "Final Fantasy Mystic Quest" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_manifest(self): | 
					
						
							|  |  |  |         manifest = super().get_manifest() | 
					
						
							|  |  |  |         manifest["patch_file_ending"] = ".apmq" | 
					
						
							| 
									
										
										
										
											2024-03-14 14:29:29 -07:00
										 |  |  |         return manifest |