FFMQ: Efficiency Improvement and Use New Options Methods (#2767)
* FFMQ Efficiency improvement and use new options methods * Hard check for 0x01 game status * Fixes * Why were Mac's Ship entrance hints excluded? * Two remaining per_slot_randoms purged * reformat generate_early * Utils.parse_yaml
This commit is contained in:
parent
ad5089b5a3
commit
e7dbfa7fcd
|
@ -71,7 +71,7 @@ class FFMQClient(SNIClient):
|
||||||
received = await snes_read(ctx, RECEIVED_DATA[0], RECEIVED_DATA[1])
|
received = await snes_read(ctx, RECEIVED_DATA[0], RECEIVED_DATA[1])
|
||||||
data = await snes_read(ctx, READ_DATA_START, READ_DATA_END - READ_DATA_START)
|
data = await snes_read(ctx, READ_DATA_START, READ_DATA_END - READ_DATA_START)
|
||||||
check_2 = await snes_read(ctx, 0xF53749, 1)
|
check_2 = await snes_read(ctx, 0xF53749, 1)
|
||||||
if check_1 in (b'\x00', b'\x55') or check_2 in (b'\x00', b'\x55'):
|
if check_1 != b'01' or check_2 != b'01':
|
||||||
return
|
return
|
||||||
|
|
||||||
def get_range(data_range):
|
def get_range(data_range):
|
||||||
|
|
|
@ -222,10 +222,10 @@ for item, data in item_table.items():
|
||||||
|
|
||||||
def create_items(self) -> None:
|
def create_items(self) -> None:
|
||||||
items = []
|
items = []
|
||||||
starting_weapon = self.multiworld.starting_weapon[self.player].current_key.title().replace("_", " ")
|
starting_weapon = self.options.starting_weapon.current_key.title().replace("_", " ")
|
||||||
self.multiworld.push_precollected(self.create_item(starting_weapon))
|
self.multiworld.push_precollected(self.create_item(starting_weapon))
|
||||||
self.multiworld.push_precollected(self.create_item("Steel Armor"))
|
self.multiworld.push_precollected(self.create_item("Steel Armor"))
|
||||||
if self.multiworld.sky_coin_mode[self.player] == "start_with":
|
if self.options.sky_coin_mode == "start_with":
|
||||||
self.multiworld.push_precollected(self.create_item("Sky Coin"))
|
self.multiworld.push_precollected(self.create_item("Sky Coin"))
|
||||||
|
|
||||||
precollected_item_names = {item.name for item in self.multiworld.precollected_items[self.player]}
|
precollected_item_names = {item.name for item in self.multiworld.precollected_items[self.player]}
|
||||||
|
@ -233,28 +233,28 @@ def create_items(self) -> None:
|
||||||
def add_item(item_name):
|
def add_item(item_name):
|
||||||
if item_name in ["Steel Armor", "Sky Fragment"] or "Progressive" in item_name:
|
if item_name in ["Steel Armor", "Sky Fragment"] or "Progressive" in item_name:
|
||||||
return
|
return
|
||||||
if item_name.lower().replace(" ", "_") == self.multiworld.starting_weapon[self.player].current_key:
|
if item_name.lower().replace(" ", "_") == self.options.starting_weapon.current_key:
|
||||||
return
|
return
|
||||||
if self.multiworld.progressive_gear[self.player]:
|
if self.options.progressive_gear:
|
||||||
for item_group in prog_map:
|
for item_group in prog_map:
|
||||||
if item_name in self.item_name_groups[item_group]:
|
if item_name in self.item_name_groups[item_group]:
|
||||||
item_name = prog_map[item_group]
|
item_name = prog_map[item_group]
|
||||||
break
|
break
|
||||||
if item_name == "Sky Coin":
|
if item_name == "Sky Coin":
|
||||||
if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin":
|
if self.options.sky_coin_mode == "shattered_sky_coin":
|
||||||
for _ in range(40):
|
for _ in range(40):
|
||||||
items.append(self.create_item("Sky Fragment"))
|
items.append(self.create_item("Sky Fragment"))
|
||||||
return
|
return
|
||||||
elif self.multiworld.sky_coin_mode[self.player] == "save_the_crystals":
|
elif self.options.sky_coin_mode == "save_the_crystals":
|
||||||
items.append(self.create_filler())
|
items.append(self.create_filler())
|
||||||
return
|
return
|
||||||
if item_name in precollected_item_names:
|
if item_name in precollected_item_names:
|
||||||
items.append(self.create_filler())
|
items.append(self.create_filler())
|
||||||
return
|
return
|
||||||
i = self.create_item(item_name)
|
i = self.create_item(item_name)
|
||||||
if self.multiworld.logic[self.player] != "friendly" and item_name in ("Magic Mirror", "Mask"):
|
if self.options.logic != "friendly" and item_name in ("Magic Mirror", "Mask"):
|
||||||
i.classification = ItemClassification.useful
|
i.classification = ItemClassification.useful
|
||||||
if (self.multiworld.logic[self.player] == "expert" and self.multiworld.map_shuffle[self.player] == "none" and
|
if (self.options.logic == "expert" and self.options.map_shuffle == "none" and
|
||||||
item_name == "Exit Book"):
|
item_name == "Exit Book"):
|
||||||
i.classification = ItemClassification.progression
|
i.classification = ItemClassification.progression
|
||||||
items.append(i)
|
items.append(i)
|
||||||
|
@ -263,11 +263,11 @@ def create_items(self) -> None:
|
||||||
for item in self.item_name_groups[item_group]:
|
for item in self.item_name_groups[item_group]:
|
||||||
add_item(item)
|
add_item(item)
|
||||||
|
|
||||||
if self.multiworld.brown_boxes[self.player] == "include":
|
if self.options.brown_boxes == "include":
|
||||||
filler_items = []
|
filler_items = []
|
||||||
for item, count in fillers.items():
|
for item, count in fillers.items():
|
||||||
filler_items += [self.create_item(item) for _ in range(count)]
|
filler_items += [self.create_item(item) for _ in range(count)]
|
||||||
if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin":
|
if self.options.sky_coin_mode == "shattered_sky_coin":
|
||||||
self.multiworld.random.shuffle(filler_items)
|
self.multiworld.random.shuffle(filler_items)
|
||||||
filler_items = filler_items[39:]
|
filler_items = filler_items[39:]
|
||||||
items += filler_items
|
items += filler_items
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from Options import Choice, FreeText, Toggle, Range
|
from Options import Choice, FreeText, Toggle, Range, PerGameCommonOptions
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
class Logic(Choice):
|
class Logic(Choice):
|
||||||
|
@ -321,36 +322,36 @@ class KaelisMomFightsMinotaur(Toggle):
|
||||||
default = 0
|
default = 0
|
||||||
|
|
||||||
|
|
||||||
option_definitions = {
|
@dataclass
|
||||||
"logic": Logic,
|
class FFMQOptions(PerGameCommonOptions):
|
||||||
"brown_boxes": BrownBoxes,
|
logic: Logic
|
||||||
"sky_coin_mode": SkyCoinMode,
|
brown_boxes: BrownBoxes
|
||||||
"shattered_sky_coin_quantity": ShatteredSkyCoinQuantity,
|
sky_coin_mode: SkyCoinMode
|
||||||
"starting_weapon": StartingWeapon,
|
shattered_sky_coin_quantity: ShatteredSkyCoinQuantity
|
||||||
"progressive_gear": ProgressiveGear,
|
starting_weapon: StartingWeapon
|
||||||
"leveling_curve": LevelingCurve,
|
progressive_gear: ProgressiveGear
|
||||||
"starting_companion": StartingCompanion,
|
leveling_curve: LevelingCurve
|
||||||
"available_companions": AvailableCompanions,
|
starting_companion: StartingCompanion
|
||||||
"companions_locations": CompanionsLocations,
|
available_companions: AvailableCompanions
|
||||||
"kaelis_mom_fight_minotaur": KaelisMomFightsMinotaur,
|
companions_locations: CompanionsLocations
|
||||||
"companion_leveling_type": CompanionLevelingType,
|
kaelis_mom_fight_minotaur: KaelisMomFightsMinotaur
|
||||||
"companion_spellbook_type": CompanionSpellbookType,
|
companion_leveling_type: CompanionLevelingType
|
||||||
"enemies_density": EnemiesDensity,
|
companion_spellbook_type: CompanionSpellbookType
|
||||||
"enemies_scaling_lower": EnemiesScalingLower,
|
enemies_density: EnemiesDensity
|
||||||
"enemies_scaling_upper": EnemiesScalingUpper,
|
enemies_scaling_lower: EnemiesScalingLower
|
||||||
"bosses_scaling_lower": BossesScalingLower,
|
enemies_scaling_upper: EnemiesScalingUpper
|
||||||
"bosses_scaling_upper": BossesScalingUpper,
|
bosses_scaling_lower: BossesScalingLower
|
||||||
"enemizer_attacks": EnemizerAttacks,
|
bosses_scaling_upper: BossesScalingUpper
|
||||||
"enemizer_groups": EnemizerGroups,
|
enemizer_attacks: EnemizerAttacks
|
||||||
"shuffle_res_weak_types": ShuffleResWeakType,
|
enemizer_groups: EnemizerGroups
|
||||||
"shuffle_enemies_position": ShuffleEnemiesPositions,
|
shuffle_res_weak_types: ShuffleResWeakType
|
||||||
"progressive_formations": ProgressiveFormations,
|
shuffle_enemies_position: ShuffleEnemiesPositions
|
||||||
"doom_castle_mode": DoomCastle,
|
progressive_formations: ProgressiveFormations
|
||||||
"doom_castle_shortcut": DoomCastleShortcut,
|
doom_castle_mode: DoomCastle
|
||||||
"tweak_frustrating_dungeons": TweakFrustratingDungeons,
|
doom_castle_shortcut: DoomCastleShortcut
|
||||||
"map_shuffle": MapShuffle,
|
tweak_frustrating_dungeons: TweakFrustratingDungeons
|
||||||
"crest_shuffle": CrestShuffle,
|
map_shuffle: MapShuffle
|
||||||
"shuffle_battlefield_rewards": ShuffleBattlefieldRewards,
|
crest_shuffle: CrestShuffle
|
||||||
"map_shuffle_seed": MapShuffleSeed,
|
shuffle_battlefield_rewards: ShuffleBattlefieldRewards
|
||||||
"battlefields_battles_quantities": BattlefieldsBattlesQuantities,
|
map_shuffle_seed: MapShuffleSeed
|
||||||
}
|
battlefields_battles_quantities: BattlefieldsBattlesQuantities
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import yaml
|
import yaml
|
||||||
import os
|
import os
|
||||||
import zipfile
|
import zipfile
|
||||||
|
import Utils
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from .Regions import object_id_table
|
from .Regions import object_id_table
|
||||||
from Utils import __version__
|
|
||||||
from worlds.Files import APPatch
|
from worlds.Files import APPatch
|
||||||
import pkgutil
|
import pkgutil
|
||||||
|
|
||||||
settings_template = yaml.load(pkgutil.get_data(__name__, "data/settings.yaml"), yaml.Loader)
|
settings_template = Utils.parse_yaml(pkgutil.get_data(__name__, "data/settings.yaml"))
|
||||||
|
|
||||||
|
|
||||||
def generate_output(self, output_directory):
|
def generate_output(self, output_directory):
|
||||||
|
@ -21,7 +21,7 @@ def generate_output(self, output_directory):
|
||||||
item_name = "".join(item_name.split(" "))
|
item_name = "".join(item_name.split(" "))
|
||||||
else:
|
else:
|
||||||
if item.advancement or item.useful or (item.trap and
|
if item.advancement or item.useful or (item.trap and
|
||||||
self.multiworld.per_slot_randoms[self.player].randint(0, 1)):
|
self.random.randint(0, 1)):
|
||||||
item_name = "APItem"
|
item_name = "APItem"
|
||||||
else:
|
else:
|
||||||
item_name = "APItemFiller"
|
item_name = "APItemFiller"
|
||||||
|
@ -46,60 +46,60 @@ def generate_output(self, output_directory):
|
||||||
options = deepcopy(settings_template)
|
options = deepcopy(settings_template)
|
||||||
options["name"] = self.multiworld.player_name[self.player]
|
options["name"] = self.multiworld.player_name[self.player]
|
||||||
option_writes = {
|
option_writes = {
|
||||||
"enemies_density": cc(self.multiworld.enemies_density[self.player]),
|
"enemies_density": cc(self.options.enemies_density),
|
||||||
"chests_shuffle": "Include",
|
"chests_shuffle": "Include",
|
||||||
"shuffle_boxes_content": self.multiworld.brown_boxes[self.player] == "shuffle",
|
"shuffle_boxes_content": self.options.brown_boxes == "shuffle",
|
||||||
"npcs_shuffle": "Include",
|
"npcs_shuffle": "Include",
|
||||||
"battlefields_shuffle": "Include",
|
"battlefields_shuffle": "Include",
|
||||||
"logic_options": cc(self.multiworld.logic[self.player]),
|
"logic_options": cc(self.options.logic),
|
||||||
"shuffle_enemies_position": tf(self.multiworld.shuffle_enemies_position[self.player]),
|
"shuffle_enemies_position": tf(self.options.shuffle_enemies_position),
|
||||||
"enemies_scaling_lower": cc(self.multiworld.enemies_scaling_lower[self.player]),
|
"enemies_scaling_lower": cc(self.options.enemies_scaling_lower),
|
||||||
"enemies_scaling_upper": cc(self.multiworld.enemies_scaling_upper[self.player]),
|
"enemies_scaling_upper": cc(self.options.enemies_scaling_upper),
|
||||||
"bosses_scaling_lower": cc(self.multiworld.bosses_scaling_lower[self.player]),
|
"bosses_scaling_lower": cc(self.options.bosses_scaling_lower),
|
||||||
"bosses_scaling_upper": cc(self.multiworld.bosses_scaling_upper[self.player]),
|
"bosses_scaling_upper": cc(self.options.bosses_scaling_upper),
|
||||||
"enemizer_attacks": cc(self.multiworld.enemizer_attacks[self.player]),
|
"enemizer_attacks": cc(self.options.enemizer_attacks),
|
||||||
"leveling_curve": cc(self.multiworld.leveling_curve[self.player]),
|
"leveling_curve": cc(self.options.leveling_curve),
|
||||||
"battles_quantity": cc(self.multiworld.battlefields_battles_quantities[self.player]) if
|
"battles_quantity": cc(self.options.battlefields_battles_quantities) if
|
||||||
self.multiworld.battlefields_battles_quantities[self.player].value < 5 else
|
self.options.battlefields_battles_quantities.value < 5 else
|
||||||
"RandomLow" if
|
"RandomLow" if
|
||||||
self.multiworld.battlefields_battles_quantities[self.player].value == 5 else
|
self.options.battlefields_battles_quantities.value == 5 else
|
||||||
"RandomHigh",
|
"RandomHigh",
|
||||||
"shuffle_battlefield_rewards": tf(self.multiworld.shuffle_battlefield_rewards[self.player]),
|
"shuffle_battlefield_rewards": tf(self.options.shuffle_battlefield_rewards),
|
||||||
"random_starting_weapon": True,
|
"random_starting_weapon": True,
|
||||||
"progressive_gear": tf(self.multiworld.progressive_gear[self.player]),
|
"progressive_gear": tf(self.options.progressive_gear),
|
||||||
"tweaked_dungeons": tf(self.multiworld.tweak_frustrating_dungeons[self.player]),
|
"tweaked_dungeons": tf(self.options.tweak_frustrating_dungeons),
|
||||||
"doom_castle_mode": cc(self.multiworld.doom_castle_mode[self.player]),
|
"doom_castle_mode": cc(self.options.doom_castle_mode),
|
||||||
"doom_castle_shortcut": tf(self.multiworld.doom_castle_shortcut[self.player]),
|
"doom_castle_shortcut": tf(self.options.doom_castle_shortcut),
|
||||||
"sky_coin_mode": cc(self.multiworld.sky_coin_mode[self.player]),
|
"sky_coin_mode": cc(self.options.sky_coin_mode),
|
||||||
"sky_coin_fragments_qty": cc(self.multiworld.shattered_sky_coin_quantity[self.player]),
|
"sky_coin_fragments_qty": cc(self.options.shattered_sky_coin_quantity),
|
||||||
"enable_spoilers": False,
|
"enable_spoilers": False,
|
||||||
"progressive_formations": cc(self.multiworld.progressive_formations[self.player]),
|
"progressive_formations": cc(self.options.progressive_formations),
|
||||||
"map_shuffling": cc(self.multiworld.map_shuffle[self.player]),
|
"map_shuffling": cc(self.options.map_shuffle),
|
||||||
"crest_shuffle": tf(self.multiworld.crest_shuffle[self.player]),
|
"crest_shuffle": tf(self.options.crest_shuffle),
|
||||||
"enemizer_groups": cc(self.multiworld.enemizer_groups[self.player]),
|
"enemizer_groups": cc(self.options.enemizer_groups),
|
||||||
"shuffle_res_weak_type": tf(self.multiworld.shuffle_res_weak_types[self.player]),
|
"shuffle_res_weak_type": tf(self.options.shuffle_res_weak_types),
|
||||||
"companion_leveling_type": cc(self.multiworld.companion_leveling_type[self.player]),
|
"companion_leveling_type": cc(self.options.companion_leveling_type),
|
||||||
"companion_spellbook_type": cc(self.multiworld.companion_spellbook_type[self.player]),
|
"companion_spellbook_type": cc(self.options.companion_spellbook_type),
|
||||||
"starting_companion": cc(self.multiworld.starting_companion[self.player]),
|
"starting_companion": cc(self.options.starting_companion),
|
||||||
"available_companions": ["Zero", "One", "Two",
|
"available_companions": ["Zero", "One", "Two",
|
||||||
"Three", "Four"][self.multiworld.available_companions[self.player].value],
|
"Three", "Four"][self.options.available_companions.value],
|
||||||
"companions_locations": cc(self.multiworld.companions_locations[self.player]),
|
"companions_locations": cc(self.options.companions_locations),
|
||||||
"kaelis_mom_fight_minotaur": tf(self.multiworld.kaelis_mom_fight_minotaur[self.player]),
|
"kaelis_mom_fight_minotaur": tf(self.options.kaelis_mom_fight_minotaur),
|
||||||
}
|
}
|
||||||
|
|
||||||
for option, data in option_writes.items():
|
for option, data in option_writes.items():
|
||||||
options["Final Fantasy Mystic Quest"][option][data] = 1
|
options["Final Fantasy Mystic Quest"][option][data] = 1
|
||||||
|
|
||||||
rom_name = f'MQ{__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed_name:11}'[:21]
|
rom_name = f'MQ{Utils.__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed_name:11}'[:21]
|
||||||
self.rom_name = bytearray(rom_name,
|
self.rom_name = bytearray(rom_name,
|
||||||
'utf8')
|
'utf8')
|
||||||
self.rom_name_available_event.set()
|
self.rom_name_available_event.set()
|
||||||
|
|
||||||
setup = {"version": "1.5", "name": self.multiworld.player_name[self.player], "romname": rom_name, "seed":
|
setup = {"version": "1.5", "name": self.multiworld.player_name[self.player], "romname": rom_name, "seed":
|
||||||
hex(self.multiworld.per_slot_randoms[self.player].randint(0, 0xFFFFFFFF)).split("0x")[1].upper()}
|
hex(self.random.randint(0, 0xFFFFFFFF)).split("0x")[1].upper()}
|
||||||
|
|
||||||
starting_items = [output_item_name(item) for item in self.multiworld.precollected_items[self.player]]
|
starting_items = [output_item_name(item) for item in self.multiworld.precollected_items[self.player]]
|
||||||
if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin":
|
if self.options.sky_coin_mode == "shattered_sky_coin":
|
||||||
starting_items.append("SkyCoin")
|
starting_items.append("SkyCoin")
|
||||||
|
|
||||||
file_path = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.apmq")
|
file_path = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.apmq")
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
from BaseClasses import Region, MultiWorld, Entrance, Location, LocationProgressType, ItemClassification
|
from BaseClasses import Region, MultiWorld, Entrance, Location, LocationProgressType, ItemClassification
|
||||||
from worlds.generic.Rules import add_rule
|
from worlds.generic.Rules import add_rule
|
||||||
|
from .data.rooms import rooms, entrances
|
||||||
from .Items import item_groups, yaml_item
|
from .Items import item_groups, yaml_item
|
||||||
import pkgutil
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
rooms = yaml.load(pkgutil.get_data(__name__, "data/rooms.yaml"), yaml.Loader)
|
entrance_names = {entrance["id"]: entrance["name"] for entrance in entrances}
|
||||||
entrance_names = {entrance["id"]: entrance["name"] for entrance in yaml.load(pkgutil.get_data(__name__, "data/entrances.yaml"), yaml.Loader)}
|
|
||||||
|
|
||||||
object_id_table = {}
|
object_id_table = {}
|
||||||
object_type_table = {}
|
object_type_table = {}
|
||||||
|
@ -69,7 +67,7 @@ def create_regions(self):
|
||||||
location_table else None, object["type"], object["access"],
|
location_table else None, object["type"], object["access"],
|
||||||
self.create_item(yaml_item(object["on_trigger"][0])) if object["type"] == "Trigger" else None) for object in
|
self.create_item(yaml_item(object["on_trigger"][0])) if object["type"] == "Trigger" else None) for object in
|
||||||
room["game_objects"] if "Hero Chest" not in object["name"] and object["type"] not in ("BattlefieldGp",
|
room["game_objects"] if "Hero Chest" not in object["name"] and object["type"] not in ("BattlefieldGp",
|
||||||
"BattlefieldXp") and (object["type"] != "Box" or self.multiworld.brown_boxes[self.player] == "include") and
|
"BattlefieldXp") and (object["type"] != "Box" or self.options.brown_boxes == "include") and
|
||||||
not (object["name"] == "Kaeli Companion" and not object["on_trigger"])], room["links"]))
|
not (object["name"] == "Kaeli Companion" and not object["on_trigger"])], room["links"]))
|
||||||
|
|
||||||
dark_king_room = self.multiworld.get_region("Doom Castle Dark King Room", self.player)
|
dark_king_room = self.multiworld.get_region("Doom Castle Dark King Room", self.player)
|
||||||
|
@ -91,15 +89,13 @@ def create_regions(self):
|
||||||
if "entrance" in link and link["entrance"] != -1:
|
if "entrance" in link and link["entrance"] != -1:
|
||||||
spoiler = False
|
spoiler = False
|
||||||
if link["entrance"] in crest_warps:
|
if link["entrance"] in crest_warps:
|
||||||
if self.multiworld.crest_shuffle[self.player]:
|
if self.options.crest_shuffle:
|
||||||
spoiler = True
|
spoiler = True
|
||||||
elif self.multiworld.map_shuffle[self.player] == "everything":
|
elif self.options.map_shuffle == "everything":
|
||||||
spoiler = True
|
spoiler = True
|
||||||
elif "Subregion" in region.name and self.multiworld.map_shuffle[self.player] not in ("dungeons",
|
elif "Subregion" in region.name and self.options.map_shuffle not in ("dungeons", "none"):
|
||||||
"none"):
|
|
||||||
spoiler = True
|
spoiler = True
|
||||||
elif "Subregion" not in region.name and self.multiworld.map_shuffle[self.player] not in ("none",
|
elif "Subregion" not in region.name and self.options.map_shuffle not in ("none", "overworld"):
|
||||||
"overworld"):
|
|
||||||
spoiler = True
|
spoiler = True
|
||||||
|
|
||||||
if spoiler:
|
if spoiler:
|
||||||
|
@ -111,6 +107,7 @@ def create_regions(self):
|
||||||
connection.connect(connect_room)
|
connection.connect(connect_room)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
non_dead_end_crest_rooms = [
|
non_dead_end_crest_rooms = [
|
||||||
'Libra Temple', 'Aquaria Gemini Room', "GrenadeMan's Mobius Room", 'Fireburg Gemini Room',
|
'Libra Temple', 'Aquaria Gemini Room', "GrenadeMan's Mobius Room", 'Fireburg Gemini Room',
|
||||||
'Sealed Temple', 'Alive Forest', 'Kaidge Temple Upper Ledge',
|
'Sealed Temple', 'Alive Forest', 'Kaidge Temple Upper Ledge',
|
||||||
|
@ -140,7 +137,7 @@ def set_rules(self) -> None:
|
||||||
add_rule(self.multiworld.get_location("Gidrah", self.player), hard_boss_logic)
|
add_rule(self.multiworld.get_location("Gidrah", self.player), hard_boss_logic)
|
||||||
add_rule(self.multiworld.get_location("Dullahan", self.player), hard_boss_logic)
|
add_rule(self.multiworld.get_location("Dullahan", self.player), hard_boss_logic)
|
||||||
|
|
||||||
if self.multiworld.map_shuffle[self.player]:
|
if self.options.map_shuffle:
|
||||||
for boss in ("Freezer Crab", "Ice Golem", "Jinn", "Medusa", "Dualhead Hydra"):
|
for boss in ("Freezer Crab", "Ice Golem", "Jinn", "Medusa", "Dualhead Hydra"):
|
||||||
loc = self.multiworld.get_location(boss, self.player)
|
loc = self.multiworld.get_location(boss, self.player)
|
||||||
checked_regions = {loc.parent_region}
|
checked_regions = {loc.parent_region}
|
||||||
|
@ -158,12 +155,12 @@ def set_rules(self) -> None:
|
||||||
return True
|
return True
|
||||||
check_foresta(loc.parent_region)
|
check_foresta(loc.parent_region)
|
||||||
|
|
||||||
if self.multiworld.logic[self.player] == "friendly":
|
if self.options.logic == "friendly":
|
||||||
process_rules(self.multiworld.get_entrance("Overworld - Ice Pyramid", self.player),
|
process_rules(self.multiworld.get_entrance("Overworld - Ice Pyramid", self.player),
|
||||||
["MagicMirror"])
|
["MagicMirror"])
|
||||||
process_rules(self.multiworld.get_entrance("Overworld - Volcano", self.player),
|
process_rules(self.multiworld.get_entrance("Overworld - Volcano", self.player),
|
||||||
["Mask"])
|
["Mask"])
|
||||||
if self.multiworld.map_shuffle[self.player] in ("none", "overworld"):
|
if self.options.map_shuffle in ("none", "overworld"):
|
||||||
process_rules(self.multiworld.get_entrance("Overworld - Bone Dungeon", self.player),
|
process_rules(self.multiworld.get_entrance("Overworld - Bone Dungeon", self.player),
|
||||||
["Bomb"])
|
["Bomb"])
|
||||||
process_rules(self.multiworld.get_entrance("Overworld - Wintry Cave", self.player),
|
process_rules(self.multiworld.get_entrance("Overworld - Wintry Cave", self.player),
|
||||||
|
@ -185,8 +182,8 @@ def set_rules(self) -> None:
|
||||||
process_rules(self.multiworld.get_entrance("Overworld - Mac Ship Doom", self.player),
|
process_rules(self.multiworld.get_entrance("Overworld - Mac Ship Doom", self.player),
|
||||||
["DragonClaw", "CaptainCap"])
|
["DragonClaw", "CaptainCap"])
|
||||||
|
|
||||||
if self.multiworld.logic[self.player] == "expert":
|
if self.options.logic == "expert":
|
||||||
if self.multiworld.map_shuffle[self.player] == "none" and not self.multiworld.crest_shuffle[self.player]:
|
if self.options.map_shuffle == "none" and not self.options.crest_shuffle:
|
||||||
inner_room = self.multiworld.get_region("Wintry Temple Inner Room", self.player)
|
inner_room = self.multiworld.get_region("Wintry Temple Inner Room", self.player)
|
||||||
connection = Entrance(self.player, "Sealed Temple Exit Trick", inner_room)
|
connection = Entrance(self.player, "Sealed Temple Exit Trick", inner_room)
|
||||||
connection.connect(self.multiworld.get_region("Wintry Temple Outer Room", self.player))
|
connection.connect(self.multiworld.get_region("Wintry Temple Outer Room", self.player))
|
||||||
|
@ -198,14 +195,14 @@ def set_rules(self) -> None:
|
||||||
if entrance.connected_region.name in non_dead_end_crest_rooms:
|
if entrance.connected_region.name in non_dead_end_crest_rooms:
|
||||||
entrance.access_rule = lambda state: False
|
entrance.access_rule = lambda state: False
|
||||||
|
|
||||||
if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin":
|
if self.options.sky_coin_mode == "shattered_sky_coin":
|
||||||
logic_coins = [16, 24, 32, 32, 38][self.multiworld.shattered_sky_coin_quantity[self.player].value]
|
logic_coins = [16, 24, 32, 32, 38][self.options.shattered_sky_coin_quantity.value]
|
||||||
self.multiworld.get_entrance("Focus Tower 1F - Sky Door", self.player).access_rule = \
|
self.multiworld.get_entrance("Focus Tower 1F - Sky Door", self.player).access_rule = \
|
||||||
lambda state: state.has("Sky Fragment", self.player, logic_coins)
|
lambda state: state.has("Sky Fragment", self.player, logic_coins)
|
||||||
elif self.multiworld.sky_coin_mode[self.player] == "save_the_crystals":
|
elif self.options.sky_coin_mode == "save_the_crystals":
|
||||||
self.multiworld.get_entrance("Focus Tower 1F - Sky Door", self.player).access_rule = \
|
self.multiworld.get_entrance("Focus Tower 1F - Sky Door", self.player).access_rule = \
|
||||||
lambda state: state.has_all(["Flamerus Rex", "Dualhead Hydra", "Ice Golem", "Pazuzu"], self.player)
|
lambda state: state.has_all(["Flamerus Rex", "Dualhead Hydra", "Ice Golem", "Pazuzu"], self.player)
|
||||||
elif self.multiworld.sky_coin_mode[self.player] in ("standard", "start_with"):
|
elif self.options.sky_coin_mode in ("standard", "start_with"):
|
||||||
self.multiworld.get_entrance("Focus Tower 1F - Sky Door", self.player).access_rule = \
|
self.multiworld.get_entrance("Focus Tower 1F - Sky Door", self.player).access_rule = \
|
||||||
lambda state: state.has("Sky Coin", self.player)
|
lambda state: state.has("Sky Coin", self.player)
|
||||||
|
|
||||||
|
@ -213,26 +210,24 @@ def set_rules(self) -> None:
|
||||||
def stage_set_rules(multiworld):
|
def stage_set_rules(multiworld):
|
||||||
# If there's no enemies, there's no repeatable income sources
|
# If there's no enemies, there's no repeatable income sources
|
||||||
no_enemies_players = [player for player in multiworld.get_game_players("Final Fantasy Mystic Quest")
|
no_enemies_players = [player for player in multiworld.get_game_players("Final Fantasy Mystic Quest")
|
||||||
if multiworld.enemies_density[player] == "none"]
|
if multiworld.worlds[player].options.enemies_density == "none"]
|
||||||
if (len([item for item in multiworld.itempool if item.classification in (ItemClassification.filler,
|
if (len([item for item in multiworld.itempool if item.classification in (ItemClassification.filler,
|
||||||
ItemClassification.trap)]) > len([player for player in no_enemies_players if
|
ItemClassification.trap)]) > len([player for player in no_enemies_players if
|
||||||
multiworld.accessibility[player] == "minimal"]) * 3):
|
multiworld.worlds[player].options.accessibility == "minimal"]) * 3):
|
||||||
for player in no_enemies_players:
|
for player in no_enemies_players:
|
||||||
for location in vendor_locations:
|
for location in vendor_locations:
|
||||||
if multiworld.accessibility[player] == "locations":
|
if multiworld.worlds[player].options.accessibility == "locations":
|
||||||
multiworld.get_location(location, player).progress_type = LocationProgressType.EXCLUDED
|
multiworld.get_location(location, player).progress_type = LocationProgressType.EXCLUDED
|
||||||
else:
|
else:
|
||||||
multiworld.get_location(location, player).access_rule = lambda state: False
|
multiworld.get_location(location, player).access_rule = lambda state: False
|
||||||
else:
|
else:
|
||||||
# There are not enough junk items to fill non-minimal players' vendors. Just set an item rule not allowing
|
# There are not enough junk items to fill non-minimal players' vendors. Just set an item rule not allowing
|
||||||
# advancement items so that useful items can be placed
|
# advancement items so that useful items can be placed.
|
||||||
for player in no_enemies_players:
|
for player in no_enemies_players:
|
||||||
for location in vendor_locations:
|
for location in vendor_locations:
|
||||||
multiworld.get_location(location, player).item_rule = lambda item: not item.advancement
|
multiworld.get_location(location, player).item_rule = lambda item: not item.advancement
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FFMQLocation(Location):
|
class FFMQLocation(Location):
|
||||||
game = "Final Fantasy Mystic Quest"
|
game = "Final Fantasy Mystic Quest"
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ from .Regions import create_regions, location_table, set_rules, stage_set_rules,
|
||||||
non_dead_end_crest_warps
|
non_dead_end_crest_warps
|
||||||
from .Items import item_table, item_groups, create_items, FFMQItem, fillers
|
from .Items import item_table, item_groups, create_items, FFMQItem, fillers
|
||||||
from .Output import generate_output
|
from .Output import generate_output
|
||||||
from .Options import option_definitions
|
from .Options import FFMQOptions
|
||||||
from .Client import FFMQClient
|
from .Client import FFMQClient
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,7 +45,8 @@ class FFMQWorld(World):
|
||||||
|
|
||||||
item_name_to_id = {name: data.id for name, data in item_table.items() if data.id is not None}
|
item_name_to_id = {name: data.id for name, data in item_table.items() if data.id is not None}
|
||||||
location_name_to_id = location_table
|
location_name_to_id = location_table
|
||||||
option_definitions = option_definitions
|
options_dataclass = FFMQOptions
|
||||||
|
options: FFMQOptions
|
||||||
|
|
||||||
topology_present = True
|
topology_present = True
|
||||||
|
|
||||||
|
@ -67,20 +68,14 @@ class FFMQWorld(World):
|
||||||
super().__init__(world, player)
|
super().__init__(world, player)
|
||||||
|
|
||||||
def generate_early(self):
|
def generate_early(self):
|
||||||
if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin":
|
if self.options.sky_coin_mode == "shattered_sky_coin":
|
||||||
self.multiworld.brown_boxes[self.player].value = 1
|
self.options.brown_boxes.value = 1
|
||||||
if self.multiworld.enemies_scaling_lower[self.player].value > \
|
if self.options.enemies_scaling_lower.value > self.options.enemies_scaling_upper.value:
|
||||||
self.multiworld.enemies_scaling_upper[self.player].value:
|
self.options.enemies_scaling_lower.value, self.options.enemies_scaling_upper.value = \
|
||||||
(self.multiworld.enemies_scaling_lower[self.player].value,
|
self.options.enemies_scaling_upper.value, self.options.enemies_scaling_lower.value
|
||||||
self.multiworld.enemies_scaling_upper[self.player].value) =\
|
if self.options.bosses_scaling_lower.value > self.options.bosses_scaling_upper.value:
|
||||||
(self.multiworld.enemies_scaling_upper[self.player].value,
|
self.options.bosses_scaling_lower.value, self.options.bosses_scaling_upper.value = \
|
||||||
self.multiworld.enemies_scaling_lower[self.player].value)
|
self.options.bosses_scaling_upper.value, self.options.bosses_scaling_lower.value
|
||||||
if self.multiworld.bosses_scaling_lower[self.player].value > \
|
|
||||||
self.multiworld.bosses_scaling_upper[self.player].value:
|
|
||||||
(self.multiworld.bosses_scaling_lower[self.player].value,
|
|
||||||
self.multiworld.bosses_scaling_upper[self.player].value) =\
|
|
||||||
(self.multiworld.bosses_scaling_upper[self.player].value,
|
|
||||||
self.multiworld.bosses_scaling_lower[self.player].value)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def stage_generate_early(cls, multiworld):
|
def stage_generate_early(cls, multiworld):
|
||||||
|
@ -94,20 +89,20 @@ class FFMQWorld(World):
|
||||||
rooms_data = {}
|
rooms_data = {}
|
||||||
|
|
||||||
for world in multiworld.get_game_worlds("Final Fantasy Mystic Quest"):
|
for world in multiworld.get_game_worlds("Final Fantasy Mystic Quest"):
|
||||||
if (world.multiworld.map_shuffle[world.player] or world.multiworld.crest_shuffle[world.player] or
|
if (world.options.map_shuffle or world.options.crest_shuffle or world.options.shuffle_battlefield_rewards
|
||||||
world.multiworld.crest_shuffle[world.player]):
|
or world.options.companions_locations):
|
||||||
if world.multiworld.map_shuffle_seed[world.player].value.isdigit():
|
if world.options.map_shuffle_seed.value.isdigit():
|
||||||
multiworld.random.seed(int(world.multiworld.map_shuffle_seed[world.player].value))
|
multiworld.random.seed(int(world.options.map_shuffle_seed.value))
|
||||||
elif world.multiworld.map_shuffle_seed[world.player].value != "random":
|
elif world.options.map_shuffle_seed.value != "random":
|
||||||
multiworld.random.seed(int(hash(world.multiworld.map_shuffle_seed[world.player].value))
|
multiworld.random.seed(int(hash(world.options.map_shuffle_seed.value))
|
||||||
+ int(world.multiworld.seed))
|
+ int(world.multiworld.seed))
|
||||||
|
|
||||||
seed = hex(multiworld.random.randint(0, 0xFFFFFFFF)).split("0x")[1].upper()
|
seed = hex(multiworld.random.randint(0, 0xFFFFFFFF)).split("0x")[1].upper()
|
||||||
map_shuffle = multiworld.map_shuffle[world.player].value
|
map_shuffle = world.options.map_shuffle.value
|
||||||
crest_shuffle = multiworld.crest_shuffle[world.player].current_key
|
crest_shuffle = world.options.crest_shuffle.current_key
|
||||||
battlefield_shuffle = multiworld.shuffle_battlefield_rewards[world.player].current_key
|
battlefield_shuffle = world.options.shuffle_battlefield_rewards.current_key
|
||||||
companion_shuffle = multiworld.companions_locations[world.player].value
|
companion_shuffle = world.options.companions_locations.value
|
||||||
kaeli_mom = multiworld.kaelis_mom_fight_minotaur[world.player].current_key
|
kaeli_mom = world.options.kaelis_mom_fight_minotaur.current_key
|
||||||
|
|
||||||
query = f"s={seed}&m={map_shuffle}&c={crest_shuffle}&b={battlefield_shuffle}&cs={companion_shuffle}&km={kaeli_mom}"
|
query = f"s={seed}&m={map_shuffle}&c={crest_shuffle}&b={battlefield_shuffle}&cs={companion_shuffle}&km={kaeli_mom}"
|
||||||
|
|
||||||
|
@ -175,14 +170,14 @@ class FFMQWorld(World):
|
||||||
|
|
||||||
def extend_hint_information(self, hint_data):
|
def extend_hint_information(self, hint_data):
|
||||||
hint_data[self.player] = {}
|
hint_data[self.player] = {}
|
||||||
if self.multiworld.map_shuffle[self.player]:
|
if self.options.map_shuffle:
|
||||||
single_location_regions = ["Subregion Volcano Battlefield", "Subregion Mac's Ship", "Subregion Doom Castle"]
|
single_location_regions = ["Subregion Volcano Battlefield", "Subregion Mac's Ship", "Subregion Doom Castle"]
|
||||||
for subregion in ["Subregion Foresta", "Subregion Aquaria", "Subregion Frozen Fields", "Subregion Fireburg",
|
for subregion in ["Subregion Foresta", "Subregion Aquaria", "Subregion Frozen Fields", "Subregion Fireburg",
|
||||||
"Subregion Volcano Battlefield", "Subregion Windia", "Subregion Mac's Ship",
|
"Subregion Volcano Battlefield", "Subregion Windia", "Subregion Mac's Ship",
|
||||||
"Subregion Doom Castle"]:
|
"Subregion Doom Castle"]:
|
||||||
region = self.multiworld.get_region(subregion, self.player)
|
region = self.multiworld.get_region(subregion, self.player)
|
||||||
for location in region.locations:
|
for location in region.locations:
|
||||||
if location.address and self.multiworld.map_shuffle[self.player] != "dungeons":
|
if location.address and self.options.map_shuffle != "dungeons":
|
||||||
hint_data[self.player][location.address] = (subregion.split("Subregion ")[-1]
|
hint_data[self.player][location.address] = (subregion.split("Subregion ")[-1]
|
||||||
+ (" Region" if subregion not in
|
+ (" Region" if subregion not in
|
||||||
single_location_regions else ""))
|
single_location_regions else ""))
|
||||||
|
@ -202,14 +197,13 @@ class FFMQWorld(World):
|
||||||
for location in exit_check.connected_region.locations:
|
for location in exit_check.connected_region.locations:
|
||||||
if location.address:
|
if location.address:
|
||||||
hint = []
|
hint = []
|
||||||
if self.multiworld.map_shuffle[self.player] != "dungeons":
|
if self.options.map_shuffle != "dungeons":
|
||||||
hint.append((subregion.split("Subregion ")[-1] + (" Region" if subregion not
|
hint.append((subregion.split("Subregion ")[-1] + (" Region" if subregion not
|
||||||
in single_location_regions else "")))
|
in single_location_regions else "")))
|
||||||
if self.multiworld.map_shuffle[self.player] != "overworld" and subregion not in \
|
if self.options.map_shuffle != "overworld":
|
||||||
("Subregion Mac's Ship", "Subregion Doom Castle"):
|
|
||||||
hint.append(overworld_spot.name.split("Overworld - ")[-1].replace("Pazuzu",
|
hint.append(overworld_spot.name.split("Overworld - ")[-1].replace("Pazuzu",
|
||||||
"Pazuzu's"))
|
"Pazuzu's"))
|
||||||
hint = " - ".join(hint)
|
hint = " - ".join(hint).replace(" - Mac Ship", "")
|
||||||
if location.address in hint_data[self.player]:
|
if location.address in hint_data[self.player]:
|
||||||
hint_data[self.player][location.address] += f"/{hint}"
|
hint_data[self.player][location.address] += f"/{hint}"
|
||||||
else:
|
else:
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue