mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00

FFMQR by @wildham0 Uses an API created by wildham for Map Shuffle, Crest Shuffle and Battlefield Reward Shuffle, using a similar method of obtaining data from an external website to Super Metroid's Varia Preset option. Generates a .apmq file which the user must bring to the FFMQR website https://www.ffmqrando.net/Archipelago to patch their rom. It is not an actual patch file but contains item placement and options data for the FFMQR website to generate a patched rom with for AP. Some of the AP options may seem unusual, using Choice instead of Range where it may seem more appropriate, but these are options that are passed to FFMQR and I can only be as flexible as it is. @wildham0 deserves the bulk of the credit for not only creating FFMQR in the first place but all the ASM work on the rom needed to make this possible, work on FFMQR to allow patching with the .apmq files, and creating the API that meant I did not have to recreate his map shuffle from scratch.
297 lines
17 KiB
Python
297 lines
17 KiB
Python
from BaseClasses import ItemClassification, Item
|
|
|
|
fillers = {"Cure Potion": 61, "Heal Potion": 52, "Refresher": 17, "Seed": 2, "Bomb Refill": 19,
|
|
"Projectile Refill": 50}
|
|
|
|
|
|
class ItemData:
|
|
def __init__(self, item_id, classification, groups=(), data_name=None):
|
|
self.groups = groups
|
|
self.classification = classification
|
|
self.id = None
|
|
if item_id is not None:
|
|
self.id = item_id + 0x420000
|
|
self.data_name = data_name
|
|
|
|
|
|
item_table = {
|
|
"Elixir": ItemData(0, ItemClassification.progression, ["Key Items"]),
|
|
"Tree Wither": ItemData(1, ItemClassification.progression, ["Key Items"]),
|
|
"Wakewater": ItemData(2, ItemClassification.progression, ["Key Items"]),
|
|
"Venus Key": ItemData(3, ItemClassification.progression, ["Key Items"]),
|
|
"Multi Key": ItemData(4, ItemClassification.progression, ["Key Items"]),
|
|
"Mask": ItemData(5, ItemClassification.progression, ["Key Items"]),
|
|
"Magic Mirror": ItemData(6, ItemClassification.progression, ["Key Items"]),
|
|
"Thunder Rock": ItemData(7, ItemClassification.progression, ["Key Items"]),
|
|
"Captain's Cap": ItemData(8, ItemClassification.progression_skip_balancing, ["Key Items"]),
|
|
"Libra Crest": ItemData(9, ItemClassification.progression, ["Key Items"]),
|
|
"Gemini Crest": ItemData(10, ItemClassification.progression, ["Key Items"]),
|
|
"Mobius Crest": ItemData(11, ItemClassification.progression, ["Key Items"]),
|
|
"Sand Coin": ItemData(12, ItemClassification.progression, ["Key Items", "Coins"]),
|
|
"River Coin": ItemData(13, ItemClassification.progression, ["Key Items", "Coins"]),
|
|
"Sun Coin": ItemData(14, ItemClassification.progression, ["Key Items", "Coins"]),
|
|
"Sky Coin": ItemData(15, ItemClassification.progression_skip_balancing, ["Key Items", "Coins"]),
|
|
"Sky Fragment": ItemData(15 + 256, ItemClassification.progression_skip_balancing, ["Key Items"]),
|
|
"Cure Potion": ItemData(16, ItemClassification.filler, ["Consumables"]),
|
|
"Heal Potion": ItemData(17, ItemClassification.filler, ["Consumables"]),
|
|
"Seed": ItemData(18, ItemClassification.filler, ["Consumables"]),
|
|
"Refresher": ItemData(19, ItemClassification.filler, ["Consumables"]),
|
|
"Exit Book": ItemData(20, ItemClassification.useful, ["Spells"]),
|
|
"Cure Book": ItemData(21, ItemClassification.useful, ["Spells"]),
|
|
"Heal Book": ItemData(22, ItemClassification.useful, ["Spells"]),
|
|
"Life Book": ItemData(23, ItemClassification.useful, ["Spells"]),
|
|
"Quake Book": ItemData(24, ItemClassification.useful, ["Spells"]),
|
|
"Blizzard Book": ItemData(25, ItemClassification.useful, ["Spells"]),
|
|
"Fire Book": ItemData(26, ItemClassification.useful, ["Spells"]),
|
|
"Aero Book": ItemData(27, ItemClassification.useful, ["Spells"]),
|
|
"Thunder Seal": ItemData(28, ItemClassification.useful, ["Spells"]),
|
|
"White Seal": ItemData(29, ItemClassification.useful, ["Spells"]),
|
|
"Meteor Seal": ItemData(30, ItemClassification.useful, ["Spells"]),
|
|
"Flare Seal": ItemData(31, ItemClassification.useful, ["Spells"]),
|
|
"Progressive Sword": ItemData(32 + 256, ItemClassification.progression, ["Weapons", "Swords"]),
|
|
"Steel Sword": ItemData(32, ItemClassification.progression, ["Weapons", "Swords"]),
|
|
"Knight Sword": ItemData(33, ItemClassification.progression_skip_balancing, ["Weapons", "Swords"]),
|
|
"Excalibur": ItemData(34, ItemClassification.progression_skip_balancing, ["Weapons", "Swords"]),
|
|
"Progressive Axe": ItemData(35 + 256, ItemClassification.progression, ["Weapons", "Axes"]),
|
|
"Axe": ItemData(35, ItemClassification.progression, ["Weapons", "Axes"]),
|
|
"Battle Axe": ItemData(36, ItemClassification.progression_skip_balancing, ["Weapons", "Axes"]),
|
|
"Giant's Axe": ItemData(37, ItemClassification.progression_skip_balancing, ["Weapons", "Axes"]),
|
|
"Progressive Claw": ItemData(38 + 256, ItemClassification.progression, ["Weapons", "Axes"]),
|
|
"Cat Claw": ItemData(38, ItemClassification.progression, ["Weapons", "Claws"]),
|
|
"Charm Claw": ItemData(39, ItemClassification.progression_skip_balancing, ["Weapons", "Claws"]),
|
|
"Dragon Claw": ItemData(40, ItemClassification.progression, ["Weapons", "Claws"]),
|
|
"Progressive Bomb": ItemData(41 + 256, ItemClassification.progression, ["Weapons", "Bombs"]),
|
|
"Bomb": ItemData(41, ItemClassification.progression, ["Weapons", "Bombs"]),
|
|
"Jumbo Bomb": ItemData(42, ItemClassification.progression_skip_balancing, ["Weapons", "Bombs"]),
|
|
"Mega Grenade": ItemData(43, ItemClassification.progression, ["Weapons", "Bombs"]),
|
|
# Ally-only equipment does nothing when received, no reason to put them in the datapackage
|
|
#"Morning Star": ItemData(44, ItemClassification.progression, ["Weapons"]),
|
|
#"Bow Of Grace": ItemData(45, ItemClassification.progression, ["Weapons"]),
|
|
#"Ninja Star": ItemData(46, ItemClassification.progression, ["Weapons"]),
|
|
|
|
"Progressive Helm": ItemData(47 + 256, ItemClassification.useful, ["Helms"]),
|
|
"Steel Helm": ItemData(47, ItemClassification.useful, ["Helms"]),
|
|
"Moon Helm": ItemData(48, ItemClassification.useful, ["Helms"]),
|
|
"Apollo Helm": ItemData(49, ItemClassification.useful, ["Helms"]),
|
|
"Progressive Armor": ItemData(50 + 256, ItemClassification.useful, ["Armors"]),
|
|
"Steel Armor": ItemData(50, ItemClassification.useful, ["Armors"]),
|
|
"Noble Armor": ItemData(51, ItemClassification.useful, ["Armors"]),
|
|
"Gaia's Armor": ItemData(52, ItemClassification.useful, ["Armors"]),
|
|
#"Replica Armor": ItemData(53, ItemClassification.progression, ["Armors"]),
|
|
#"Mystic Robes": ItemData(54, ItemClassification.progression, ["Armors"]),
|
|
#"Flame Armor": ItemData(55, ItemClassification.progression, ["Armors"]),
|
|
#"Black Robe": ItemData(56, ItemClassification.progression, ["Armors"]),
|
|
"Progressive Shield": ItemData(57 + 256, ItemClassification.useful, ["Shields"]),
|
|
"Steel Shield": ItemData(57, ItemClassification.useful, ["Shields"]),
|
|
"Venus Shield": ItemData(58, ItemClassification.useful, ["Shields"]),
|
|
"Aegis Shield": ItemData(59, ItemClassification.useful, ["Shields"]),
|
|
#"Ether Shield": ItemData(60, ItemClassification.progression, ["Shields"]),
|
|
"Progressive Accessory": ItemData(61 + 256, ItemClassification.useful, ["Accessories"]),
|
|
"Charm": ItemData(61, ItemClassification.useful, ["Accessories"]),
|
|
"Magic Ring": ItemData(62, ItemClassification.useful, ["Accessories"]),
|
|
"Cupid Locket": ItemData(63, ItemClassification.useful, ["Accessories"]),
|
|
|
|
# these are understood by FFMQR and I could place these if I want, but it's easier to just let FFMQR
|
|
# place them. I want an option to make shuffle battlefield rewards NOT color-code the battlefields,
|
|
# and then I would make the non-item reward battlefields into AP checks and these would be put into those as
|
|
# the item for AP. But there is no such option right now.
|
|
# "54 XP": ItemData(96, ItemClassification.filler, data_name="Xp54"),
|
|
# "99 XP": ItemData(97, ItemClassification.filler, data_name="Xp99"),
|
|
# "540 XP": ItemData(98, ItemClassification.filler, data_name="Xp540"),
|
|
# "744 XP": ItemData(99, ItemClassification.filler, data_name="Xp744"),
|
|
# "816 XP": ItemData(100, ItemClassification.filler, data_name="Xp816"),
|
|
# "1068 XP": ItemData(101, ItemClassification.filler, data_name="Xp1068"),
|
|
# "1200 XP": ItemData(102, ItemClassification.filler, data_name="Xp1200"),
|
|
# "2700 XP": ItemData(103, ItemClassification.filler, data_name="Xp2700"),
|
|
# "2808 XP": ItemData(104, ItemClassification.filler, data_name="Xp2808"),
|
|
# "150 Gp": ItemData(105, ItemClassification.filler, data_name="Gp150"),
|
|
# "300 Gp": ItemData(106, ItemClassification.filler, data_name="Gp300"),
|
|
# "600 Gp": ItemData(107, ItemClassification.filler, data_name="Gp600"),
|
|
# "900 Gp": ItemData(108, ItemClassification.filler, data_name="Gp900"),
|
|
# "1200 Gp": ItemData(109, ItemClassification.filler, data_name="Gp1200"),
|
|
|
|
|
|
"Bomb Refill": ItemData(221, ItemClassification.filler, ["Refills"]),
|
|
"Projectile Refill": ItemData(222, ItemClassification.filler, ["Refills"]),
|
|
#"None": ItemData(255, ItemClassification.progression, []),
|
|
|
|
"Kaeli 1": ItemData(None, ItemClassification.progression),
|
|
"Kaeli 2": ItemData(None, ItemClassification.progression),
|
|
"Tristam": ItemData(None, ItemClassification.progression),
|
|
"Phoebe 1": ItemData(None, ItemClassification.progression),
|
|
"Reuben 1": ItemData(None, ItemClassification.progression),
|
|
"Reuben Dad Saved": ItemData(None, ItemClassification.progression),
|
|
"Otto": ItemData(None, ItemClassification.progression),
|
|
"Captain Mac": ItemData(None, ItemClassification.progression),
|
|
"Ship Steering Wheel": ItemData(None, ItemClassification.progression),
|
|
"Minotaur": ItemData(None, ItemClassification.progression),
|
|
"Flamerus Rex": ItemData(None, ItemClassification.progression),
|
|
"Phanquid": ItemData(None, ItemClassification.progression),
|
|
"Freezer Crab": ItemData(None, ItemClassification.progression),
|
|
"Ice Golem": ItemData(None, ItemClassification.progression),
|
|
"Jinn": ItemData(None, ItemClassification.progression),
|
|
"Medusa": ItemData(None, ItemClassification.progression),
|
|
"Dualhead Hydra": ItemData(None, ItemClassification.progression),
|
|
"Gidrah": ItemData(None, ItemClassification.progression),
|
|
"Dullahan": ItemData(None, ItemClassification.progression),
|
|
"Pazuzu": ItemData(None, ItemClassification.progression),
|
|
"Aquaria Plaza": ItemData(None, ItemClassification.progression),
|
|
"Summer Aquaria": ItemData(None, ItemClassification.progression),
|
|
"Reuben Mine": ItemData(None, ItemClassification.progression),
|
|
"Alive Forest": ItemData(None, ItemClassification.progression),
|
|
"Rainbow Bridge": ItemData(None, ItemClassification.progression),
|
|
"Collapse Spencer's Cave": ItemData(None, ItemClassification.progression),
|
|
"Ship Liberated": ItemData(None, ItemClassification.progression),
|
|
"Ship Loaned": ItemData(None, ItemClassification.progression),
|
|
"Ship Dock Access": ItemData(None, ItemClassification.progression),
|
|
"Stone Golem": ItemData(None, ItemClassification.progression),
|
|
"Twinhead Wyvern": ItemData(None, ItemClassification.progression),
|
|
"Zuh": ItemData(None, ItemClassification.progression),
|
|
|
|
"Libra Temple Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Life Temple Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Aquaria Vendor Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Fireburg Vendor Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Fireburg Grenademan Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Sealed Temple Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Wintry Temple Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Kaidge Temple Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Light Temple Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Windia Kids Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Windia Dock Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Ship Dock Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Alive Forest Libra Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Alive Forest Gemini Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Alive Forest Mobius Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Wood House Libra Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Wood House Gemini Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Wood House Mobius Crest Tile": ItemData(None, ItemClassification.progression),
|
|
"Barrel Pushed": ItemData(None, ItemClassification.progression),
|
|
"Long Spine Bombed": ItemData(None, ItemClassification.progression),
|
|
"Short Spine Bombed": ItemData(None, ItemClassification.progression),
|
|
"Skull 1 Bombed": ItemData(None, ItemClassification.progression),
|
|
"Skull 2 Bombed": ItemData(None, ItemClassification.progression),
|
|
"Ice Pyramid 1F Statue": ItemData(None, ItemClassification.progression),
|
|
"Ice Pyramid 3F Statue": ItemData(None, ItemClassification.progression),
|
|
"Ice Pyramid 4F Statue": ItemData(None, ItemClassification.progression),
|
|
"Ice Pyramid 5F Statue": ItemData(None, ItemClassification.progression),
|
|
"Spencer Cave Libra Block Bombed": ItemData(None, ItemClassification.progression),
|
|
"Lava Dome Plate": ItemData(None, ItemClassification.progression),
|
|
"Pazuzu 2F Lock": ItemData(None, ItemClassification.progression),
|
|
"Pazuzu 4F Lock": ItemData(None, ItemClassification.progression),
|
|
"Pazuzu 6F Lock": ItemData(None, ItemClassification.progression),
|
|
"Pazuzu 1F": ItemData(None, ItemClassification.progression),
|
|
"Pazuzu 2F": ItemData(None, ItemClassification.progression),
|
|
"Pazuzu 3F": ItemData(None, ItemClassification.progression),
|
|
"Pazuzu 4F": ItemData(None, ItemClassification.progression),
|
|
"Pazuzu 5F": ItemData(None, ItemClassification.progression),
|
|
"Pazuzu 6F": ItemData(None, ItemClassification.progression),
|
|
"Dark King": ItemData(None, ItemClassification.progression),
|
|
#"Barred": ItemData(None, ItemClassification.progression),
|
|
|
|
}
|
|
|
|
prog_map = {
|
|
"Swords": "Progressive Sword",
|
|
"Axes": "Progressive Axe",
|
|
"Claws": "Progressive Claw",
|
|
"Bombs": "Progressive Bomb",
|
|
"Shields": "Progressive Shield",
|
|
"Armors": "Progressive Armor",
|
|
"Helms": "Progressive Helm",
|
|
"Accessories": "Progressive Accessory",
|
|
}
|
|
|
|
|
|
def yaml_item(text):
|
|
if text == "CaptainCap":
|
|
return "Captain's Cap"
|
|
elif text == "WakeWater":
|
|
return "Wakewater"
|
|
return "".join(
|
|
[(" " + c if (c.isupper() or c.isnumeric()) and not (text[i - 1].isnumeric() and c == "F") else c) for
|
|
i, c in enumerate(text)]).strip()
|
|
|
|
|
|
item_groups = {}
|
|
for item, data in item_table.items():
|
|
for group in data.groups:
|
|
item_groups[group] = item_groups.get(group, []) + [item]
|
|
|
|
|
|
def create_items(self) -> None:
|
|
items = []
|
|
starting_weapon = self.multiworld.starting_weapon[self.player].current_key.title().replace("_", " ")
|
|
if self.multiworld.progressive_gear[self.player]:
|
|
for item_group in prog_map:
|
|
if starting_weapon in self.item_name_groups[item_group]:
|
|
starting_weapon = prog_map[item_group]
|
|
break
|
|
self.multiworld.push_precollected(self.create_item(starting_weapon))
|
|
self.multiworld.push_precollected(self.create_item("Steel Armor"))
|
|
if self.multiworld.sky_coin_mode[self.player] == "start_with":
|
|
self.multiworld.push_precollected(self.create_item("Sky Coin"))
|
|
|
|
precollected_item_names = {item.name for item in self.multiworld.precollected_items[self.player]}
|
|
|
|
def add_item(item_name):
|
|
if item_name in ["Steel Armor", "Sky Fragment"] or "Progressive" in item_name:
|
|
return
|
|
if item_name.lower().replace(" ", "_") == self.multiworld.starting_weapon[self.player].current_key:
|
|
return
|
|
if self.multiworld.progressive_gear[self.player]:
|
|
for item_group in prog_map:
|
|
if item_name in self.item_name_groups[item_group]:
|
|
item_name = prog_map[item_group]
|
|
break
|
|
if item_name == "Sky Coin":
|
|
if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin":
|
|
for _ in range(40):
|
|
items.append(self.create_item("Sky Fragment"))
|
|
return
|
|
elif self.multiworld.sky_coin_mode[self.player] == "save_the_crystals":
|
|
items.append(self.create_filler())
|
|
return
|
|
if item_name in precollected_item_names:
|
|
items.append(self.create_filler())
|
|
return
|
|
i = self.create_item(item_name)
|
|
if self.multiworld.logic[self.player] != "friendly" and item_name in ("Magic Mirror", "Mask"):
|
|
i.classification = ItemClassification.useful
|
|
if (self.multiworld.logic[self.player] == "expert" and self.multiworld.map_shuffle[self.player] == "none" and
|
|
item_name == "Exit Book"):
|
|
i.classification = ItemClassification.progression
|
|
items.append(i)
|
|
|
|
for item_group in ("Key Items", "Spells", "Armors", "Helms", "Shields", "Accessories", "Weapons"):
|
|
for item in self.item_name_groups[item_group]:
|
|
add_item(item)
|
|
|
|
if self.multiworld.brown_boxes[self.player] == "include":
|
|
filler_items = []
|
|
for item, count in fillers.items():
|
|
filler_items += [self.create_item(item) for _ in range(count)]
|
|
if self.multiworld.sky_coin_mode[self.player] == "shattered_sky_coin":
|
|
self.multiworld.random.shuffle(filler_items)
|
|
filler_items = filler_items[39:]
|
|
items += filler_items
|
|
|
|
self.multiworld.itempool += items
|
|
|
|
if len(self.multiworld.player_ids) > 1:
|
|
early_choices = ["Sand Coin", "River Coin"]
|
|
early_item = self.multiworld.random.choice(early_choices)
|
|
self.multiworld.early_items[self.player][early_item] = 1
|
|
|
|
|
|
class FFMQItem(Item):
|
|
game = "Final Fantasy Mystic Quest"
|
|
type = None
|
|
|
|
def __init__(self, name, player: int = None):
|
|
item_data = item_table[name]
|
|
super(FFMQItem, self).__init__(
|
|
name,
|
|
item_data.classification,
|
|
item_data.id, player
|
|
) |