diff --git a/test/general/test_items.py b/test/general/test_items.py
index 25623d4d..7c0b7050 100644
--- a/test/general/test_items.py
+++ b/test/general/test_items.py
@@ -25,6 +25,8 @@ class TestBase(unittest.TestCase):
{"medallions", "stones", "rewards", "logic_bottles"},
"Starcraft 2":
{"Missions", "WoL Missions"},
+ "Yu-Gi-Oh! 2006":
+ {"Campaign Boss Beaten"}
}
for game_name, world_type in AutoWorldRegister.world_types.items():
with self.subTest(game_name, game_name=game_name):
diff --git a/worlds/yugioh06/__init__.py b/worlds/yugioh06/__init__.py
new file mode 100644
index 00000000..ec7e769f
--- /dev/null
+++ b/worlds/yugioh06/__init__.py
@@ -0,0 +1,454 @@
+import os
+import pkgutil
+from typing import Any, ClassVar, Dict, List
+
+import settings
+from BaseClasses import Entrance, Item, ItemClassification, Location, MultiWorld, Region, Tutorial
+
+import Utils
+from worlds.AutoWorld import WebWorld, World
+
+from .boosterpacks import booster_contents as booster_contents
+from .boosterpacks import get_booster_locations
+from .items import (
+ Banlist_Items,
+ booster_packs,
+ draft_boosters,
+ draft_opponents,
+ excluded_items,
+ item_to_index,
+ tier_1_opponents,
+ useful,
+)
+from .items import (
+ challenges as challenges,
+)
+from .locations import (
+ Bonuses,
+ Campaign_Opponents,
+ Limited_Duels,
+ Required_Cards,
+ Theme_Duels,
+ collection_events,
+ get_beat_challenge_events,
+ special,
+)
+from .logic import core_booster, yugioh06_difficulty
+from .opponents import OpponentData, get_opponent_condition, get_opponent_locations, get_opponents
+from .opponents import challenge_opponents as challenge_opponents
+from .options import Yugioh06Options
+from .rom import MD5America, MD5Europe, YGO06ProcedurePatch, write_tokens
+from .rom import get_base_rom_path as get_base_rom_path
+from .rom_values import banlist_ids as banlist_ids
+from .rom_values import function_addresses as function_addresses
+from .rom_values import structure_deck_selection as structure_deck_selection
+from .rules import set_rules
+from .structure_deck import get_deck_content_locations
+from .client_bh import YuGiOh2006Client
+
+
+class Yugioh06Web(WebWorld):
+ theme = "stone"
+ setup = Tutorial(
+ "Multiworld Setup Tutorial",
+ "A guide to setting up Yu-Gi-Oh! - Ultimate Masters Edition - World Championship Tournament 2006 "
+ "for Archipelago on your computer.",
+ "English",
+ "docs/setup_en.md",
+ "setup/en",
+ ["Rensen"],
+ )
+ tutorials = [setup]
+
+
+class Yugioh2006Setting(settings.Group):
+ class Yugioh2006RomFile(settings.UserFilePath):
+ """File name of your Yu-Gi-Oh 2006 ROM"""
+
+ description = "Yu-Gi-Oh 2006 ROM File"
+ copy_to = "YuGiOh06.gba"
+ md5s = [MD5Europe, MD5America]
+
+ rom_file: Yugioh2006RomFile = Yugioh2006RomFile(Yugioh2006RomFile.copy_to)
+
+
+class Yugioh06World(World):
+ """
+ Yu-Gi-Oh! Ultimate Masters: World Championship Tournament 2006 is the definitive Yu-Gi-Oh
+ simulator on the GBA. Featuring over 2000 cards and over 90 Challenges.
+ """
+
+ game = "Yu-Gi-Oh! 2006"
+ web = Yugioh06Web()
+ options: Yugioh06Options
+ options_dataclass = Yugioh06Options
+ settings_key = "yugioh06_settings"
+ settings: ClassVar[Yugioh2006Setting]
+
+ item_name_to_id = {}
+ start_id = 5730000
+ for k, v in item_to_index.items():
+ item_name_to_id[k] = v + start_id
+
+ location_name_to_id = {}
+ for k, v in Bonuses.items():
+ location_name_to_id[k] = v + start_id
+
+ for k, v in Limited_Duels.items():
+ location_name_to_id[k] = v + start_id
+
+ for k, v in Theme_Duels.items():
+ location_name_to_id[k] = v + start_id
+
+ for k, v in Campaign_Opponents.items():
+ location_name_to_id[k] = v + start_id
+
+ for k, v in special.items():
+ location_name_to_id[k] = v + start_id
+
+ for k, v in Required_Cards.items():
+ location_name_to_id[k] = v + start_id
+
+ item_name_groups = {
+ "Core Booster": core_booster,
+ "Campaign Boss Beaten": ["Tier 1 Beaten", "Tier 2 Beaten", "Tier 3 Beaten", "Tier 4 Beaten", "Tier 5 Beaten"],
+ }
+
+ removed_challenges: List[str]
+ starting_booster: str
+ starting_opponent: str
+ campaign_opponents: List[OpponentData]
+ is_draft_mode: bool
+
+ def __init__(self, world: MultiWorld, player: int):
+ super().__init__(world, player)
+
+ def generate_early(self):
+ self.starting_opponent = ""
+ self.starting_booster = ""
+ self.removed_challenges = []
+ # Universal tracker stuff, shouldn't do anything in standard gen
+ if hasattr(self.multiworld, "re_gen_passthrough"):
+ if "Yu-Gi-Oh! 2006" in self.multiworld.re_gen_passthrough:
+ # bypassing random yaml settings
+ slot_data = self.multiworld.re_gen_passthrough["Yu-Gi-Oh! 2006"]
+ self.options.structure_deck.value = slot_data["structure_deck"]
+ self.options.banlist.value = slot_data["banlist"]
+ self.options.final_campaign_boss_unlock_condition.value = slot_data[
+ "final_campaign_boss_unlock_condition"
+ ]
+ self.options.fourth_tier_5_campaign_boss_unlock_condition.value = slot_data[
+ "fourth_tier_5_campaign_boss_unlock_condition"
+ ]
+ self.options.third_tier_5_campaign_boss_unlock_condition.value = slot_data[
+ "third_tier_5_campaign_boss_unlock_condition"
+ ]
+ self.options.final_campaign_boss_challenges.value = slot_data["final_campaign_boss_challenges"]
+ self.options.fourth_tier_5_campaign_boss_challenges.value = slot_data[
+ "fourth_tier_5_campaign_boss_challenges"
+ ]
+ self.options.third_tier_5_campaign_boss_challenges.value = slot_data[
+ "third_tier_5_campaign_boss_challenges"
+ ]
+ self.options.final_campaign_boss_campaign_opponents.value = slot_data[
+ "final_campaign_boss_campaign_opponents"
+ ]
+ self.options.fourth_tier_5_campaign_boss_campaign_opponents.value = slot_data[
+ "fourth_tier_5_campaign_boss_campaign_opponents"
+ ]
+ self.options.third_tier_5_campaign_boss_campaign_opponents.value = slot_data[
+ "third_tier_5_campaign_boss_campaign_opponents"
+ ]
+ self.options.number_of_challenges.value = slot_data["number_of_challenges"]
+ self.removed_challenges = slot_data["removed challenges"]
+ self.starting_booster = slot_data["starting_booster"]
+ self.starting_opponent = slot_data["starting_opponent"]
+
+ if self.options.structure_deck.current_key == "none":
+ self.is_draft_mode = True
+ boosters = draft_boosters
+ if self.options.campaign_opponents_shuffle.value:
+ opponents = tier_1_opponents
+ else:
+ opponents = draft_opponents
+ else:
+ self.is_draft_mode = False
+ boosters = booster_packs
+ opponents = tier_1_opponents
+
+ if self.options.structure_deck.current_key == "random_deck":
+ self.options.structure_deck.value = self.random.randint(0, 5)
+ for item in self.options.start_inventory:
+ if item in opponents:
+ self.starting_opponent = item
+ if item in boosters:
+ self.starting_booster = item
+ if not self.starting_opponent:
+ self.starting_opponent = self.random.choice(opponents)
+ self.multiworld.push_precollected(self.create_item(self.starting_opponent))
+ if not self.starting_booster:
+ self.starting_booster = self.random.choice(boosters)
+ self.multiworld.push_precollected(self.create_item(self.starting_booster))
+ banlist = self.options.banlist.value
+ self.multiworld.push_precollected(self.create_item(Banlist_Items[banlist]))
+
+ if not self.removed_challenges:
+ challenge = list((Limited_Duels | Theme_Duels).keys())
+ noc = len(challenge) - max(
+ self.options.third_tier_5_campaign_boss_challenges.value
+ if self.options.third_tier_5_campaign_boss_unlock_condition == "challenges"
+ else 0,
+ self.options.fourth_tier_5_campaign_boss_challenges.value
+ if self.options.fourth_tier_5_campaign_boss_unlock_condition == "challenges"
+ else 0,
+ self.options.final_campaign_boss_challenges.value
+ if self.options.final_campaign_boss_unlock_condition == "challenges"
+ else 0,
+ self.options.number_of_challenges.value,
+ )
+
+ self.random.shuffle(challenge)
+ excluded = self.options.exclude_locations.value.intersection(challenge)
+ prio = self.options.priority_locations.value.intersection(challenge)
+ normal = [e for e in challenge if e not in excluded and e not in prio]
+ total = list(excluded) + normal + list(prio)
+ self.removed_challenges = total[:noc]
+
+ self.campaign_opponents = get_opponents(
+ self.multiworld, self.player, self.options.campaign_opponents_shuffle.value
+ )
+
+ def create_region(self, name: str, locations=None, exits=None):
+ region = Region(name, self.player, self.multiworld)
+ if locations:
+ for location_name, lid in locations.items():
+ if lid is not None and isinstance(lid, int):
+ lid = self.location_name_to_id[location_name]
+ else:
+ lid = None
+ location = Yugioh2006Location(self.player, location_name, lid, region)
+ region.locations.append(location)
+
+ if exits:
+ for _exit in exits:
+ region.exits.append(Entrance(self.player, _exit, region))
+ return region
+
+ def create_regions(self):
+ structure_deck = self.options.structure_deck.current_key
+ self.multiworld.regions += [
+ self.create_region("Menu", None, ["to Deck Edit", "to Campaign", "to Challenges", "to Card Shop"]),
+ self.create_region("Campaign", Bonuses | Campaign_Opponents),
+ self.create_region("Challenges"),
+ self.create_region("Card Shop", Required_Cards | collection_events),
+ self.create_region("Structure Deck", get_deck_content_locations(structure_deck)),
+ ]
+
+ self.get_entrance("to Campaign").connect(self.get_region("Campaign"))
+ self.get_entrance("to Challenges").connect(self.get_region("Challenges"))
+ self.get_entrance("to Card Shop").connect(self.get_region("Card Shop"))
+ self.get_entrance("to Deck Edit").connect(self.get_region("Structure Deck"))
+
+ campaign = self.get_region("Campaign")
+ # Campaign Opponents
+ for opponent in self.campaign_opponents:
+ unlock_item = "Campaign Tier " + str(opponent.tier) + " Column " + str(opponent.column)
+ region = self.create_region(opponent.name, get_opponent_locations(opponent))
+ entrance = Entrance(self.player, unlock_item, campaign)
+ if opponent.tier == 5 and opponent.column > 2:
+ unlock_amount = 0
+ is_challenge = True
+ if opponent.column == 3:
+ if self.options.third_tier_5_campaign_boss_unlock_condition.value == 1:
+ unlock_item = "Challenge Beaten"
+ unlock_amount = self.options.third_tier_5_campaign_boss_challenges.value
+ is_challenge = True
+ else:
+ unlock_item = "Campaign Boss Beaten"
+ unlock_amount = self.options.third_tier_5_campaign_boss_campaign_opponents.value
+ is_challenge = False
+ if opponent.column == 4:
+ if self.options.fourth_tier_5_campaign_boss_unlock_condition.value == 1:
+ unlock_item = "Challenge Beaten"
+ unlock_amount = self.options.fourth_tier_5_campaign_boss_challenges.value
+ is_challenge = True
+ else:
+ unlock_item = "Campaign Boss Beaten"
+ unlock_amount = self.options.fourth_tier_5_campaign_boss_campaign_opponents.value
+ is_challenge = False
+ if opponent.column == 5:
+ if self.options.final_campaign_boss_unlock_condition.value == 1:
+ unlock_item = "Challenge Beaten"
+ unlock_amount = self.options.final_campaign_boss_challenges.value
+ is_challenge = True
+ else:
+ unlock_item = "Campaign Boss Beaten"
+ unlock_amount = self.options.final_campaign_boss_campaign_opponents.value
+ is_challenge = False
+ entrance.access_rule = get_opponent_condition(
+ opponent, unlock_item, unlock_amount, self.player, is_challenge
+ )
+ else:
+ entrance.access_rule = lambda state, unlock=unlock_item, opp=opponent: state.has(
+ unlock, self.player
+ ) and yugioh06_difficulty(state, self.player, opp.difficulty)
+ campaign.exits.append(entrance)
+ entrance.connect(region)
+ self.multiworld.regions.append(region)
+
+ card_shop = self.get_region("Card Shop")
+ # Booster Contents
+ for booster in booster_packs:
+ region = self.create_region(booster, get_booster_locations(booster))
+ entrance = Entrance(self.player, booster, card_shop)
+ entrance.access_rule = lambda state, unlock=booster: state.has(unlock, self.player)
+ card_shop.exits.append(entrance)
+ entrance.connect(region)
+ self.multiworld.regions.append(region)
+
+ challenge_region = self.get_region("Challenges")
+ # Challenges
+ for challenge, lid in (Limited_Duels | Theme_Duels).items():
+ if challenge in self.removed_challenges:
+ continue
+ region = self.create_region(challenge, {challenge: lid, challenge + " Complete": None})
+ entrance = Entrance(self.player, challenge, challenge_region)
+ entrance.access_rule = lambda state, unlock=challenge: state.has(unlock + " Unlock", self.player)
+ challenge_region.exits.append(entrance)
+ entrance.connect(region)
+ self.multiworld.regions.append(region)
+
+ def create_item(self, name: str) -> Item:
+ classification: ItemClassification = ItemClassification.progression
+ if name == "5000DP":
+ classification = ItemClassification.filler
+ if name in useful:
+ classification = ItemClassification.useful
+ return Item(name, classification, self.item_name_to_id[name], self.player)
+
+ def create_filler(self) -> Item:
+ return self.create_item("5000DP")
+
+ def get_filler_item_name(self) -> str:
+ return "5000DP"
+
+ def create_items(self):
+ start_inventory = self.options.start_inventory.value.copy()
+ item_pool = []
+ items = item_to_index.copy()
+ starting_list = Banlist_Items[self.options.banlist.value]
+ if not self.options.add_empty_banlist.value and starting_list != "No Banlist":
+ items.pop("No Banlist")
+ for rc in self.removed_challenges:
+ items.pop(rc + " Unlock")
+ items.pop(self.starting_opponent)
+ items.pop(self.starting_booster)
+ items.pop(starting_list)
+ for name in items:
+ if name in excluded_items or name in start_inventory:
+ continue
+ item = self.create_item(name)
+ item_pool.append(item)
+
+ needed_item_pool_size = sum(loc not in self.removed_challenges for loc in self.location_name_to_id)
+ needed_filler_amount = needed_item_pool_size - len(item_pool)
+ item_pool += [self.create_item("5000DP") for _ in range(needed_filler_amount)]
+
+ self.multiworld.itempool += item_pool
+
+ for challenge in get_beat_challenge_events(self):
+ item = Yugioh2006Item("Challenge Beaten", ItemClassification.progression, None, self.player)
+ location = self.multiworld.get_location(challenge, self.player)
+ location.place_locked_item(item)
+
+ for opponent in self.campaign_opponents:
+ for location_name, event in get_opponent_locations(opponent).items():
+ if event is not None and not isinstance(event, int):
+ item = Yugioh2006Item(event, ItemClassification.progression, None, self.player)
+ location = self.multiworld.get_location(location_name, self.player)
+ location.place_locked_item(item)
+
+ for booster in booster_packs:
+ for location_name, content in get_booster_locations(booster).items():
+ item = Yugioh2006Item(content, ItemClassification.progression, None, self.player)
+ location = self.multiworld.get_location(location_name, self.player)
+ location.place_locked_item(item)
+
+ structure_deck = self.options.structure_deck.current_key
+ for location_name, content in get_deck_content_locations(structure_deck).items():
+ item = Yugioh2006Item(content, ItemClassification.progression, None, self.player)
+ location = self.multiworld.get_location(location_name, self.player)
+ location.place_locked_item(item)
+
+ for event in collection_events:
+ item = Yugioh2006Item(event, ItemClassification.progression, None, self.player)
+ location = self.multiworld.get_location(event, self.player)
+ location.place_locked_item(item)
+
+ def set_rules(self):
+ set_rules(self)
+
+ def generate_output(self, output_directory: str):
+ outfilepname = f"_P{self.player}"
+ outfilepname += f"_{self.multiworld.get_file_safe_player_name(self.player).replace(' ', '_')}"
+ self.rom_name_text = f'YGO06{Utils.__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed:11}\0'
+ self.romName = bytearray(self.rom_name_text, "utf8")[:0x20]
+ self.romName.extend([0] * (0x20 - len(self.romName)))
+ self.rom_name = self.romName
+ self.playerName = bytearray(self.multiworld.player_name[self.player], "utf8")[:0x20]
+ self.playerName.extend([0] * (0x20 - len(self.playerName)))
+ patch = YGO06ProcedurePatch(player=self.player, player_name=self.multiworld.player_name[self.player])
+ patch.write_file("base_patch.bsdiff4", pkgutil.get_data(__name__, "patch.bsdiff4"))
+ if self.is_draft_mode:
+ patch.procedure.insert(1, ("apply_bsdiff4", ["draft_patch.bsdiff4"]))
+ patch.write_file("draft_patch.bsdiff4", pkgutil.get_data(__name__, "patches/draft.bsdiff4"))
+ if self.options.ocg_arts:
+ patch.procedure.insert(1, ("apply_bsdiff4", ["ocg_patch.bsdiff4"]))
+ patch.write_file("ocg_patch.bsdiff4", pkgutil.get_data(__name__, "patches/ocg.bsdiff4"))
+ write_tokens(self, patch)
+
+ # Write Output
+ out_file_name = self.multiworld.get_out_file_name_base(self.player)
+ patch.write(os.path.join(output_directory, f"{out_file_name}{patch.patch_file_ending}"))
+
+ def fill_slot_data(self) -> Dict[str, Any]:
+ slot_data: Dict[str, Any] = {
+ "structure_deck": self.options.structure_deck.value,
+ "banlist": self.options.banlist.value,
+ "final_campaign_boss_unlock_condition": self.options.final_campaign_boss_unlock_condition.value,
+ "fourth_tier_5_campaign_boss_unlock_condition":
+ self.options.fourth_tier_5_campaign_boss_unlock_condition.value,
+ "third_tier_5_campaign_boss_unlock_condition":
+ self.options.third_tier_5_campaign_boss_unlock_condition.value,
+ "final_campaign_boss_challenges": self.options.final_campaign_boss_challenges.value,
+ "fourth_tier_5_campaign_boss_challenges":
+ self.options.fourth_tier_5_campaign_boss_challenges.value,
+ "third_tier_5_campaign_boss_challenges":
+ self.options.third_tier_5_campaign_boss_campaign_opponents.value,
+ "final_campaign_boss_campaign_opponents":
+ self.options.final_campaign_boss_campaign_opponents.value,
+ "fourth_tier_5_campaign_boss_campaign_opponents":
+ self.options.fourth_tier_5_campaign_boss_unlock_condition.value,
+ "third_tier_5_campaign_boss_campaign_opponents":
+ self.options.third_tier_5_campaign_boss_campaign_opponents.value,
+ "number_of_challenges": self.options.number_of_challenges.value,
+ }
+
+ slot_data["removed challenges"] = self.removed_challenges
+ slot_data["starting_booster"] = self.starting_booster
+ slot_data["starting_opponent"] = self.starting_opponent
+ return slot_data
+
+ # for the universal tracker, doesn't get called in standard gen
+ @staticmethod
+ def interpret_slot_data(slot_data: Dict[str, Any]) -> Dict[str, Any]:
+ # returning slot_data so it regens, giving it back in multiworld.re_gen_passthrough
+ return slot_data
+
+
+class Yugioh2006Item(Item):
+ game: str = "Yu-Gi-Oh! 2006"
+
+
+class Yugioh2006Location(Location):
+ game: str = "Yu-Gi-Oh! 2006"
diff --git a/worlds/yugioh06/boosterpacks.py b/worlds/yugioh06/boosterpacks.py
new file mode 100644
index 00000000..f6f4ec77
--- /dev/null
+++ b/worlds/yugioh06/boosterpacks.py
@@ -0,0 +1,923 @@
+from typing import Dict, Set
+
+booster_contents: Dict[str, Set[str]] = {
+ "LEGEND OF B.E.W.D.": {
+ "Exodia",
+ "Dark Magician",
+ "Polymerization",
+ "Skull Servant"
+ },
+ "METAL RAIDERS": {
+ "Petit Moth",
+ "Cocoon of Evolution",
+ "Time Wizard",
+ "Gate Guardian",
+ "Kazejin",
+ "Suijin",
+ "Sanga of the Thunder",
+ "Sangan",
+ "Castle of Dark Illusions",
+ "Soul Release",
+ "Magician of Faith",
+ "Dark Elf",
+ "Summoned Skull",
+ "Sangan",
+ "7 Colored Fish",
+ "Tribute to the Doomed",
+ "Horn of Heaven",
+ "Magic Jammer",
+ "Seven Tools of the Bandit",
+ "Solemn Judgment",
+ "Dream Clown",
+ "Heavy Storm"
+ },
+ "PHARAOH'S SERVANT": {
+ "Beast of Talwar",
+ "Jinzo",
+ "Gearfried the Iron Knight",
+ "Harpie's Brother",
+ "Gravity Bind",
+ "Solemn Wishes",
+ "Kiseitai",
+ "Morphing Jar #2",
+ "The Shallow Grave",
+ "Nobleman of Crossout",
+ "Magic Drain"
+ },
+ "PHARAONIC GUARDIAN": {
+ "Don Zaloog",
+ "Reasoning",
+ "Dark Snake Syndrome",
+ "Helpoemer",
+ "Newdoria",
+ "Spirit Reaper",
+ "Yomi Ship",
+ "Pyramid Turtle",
+ "Master Kyonshee",
+ "Book of Life",
+ "Call of the Mummy",
+ "Gravekeeper's Spy",
+ "Gravekeeper's Guard",
+ "A Cat of Ill Omen",
+ "Jowls of Dark Demise",
+ "Non Aggression Area",
+ "Terraforming",
+ "Des Lacooda",
+ "Swarm of Locusts",
+ "Swarm of Scarabs",
+ "Wandering Mummy",
+ "Royal Keeper",
+ "Book of Moon",
+ "Book of Taiyou",
+ "Dust Tornado",
+ "Raigeki Break"
+ },
+ "SPELL RULER": {
+ "Ritual",
+ "Messenger of Peace",
+ "Megamorph",
+ "Shining Angel",
+ "Mystic Tomato",
+ "Giant Rat",
+ "Mother Grizzly",
+ "UFO Turtle",
+ "Flying Kamakiri 1",
+ "Giant Germ",
+ "Nimble Momonga",
+ "Cyber Jar",
+ "Spear Cretin",
+ "Toon Mermaid",
+ "Toon Summoned Skull",
+ "Toon World",
+ "Rush Recklessly",
+ "The Reliable Guardian",
+ "Senju of the Thousand Hands",
+ "Sonic Bird",
+ "Mystical Space Typhoon"
+ },
+ "LABYRINTH OF NIGHTMARE": {
+ "Destiny Board",
+ "Spirit Message 'I'",
+ "Spirit Message 'N'",
+ "Spirit Message 'A'",
+ "Spirit Message 'L'",
+ "Fusion Gate",
+ "Jowgen the Spiritualist",
+ "Fairy Box",
+ "Aqua Spirit",
+ "Rock Spirit",
+ "Spirit of Flames",
+ "Garuda the Wind Spirit",
+ "Hysteric Fairy",
+ "Kycoo the Ghost Destroyer",
+ "Gemini Elf",
+ "Amphibian Beast",
+ "Revival Jam",
+ "Dancing Fairy",
+ "Cure Mermaid",
+ "The Last Warrior from Another Planet",
+ "United We Stand",
+ "Earthbound Spirit",
+ "The Masked Beast"
+ },
+ "LEGACY OF DARKNESS": {
+ "Last Turn",
+ "Yata-Garasu",
+ "Opticlops",
+ "Dark Ruler Ha Des",
+ "Exiled Force",
+ "Injection Fairy Lily",
+ "Spear Dragon",
+ "Luster Dragon #2",
+ "Twin-Headed Behemoth",
+ "Airknight Parshath",
+ "Freed the Matchless General",
+ "Marauding Captain",
+ "Reinforcement of the Army",
+ "Cave Dragon",
+ "Troop Dragon",
+ "Stamping Destruction",
+ "Creature Swap",
+ "Asura Priest",
+ "Fushi No Tori",
+ "Maharaghi",
+ "Susa Soldier",
+ "Emergency Provisions",
+ },
+ "MAGICIAN'S FORCE": {
+ "Huge Revolution",
+ "Oppressed People",
+ "United Resistance",
+ "People Running About",
+ "X-Head Cannon",
+ "Y-Dragon Head",
+ "Z-Metal Tank",
+ "XY-Dragon Cannon",
+ "XZ-Tank Cannon",
+ "YZ-Tank Dragon",
+ "XYZ-Dragon Cannon",
+ "Cliff the Trap Remover",
+ "Wave-Motion Cannon",
+ "Ritual",
+ "Magical Merchant",
+ "Poison of the Old Man",
+ "Chaos Command Magician",
+ "Skilled Dark Magician",
+ "Dark Blade",
+ "Great Angus",
+ "Luster Dragon",
+ "Breaker the magical Warrior",
+ "Old Vindictive Magician",
+ "Apprentice Magician",
+ "Burning Beast",
+ "Freezing Beast",
+ "Pitch-Dark Dragon",
+ "Giant Orc",
+ "Second Goblin",
+ "Decayed Commander",
+ "Zombie Tiger",
+ "Vampire Orchis",
+ "Des Dendle",
+ "Frontline Base",
+ "Formation Union",
+ "Pitch-Black Power Stone",
+ "Magical Marionette",
+ "Royal Magical Library",
+ "Spell Shield Type-8",
+ "Tribute Doll",
+ },
+ "DARK CRISIS": {
+ "Final Countdown",
+ "Ojama Green",
+ "Dark Scorpion Combination",
+ "Dark Scorpion - Chick the Yellow",
+ "Dark Scorpion - Meanae the Thorn",
+ "Dark Scorpion - Gorg the Strong",
+ "Ritual",
+ "Tsukuyomi",
+ "Ojama Trio",
+ "Kaiser Glider",
+ "D.D. Warrior Lady",
+ "Archfiend Soldier",
+ "Skull Archfiend of Lightning",
+ "Blindly Loyal Goblin",
+ "Gagagigo",
+ "Nin-Ken Dog",
+ "Zolga",
+ "Kelbek",
+ "Mudora",
+ "Cestus of Dagla",
+ "Vampire Lord",
+ "Metallizing Parasite - Lunatite",
+ "D. D. Trainer",
+ "Spell Reproduction",
+ "Contract with the Abyss",
+ "Dark Master - Zorc"
+ },
+ "INVASION OF CHAOS": {
+ "Ojama Delta Hurricane",
+ "Ojama Yellow",
+ "Ojama Black",
+ "Heart of the Underdog",
+ "Chaos Emperor Dragon - Envoy of the End",
+ "Self-Destruct Button",
+ "Manticore of Darkness",
+ "Dimension Fusion",
+ "Gigantes",
+ "Inferno",
+ "Silpheed",
+ "Mad Dog of Darkness",
+ "Ryu Kokki",
+ "Berserk Gorilla",
+ "Neo Bug",
+ "Dark Driceratops",
+ "Hyper Hammerhead",
+ "Sea Serpent Warrior of Darkness",
+ "Giga Gagagigo",
+ "Terrorking Salmon",
+ "Blazing Inpachi",
+ "Stealth Bird",
+ "Reload",
+ "Cursed Seal of the Forbidden Spell",
+ "Stray Lambs",
+ "Manju of the Ten Thousand Hands"
+ },
+ "ANCIENT SANCTUARY": {
+ "Monster Gate",
+ "Wall of Revealing Light",
+ "Mystik Wok",
+ "The Agent of Judgment - Saturn",
+ "Zaborg the Thunder Monarch",
+ "Regenerating Mummy",
+ "The End of Anubis",
+ "Solar Flare Dragon",
+ "Level Limit - Area B",
+ "King of the Swamp",
+ "Enemy Controller",
+ "Enchanting Fitting Room"
+ },
+ "SOUL OF THE DUELIST": {
+ "Ninja Grandmaster Sasuke",
+ "Mystic Swordsman LV2",
+ "Mystic Swordsman LV4",
+ "Enraged Muka Muka",
+ "Mobius the Frost Monarch",
+ "Horus the Black Flame Dragon LV6",
+ "Ultimate Baseball Kid",
+ "Armed Dragon LV3",
+ "Armed Dragon LV5",
+ "Masked Dragon",
+ "Element Dragon",
+ "Horus the Black Flame Dragon LV4",
+ "Level Up!",
+ "Howling Insect",
+ "Mobius the Frost Monarch"
+ },
+ "RISE OF DESTINY": {
+ "Homunculus the Alchemic Being",
+ "Thestalos the Firestorm Monarch",
+ "Roc from the Valley of Haze",
+ "Harpie Lady 1",
+ "Silent Swordsman Lv3",
+ "Mystic Swordsman LV6",
+ "Ultimate Insect Lv3",
+ "Divine Wrath",
+ "Serial Spell"
+ },
+ "FLAMING ETERNITY": {
+ "Insect Knight",
+ "Chiron the Mage",
+ "Granmarg the Rock Monarch",
+ "Silent Swordsman Lv5",
+ "The Dark - Hex-Sealed Fusion",
+ "The Earth - Hex-Sealed Fusion",
+ "The Light - Hex-Sealed Fusion",
+ "Ultimate Insect Lv5",
+ "Blast Magician",
+ "Golem Sentry",
+ "Rescue Cat",
+ "Blade Rabbit"
+ },
+ "THE LOST MILLENIUM": {
+ "Ritual",
+ "Megarock Dragon",
+ "D.D. Survivor",
+ "Hieracosphinx",
+ "Elemental Hero Flame Wingman",
+ "Elemental Hero Avian",
+ "Elemental Hero Burstinatrix",
+ "Elemental Hero Clayman",
+ "Elemental Hero Sparkman",
+ "Elemental Hero Thunder Giant",
+ "Aussa the Earth Charmer",
+ "Brain Control"
+ },
+ "CYBERNETIC REVOLUTION": {
+ "Power Bond",
+ "Cyber Dragon",
+ "Cyber Twin Dragon",
+ "Cybernetic Magician",
+ "Indomitable Fighter Lei Lei",
+ "Protective Soul Ailin",
+ "Miracle Fusion",
+ "Elemental Hero Bubbleman",
+ "Jerry Beans Man"
+ },
+ "ELEMENTAL ENERGY": {
+ "V-Tiger Jet",
+ "W-Wing Catapult",
+ "VW-Tiger Catapult",
+ "VWXYZ-Dragon Catapult Cannon",
+ "Zure, Knight of Dark World",
+ "Brron, Mad King of Dark World",
+ "Familiar-Possessed - Aussa",
+ "Familiar-Possessed - Eria",
+ "Familiar-Possessed - Hiita",
+ "Familiar-Possessed - Wynn",
+ "Oxygeddon",
+ "Roll Out!",
+ "Dark World Lightning",
+ "Elemental Hero Rampart Blaster",
+ "Elemental Hero Shining Flare Wingman",
+ "Elemental Hero Wildedge",
+ "Elemental Hero Wildheart",
+ "Elemental Hero Bladedge",
+ "Pot of Avarice",
+ "B.E.S. Tetran"
+ },
+ "SHADOW OF INFINITY": {
+ "Hamon, Lord of Striking Thunder",
+ "Raviel, Lord of Phantasms",
+ "Uria, Lord of Searing Flames",
+ "Ritual",
+ "Treeborn Frog",
+ "Saber Beetle",
+ "Tenkabito Shien",
+ "Princess Pikeru",
+ "Gokipon",
+ "Demise, King of Armageddon",
+ "Anteatereatingant"
+ },
+ "GAME GIFT COLLECTION": {
+ "Ritual",
+ "Valkyrion the Magna Warrior",
+ "Alpha the Magnet Warrior",
+ "Beta the Magnet Warrior",
+ "Gamma the Magnet Warrior",
+ "Magical Blast",
+ "Dunames Dark Witch",
+ "Vorse Raider",
+ "Exarion Universe",
+ "Abyss Soldier",
+ "Slate Warrior",
+ "Cyber-Tech Alligator",
+ "D.D. Assailant",
+ "Goblin Zombie",
+ "Elemental Hero Madballman",
+ "Mind Control",
+ "Toon Dark Magician Girl",
+ "Great Spirit",
+ "Graceful Dice",
+ "Negate Attack",
+ "Foolish Burial",
+ "Card Destruction",
+ "Dark Magic Ritual",
+ "Calamity of the Wicked"
+ },
+ "Special Gift Collection": {
+ "Gate Guardian",
+ "Scapegoat",
+ "Gil Garth",
+ "La Jinn the Mystical Genie of the Lamp",
+ "Summoned Skull",
+ "Inferno Hammer",
+ "Gemini Elf",
+ "Cyber Harpie Lady",
+ "Dandylion",
+ "Blade Knight",
+ "Curse of Vampire",
+ "Elemental Hero Flame Wingman",
+ "Magician of Black Chaos"
+ },
+ "Fairy Collection": {
+ "Silpheed",
+ "Dunames Dark Witch",
+ "Hysteric Fairy",
+ "The Agent of Judgment - Saturn",
+ "Shining Angel",
+ "Airknight Parshath",
+ "Dancing Fairy",
+ "Zolga",
+ "Kelbek",
+ "Mudora",
+ "Protective Soul Ailin",
+ "Marshmallon",
+ "Goddess with the Third Eye",
+ "Asura Priest",
+ "Manju of the Ten Thousand Hands",
+ "Senju of the Thousand Hands"
+ },
+ "Dragon Collection": {
+ "Victory D.",
+ "Chaos Emperor Dragon - Envoy of the End",
+ "Kaiser Glider",
+ "Horus the Black Flame Dragon LV6",
+ "Luster Dragon",
+ "Luster Dragon #2"
+ "Spear Dragon",
+ "Armed Dragon LV3",
+ "Armed Dragon LV5",
+ "Twin-Headed Behemoth",
+ "Cave Dragon",
+ "Masked Dragon",
+ "Element Dragon",
+ "Troop Dragon",
+ "Horus the Black Flame Dragon LV4",
+ "Pitch-Dark Dragon"
+ },
+ "Warrior Collection A": {
+ "Gate Guardian",
+ "Gearfried the Iron Knight",
+ "Dimensional Warrior",
+ "Command Knight",
+ "The Last Warrior from Another Planet",
+ "Dream Clown"
+ },
+ "Warrior Collection B": {
+ "Don Zaloog",
+ "Dark Scorpion - Chick the Yellow",
+ "Dark Scorpion - Meanae the Thorn",
+ "Dark Scorpion - Gorg the Strong",
+ "Cliff the Trap Remover",
+ "Ninja Grandmaster Sasuke",
+ "D.D. Warrior Lady",
+ "Mystic Swordsman LV2",
+ "Mystic Swordsman LV4",
+ "Mystic Swordsman LV6",
+ "Dark Blade",
+ "Blindly Loyal Goblin",
+ "Exiled Force",
+ "Ultimate Baseball Kid",
+ "Freed the Matchless General",
+ "Holy Knight Ishzark",
+ "Silent Swordsman Lv3",
+ "Silent Swordsman Lv5",
+ "Warrior Lady of the Wasteland",
+ "D.D. Assailant",
+ "Blade Knight",
+ "Marauding Captain",
+ "Toon Goblin Attack Force"
+ },
+ "Fiend Collection A": {
+ "Sangan",
+ "Castle of Dark Illusions",
+ "Barox",
+ "La Jinn the Mystical Genie of the Lamp",
+ "Summoned Skull",
+ "Beast of Talwar",
+ "Sangan",
+ "Giant Germ",
+ "Spear Cretin",
+ "Versago the Destroyer",
+ "Toon Summoned Skull"
+ },
+ "Fiend Collection B": {
+ "Raviel, Lord of Phantasms",
+ "Yata-Garasu",
+ "Helpoemer",
+ "Archfiend Soldier",
+ "Skull Descovery Knight",
+ "Gil Garth",
+ "Opticlops",
+ "Zure, Knight of Dark World",
+ "Brron, Mad King of Dark World",
+ "D.D. Survivor",
+ "Skull Archfiend of Lightning",
+ "The End of Anubis",
+ "Dark Ruler Ha Des",
+ "Inferno Hammer",
+ "Legendary Fiend",
+ "Newdoria",
+ "Slate Warrior",
+ "Giant Orc",
+ "Second Goblin",
+ "Kiseitai",
+ "Jowls of Dark Demise",
+ "D. D. Trainer",
+ "Earthbound Spirit"
+ },
+ "Machine Collection A": {
+ "Cyber-Stein",
+ "Mechanicalchaser",
+ "Jinzo",
+ "UFO Turtle",
+ "Cyber-Tech Alligator"
+ },
+ "Machine Collection B": {
+ "X-Head Cannon",
+ "Y-Dragon Head",
+ "Z-Metal Tank",
+ "XY-Dragon Cannon",
+ "XZ-Tank Cannon",
+ "YZ-Tank Dragon",
+ "XYZ-Dragon Cannon",
+ "V-Tiger Jet",
+ "W-Wing Catapult",
+ "VW-Tiger Catapult",
+ "VWXYZ-Dragon Catapult Cannon",
+ "Cyber Dragon",
+ "Cyber Twin Dragon",
+ "Green Gadget",
+ "Red Gadget",
+ "Yellow Gadget",
+ "B.E.S. Tetran"
+ },
+ "Spellcaster Collection A": {
+ "Exodia",
+ "Dark Sage",
+ "Dark Magician",
+ "Time Wizard",
+ "Kazejin",
+ "Magician of Faith",
+ "Dark Elf",
+ "Gemini Elf",
+ "Injection Fairy Lily",
+ "Cosmo Queen",
+ "Magician of Black Chaos"
+ },
+ "Spellcaster Collection B": {
+ "Jowgen the Spiritualist",
+ "Tsukuyomi",
+ "Manticore of Darkness",
+ "Chaos Command Magician",
+ "Cybernetic Magician",
+ "Skilled Dark Magician",
+ "Kycoo the Ghost Destroyer",
+ "Toon Gemini Elf",
+ "Toon Masked Sorcerer",
+ "Toon Dark Magician Girl",
+ "Familiar-Possessed - Aussa",
+ "Familiar-Possessed - Eria",
+ "Familiar-Possessed - Hiita",
+ "Familiar-Possessed - Wynn",
+ "Breaker the magical Warrior",
+ "The Tricky",
+ "Gravekeeper's Spy",
+ "Gravekeeper's Guard",
+ "Summon Priest",
+ "Old Vindictive Magician",
+ "Apprentice Magician",
+ "Princess Pikeru",
+ "Blast Magician",
+ "Magical Marionette",
+ "Mythical Beast Cerberus",
+ "Royal Magical Library",
+ "Aussa the Earth Charmer",
+
+ },
+ "Zombie Collection": {
+ "Skull Servant",
+ "Regenerating Mummy",
+ "Ryu Kokki",
+ "Spirit Reaper",
+ "Pyramid Turtle",
+ "Master Kyonshee",
+ "Curse of Vampire",
+ "Vampire Lord",
+ "Goblin Zombie",
+ "Decayed Commander",
+ "Zombie Tiger",
+ "Des Lacooda",
+ "Wandering Mummy",
+ "Royal Keeper"
+ },
+ "Special Monsters A": {
+ "X-Head Cannon",
+ "Y-Dragon Head",
+ "Z-Metal Tank",
+ "V-Tiger Jet",
+ "W-Wing Catapult",
+ "Yata-Garasu",
+ "Tsukuyomi",
+ "Dark Blade",
+ "Toon Gemini Elf",
+ "Toon Goblin Attack Force",
+ "Toon Masked Sorcerer",
+ "Toon Mermaid",
+ "Toon Dark Magician Girl",
+ "Toon Summoned Skull",
+ "Toon World",
+ "Burning Beast",
+ "Freezing Beast",
+ "Metallizing Parasite - Lunatite",
+ "Pitch-Dark Dragon",
+ "Giant Orc",
+ "Second Goblin",
+ "Decayed Commander",
+ "Zombie Tiger",
+ "Vampire Orchis",
+ "Des Dendle",
+ "Indomitable Fighter Lei Lei",
+ "Protective Soul Ailin",
+ "Frontline Base",
+ "Formation Union",
+ "Roll Out!",
+ "Asura Priest",
+ "Fushi No Tori",
+ "Maharaghi",
+ "Susa Soldier"
+ },
+ "Special Monsters B": {
+ "Polymerization",
+ "Mystic Swordsman LV2",
+ "Mystic Swordsman LV4",
+ "Mystic Swordsman LV6",
+ "Horus the Black Flame Dragon LV6",
+ "Horus the Black Flame Dragon LV4",
+ "Armed Dragon LV3"
+ "Armed Dragon LV5",
+ "Silent Swordsman Lv3",
+ "Silent Swordsman Lv5",
+ "Elemental Hero Flame Wingman",
+ "Elemental Hero Avian",
+ "Elemental Hero Burstinatrix",
+ "Miracle Fusion",
+ "Elemental Hero Madballman",
+ "Elemental Hero Bubbleman",
+ "Elemental Hero Clayman",
+ "Elemental Hero Rampart Blaster",
+ "Elemental Hero Shining Flare Wingman",
+ "Elemental Hero Sparkman",
+ "Elemental Hero Steam Healer",
+ "Elemental Hero Thunder Giant",
+ "Elemental Hero Wildedge",
+ "Elemental Hero Wildheart",
+ "Elemental Hero Bladedge",
+ "Level Up!",
+ "Ultimate Insect Lv3",
+ "Ultimate Insect Lv5"
+ },
+ "Reverse Collection": {
+ "Magical Merchant",
+ "Castle of Dark Illusions",
+ "Magician of Faith",
+ "Penguin Soldier",
+ "Blade Knight",
+ "Gravekeeper's Spy",
+ "Gravekeeper's Guard",
+ "Old Vindictive Magician",
+ "A Cat of Ill Omen",
+ "Jowls of Dark Demise",
+ "Cyber Jar",
+ "Morphing Jar",
+ "Morphing Jar #2",
+ "Needle Worm",
+ "Spear Cretin",
+ "Nobleman of Crossout",
+ "Aussa the Earth Charmer"
+ },
+ "LP Recovery Collection": {
+ "Mystik Wok",
+ "Poison of the Old Man",
+ "Hysteric Fairy",
+ "Dancing Fairy",
+ "Zolga",
+ "Cestus of Dagla",
+ "Nimble Momonga",
+ "Solemn Wishes",
+ "Cure Mermaid",
+ "Princess Pikeru",
+ "Kiseitai",
+ "Elemental Hero Steam Healer",
+ "Fushi No Tori",
+ "Emergency Provisions"
+ },
+ "Special Summon Collection A": {
+ "Perfectly Ultimate Great Moth",
+ "Dark Sage",
+ "Polymerization",
+ "Ritual",
+ "Cyber-Stein",
+ "Scapegoat",
+ "Aqua Spirit",
+ "Rock Spirit",
+ "Spirit of Flames",
+ "Garuda the Wind Spirit",
+ "Shining Angel",
+ "Mystic Tomato",
+ "Giant Rat",
+ "Mother Grizzly",
+ "UFO Turtle",
+ "Flying Kamakiri 1",
+ "Giant Germ",
+ "Revival Jam",
+ "Pyramid Turtle",
+ "Troop Dragon",
+ "Gravekeeper's Spy",
+ "Pitch-Dark Dragon",
+ "Decayed Commander",
+ "Zombie Tiger",
+ "Vampire Orchis",
+ "Des Dendle",
+ "Nimble Momonga",
+ "The Last Warrior from Another Planet",
+ "Embodiment of Apophis",
+ "Cyber Jar",
+ "Morphing Jar #2",
+ "Spear Cretin",
+ "Dark Magic Curtain"
+ },
+ "Special Summon Collection B": {
+ "Monster Gate",
+ "Chaos Emperor Dragon - Envoy of the End",
+ "Ojama Trio",
+ "Dimension Fusion",
+ "Return from the Different Dimension",
+ "Gigantes",
+ "Inferno",
+ "Silpheed",
+ "Mystic Swordsman LV2",
+ "Mystic Swordsman LV4",
+ "Skilled Dark Magician",
+ "Horus the Black Flame Dragon LV6",
+ "Armed Dragon LV3",
+ "Armed Dragon LV5",
+ "Marauding Captain",
+ "Masked Dragon",
+ "The Tricky",
+ "Magical Dimension",
+ "Frontline Base",
+ "Formation Union",
+ "Princess Pikeru",
+ "Skull Zoma",
+ "Metal Reflect Slime"
+ "Level Up!",
+ "Howling Insect",
+ "Tribute Doll",
+ "Enchanting Fitting Room",
+ "Stray Lambs"
+ },
+ "Special Summon Collection C": {
+ "Hamon, Lord of Striking Thunder",
+ "Raviel, Lord of Phantasms",
+ "Uria, Lord of Searing Flames",
+ "Treeborn Frog",
+ "Cyber Dragon",
+ "Familiar-Possessed - Aussa",
+ "Familiar-Possessed - Eria",
+ "Familiar-Possessed - Hiita",
+ "Familiar-Possessed - Wynn",
+ "Silent Swordsman Lv3",
+ "Silent Swordsman Lv5",
+ "Warrior Lady of the Wasteland",
+ "Dandylion",
+ "Curse of Vampire",
+ "Summon Priest",
+ "Miracle Fusion",
+ "Elemental Hero Bubbleman",
+ "The Dark - Hex-Sealed Fusion",
+ "The Earth - Hex-Sealed Fusion",
+ "The Light - Hex-Sealed Fusion",
+ "Ultimate Insect Lv3",
+ "Ultimate Insect Lv5",
+ "Rescue Cat",
+ "Anteatereatingant"
+ },
+ "Equipment Collection": {
+ "Megamorph",
+ "Cestus of Dagla",
+ "United We Stand"
+ },
+ "Continuous Spell/Trap A": {
+ "Destiny Board",
+ "Spirit Message 'I'",
+ "Spirit Message 'N'",
+ "Spirit Message 'A'",
+ "Spirit Message 'L'",
+ "Messenger of Peace",
+ "Fairy Box",
+ "Ultimate Offering",
+ "Gravity Bind",
+ "Solemn Wishes",
+ "Embodiment of Apophis",
+ "Toon World"
+ },
+ "Continuous Spell/Trap B": {
+ "Hamon, Lord of Striking Thunder",
+ "Uria, Lord of Searing Flames",
+ "Wave-Motion Cannon",
+ "Heart of the Underdog",
+ "Wall of Revealing Light",
+ "Dark Snake Syndrome",
+ "Call of the Mummy",
+ "Frontline Base",
+ "Level Limit - Area B",
+ "Skull Zoma",
+ "Pitch-Black Power Stone",
+ "Metal Reflect Slime"
+ },
+ "Quick/Counter Collection": {
+ "Mystik Wok",
+ "Poison of the Old Man",
+ "Scapegoat",
+ "Magical Dimension",
+ "Enemy Controller",
+ "Collapse",
+ "Emergency Provisions",
+ "Graceful Dice",
+ "Offerings to the Doomed",
+ "Reload",
+ "Rush Recklessly",
+ "The Reliable Guardian",
+ "Cursed Seal of the Forbidden Spell",
+ "Divine Wrath",
+ "Horn of Heaven",
+ "Magic Drain",
+ "Magic Jammer",
+ "Negate Attack",
+ "Seven Tools of the Bandit",
+ "Solemn Judgment",
+ "Spell Shield Type-8",
+ "Book of Moon",
+ "Serial Spell",
+ "Mystical Space Typhoon"
+ },
+ "Direct Damage Collection": {
+ "Hamon, Lord of Striking Thunder",
+ "Chaos Emperor Dragon - Envoy of the End",
+ "Dark Snake Syndrome",
+ "Inferno",
+ "Exarion Universe",
+ "Kycoo the Ghost Destroyer",
+ "Giant Germ",
+ "Familiar-Possessed - Aussa",
+ "Familiar-Possessed - Eria",
+ "Familiar-Possessed - Hiita",
+ "Familiar-Possessed - Wynn",
+ "Dark Driceratops",
+ "Saber Beetle",
+ "Thestalos the Firestorm Monarch",
+ "Solar Flare Dragon",
+ "Ultimate Baseball Kid",
+ "Spear Dragon",
+ "Oxygeddon",
+ "Airknight Parshath",
+ "Vampire Lord",
+ "Stamping Destruction",
+ "Decayed Commander",
+ "Jowls of Dark Demise",
+ "Stealth Bird",
+ "Elemental Hero Bladedge",
+ },
+ "Direct Attack Collection": {
+ "Victory D.",
+ "Dark Scorpion Combination",
+ "Spirit Reaper",
+ "Elemental Hero Rampart Blaster",
+ "Toon Gemini Elf",
+ "Toon Goblin Attack Force",
+ "Toon Masked Sorcerer",
+ "Toon Mermaid",
+ "Toon Summoned Skull",
+ "Toon Dark Magician Girl"
+ },
+ "Monster Destroy Collection": {
+ "Hamon, Lord of Striking Thunder",
+ "Inferno",
+ "Ninja Grandmaster Sasuke",
+ "Zaborg the Thunder Monarch",
+ "Mystic Swordsman LV2",
+ "Mystic Swordsman LV4",
+ "Mystic Swordsman LV6",
+ "Skull Descovery Knight",
+ "Inferno Hammer",
+ "Ryu Kokki",
+ "Newdoria",
+ "Exiled Force",
+ "Yomi Ship",
+ "Armed Dragon LV5",
+ "Element Dragon",
+ "Old Vindictive Magician",
+ "Magical Dimension",
+ "Des Dendle",
+ "Nobleman of Crossout",
+ "Shield Crash",
+ "Tribute to the Doomed",
+ "Elemental Hero Flame Wingman",
+ "Elemental Hero Shining Flare Wingman",
+ "Elemental Hero Steam Healer",
+ "Blast Magician",
+ "Magical Marionette",
+ "Swarm of Scarabs",
+ "Offerings to the Doomed",
+ "Divine Wrath",
+ "Dream Clown"
+ },
+}
+
+
+def get_booster_locations(booster: str) -> Dict[str, str]:
+ return {
+ f"{booster} {i}": content
+ for i, content in enumerate(booster_contents[booster])
+ }
diff --git a/worlds/yugioh06/client_bh.py b/worlds/yugioh06/client_bh.py
new file mode 100644
index 00000000..910eba7c
--- /dev/null
+++ b/worlds/yugioh06/client_bh.py
@@ -0,0 +1,139 @@
+import math
+from typing import TYPE_CHECKING, List, Optional, Set
+
+from NetUtils import ClientStatus, NetworkItem
+
+import worlds._bizhawk as bizhawk
+from worlds._bizhawk.client import BizHawkClient
+from worlds.yugioh06 import item_to_index
+
+if TYPE_CHECKING:
+ from worlds._bizhawk.context import BizHawkClientContext
+
+
+class YuGiOh2006Client(BizHawkClient):
+ game = "Yu-Gi-Oh! 2006"
+ system = "GBA"
+ patch_suffix = ".apygo06"
+ local_checked_locations: Set[int]
+ goal_flag: int
+ rom_slot_name: Optional[str]
+
+ def __init__(self) -> None:
+ super().__init__()
+ self.local_checked_locations = set()
+ self.rom_slot_name = None
+
+ async def validate_rom(self, ctx: "BizHawkClientContext") -> bool:
+ from CommonClient import logger
+
+ try:
+ # Check if ROM is some version of Yu-Gi-Oh! 2006
+ game_name = ((await bizhawk.read(ctx.bizhawk_ctx, [(0xA0, 11, "ROM")]))[0]).decode("ascii")
+ if game_name != "YUGIOHWCT06":
+ return False
+
+ # Check if we can read the slot name. Doing this here instead of set_auth as a protection against
+ # validating a ROM where there's no slot name to read.
+ try:
+ slot_name_bytes = (await bizhawk.read(ctx.bizhawk_ctx, [(0x30, 32, "ROM")]))[0]
+ self.rom_slot_name = bytes([byte for byte in slot_name_bytes if byte != 0]).decode("utf-8")
+ except UnicodeDecodeError:
+ logger.info("Could not read slot name from ROM. Are you sure this ROM matches this client version?")
+ return False
+ except UnicodeDecodeError:
+ return False
+ except bizhawk.RequestFailedError:
+ return False # Should verify on the next pass
+
+ ctx.game = self.game
+ ctx.items_handling = 0b001
+ ctx.want_slot_data = False
+ return True
+
+ async def set_auth(self, ctx: "BizHawkClientContext") -> None:
+ ctx.auth = self.rom_slot_name
+
+ async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
+ try:
+ read_state = await bizhawk.read(
+ ctx.bizhawk_ctx,
+ [
+ (0x0, 8, "EWRAM"),
+ (0x52E8, 32, "EWRAM"),
+ (0x5308, 32, "EWRAM"),
+ (0x5325, 1, "EWRAM"),
+ (0x6C38, 4, "EWRAM"),
+ ],
+ )
+ game_state = read_state[0].decode("utf-8")
+ locations = read_state[1]
+ items = read_state[2]
+ amount_items = int.from_bytes(read_state[3], "little")
+ money = int.from_bytes(read_state[4], "little")
+
+ # make sure save was created
+ if game_state != "YWCT2006":
+ return
+ local_items = bytearray(items)
+ await bizhawk.guarded_write(
+ ctx.bizhawk_ctx,
+ [(0x5308, parse_items(bytearray(items), ctx.items_received), "EWRAM")],
+ [(0x5308, local_items, "EWRAM")],
+ )
+ money_received = 0
+ for item in ctx.items_received:
+ if item.item == item_to_index["5000DP"] + 5730000:
+ money_received += 1
+ if money_received > amount_items:
+ await bizhawk.guarded_write(
+ ctx.bizhawk_ctx,
+ [
+ (0x6C38, (money + (money_received - amount_items) * 5000).to_bytes(4, "little"), "EWRAM"),
+ (0x5325, money_received.to_bytes(2, "little"), "EWRAM"),
+ ],
+ [
+ (0x6C38, money.to_bytes(4, "little"), "EWRAM"),
+ (0x5325, amount_items.to_bytes(2, "little"), "EWRAM"),
+ ],
+ )
+
+ locs_to_send = set()
+
+ # Check for set location flags.
+ for byte_i, byte in enumerate(bytearray(locations)):
+ for i in range(8):
+ and_value = 1 << i
+ if byte & and_value != 0:
+ flag_id = byte_i * 8 + i
+
+ location_id = flag_id + 5730001
+ if location_id in ctx.server_locations:
+ locs_to_send.add(location_id)
+
+ # Send locations if there are any to send.
+ if locs_to_send != self.local_checked_locations:
+ self.local_checked_locations = locs_to_send
+
+ if locs_to_send is not None:
+ await ctx.send_msgs([{"cmd": "LocationChecks", "locations": list(locs_to_send)}])
+
+ # Send game clear if we're in either any ending cutscene or the credits state.
+ if not ctx.finished_game and locations[18] & (1 << 5) != 0:
+ await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}])
+
+ except bizhawk.RequestFailedError:
+ # Exit handler and return to main loop to reconnect.
+ pass
+
+
+# Parses bit-map for local items and adds the received items to that bit-map
+def parse_items(local_items: bytearray, items: List[NetworkItem]) -> bytearray:
+ array = local_items
+ for item in items:
+ index = item.item - 5730001
+ if index != 254:
+ byte = math.floor(index / 8)
+ bit = index % 8
+ array[byte] = array[byte] | (1 << bit)
+ return array
diff --git a/worlds/yugioh06/docs/en_Yu-Gi-Oh! 2006.md b/worlds/yugioh06/docs/en_Yu-Gi-Oh! 2006.md
new file mode 100644
index 00000000..ee8c95a3
--- /dev/null
+++ b/worlds/yugioh06/docs/en_Yu-Gi-Oh! 2006.md
@@ -0,0 +1,53 @@
+# Yu-Gi-Oh! Ultimate Masters: World Championship Tournament 2006
+
+## Where is the options page?
+
+The [player options page for this game](../player-options) contains all the options you need to configure and
+export a config file.
+
+## What does randomization do to this game?
+
+Unlocking Booster Packs, Campaign, Limited and Theme Duel Opponents has been changed.
+You only need to beat each Campaign Opponent once.
+Logic expects you to have access to the Booster Packs necessary to get the locations at a reasonable pace and consistency.
+Logic remains, so the game is always able to be completed, but because of the shuffle, the player may need to defeat certain opponents before they
+would in the vanilla game.
+
+You can change how much money you receive and how much booster packs cost.
+
+## What is the goal of Yu-Gi-Oh! 2006 when randomized?
+
+Defeat a certain amount of Limited/Theme Duels to Unlock the final Campaign Opponent and beat it.
+
+## What items and locations get shuffled?
+
+Locations in which items can be found:
+- Getting a Duel Bonus for the first time
+- Beating a certain amount campaign opponents of the same level.
+- Beating a Limited/Theme Duel
+- Obtaining certain cards (same that unlock a theme duel in vanilla)
+
+Items that are shuffled:
+- Unlocking Booster Packs (the "ALL" Booster Packs are excluded)
+- Unlocking Campaign Opponents
+- Unlocking Limited/Theme Duels
+- Banlists
+
+## What items are _not_ randomized?
+Certain Key Items are kept in their original locations:
+- Duel Puzzles
+- Survival Mode
+- Booster Pack Contents
+
+## Which items can be in another player's world?
+
+Any shuffled item can be in other players' worlds.
+
+
+## What does another world's item look like in Yu-Gi-Oh! 2006?
+
+You can only tell when and what you got via the client.
+
+## When the player receives an item, what happens?
+
+The Opponent/Pack becomes available to you.
diff --git a/worlds/yugioh06/docs/setup_en.md b/worlds/yugioh06/docs/setup_en.md
new file mode 100644
index 00000000..1beeaa6c
--- /dev/null
+++ b/worlds/yugioh06/docs/setup_en.md
@@ -0,0 +1,72 @@
+# Setup Guide for Yu-Gi-Oh! Ultimate Masters: World Championship Tournament 2006 Archipelago
+
+## Important
+
+As we are using Bizhawk, this guide is only applicable to Windows and Linux systems.
+
+## Required Software
+
+- Bizhawk: [Bizhawk Releases from TASVideos](https://tasvideos.org/BizHawk/ReleaseHistory)
+ - Version 2.7.0 and later are supported.
+ - Detailed installation instructions for Bizhawk can be found at the above link.
+ - Windows users must run the prereq installer first, which can also be found at the above link.
+- The built-in Archipelago client, which can be installed [here](https://github.com/ArchipelagoMW/Archipelago/releases)
+- A US or European Yu-Gi-Oh! Ultimate Masters: World Championship Tournament 2006 Rom
+
+## Configuring Bizhawk
+
+Once Bizhawk has been installed, open Bizhawk and change the following settings:
+
+- Go to Config > Customize. Switch to the Advanced tab, then switch the Lua Core from "NLua+KopiLua" to
+ "Lua+LuaInterface". This is required for the Lua script to function correctly.
+ **NOTE: Even if "Lua+LuaInterface" is already selected, toggle between the two options and reselect it. Fresh installs**
+ **of newer versions of Bizhawk have a tendency to show "Lua+LuaInterface" as the default selected option but still load**
+ **"NLua+KopiLua" until this step is done.**
+- Under Config > Customize > Advanced, make sure the box for AutoSaveRAM is checked, and click the 5s button.
+ This reduces the possibility of losing save data in emulator crashes.
+- Under Config > Customize, check the "Run in background" and "Accept background input" boxes. This will allow you to
+ continue playing in the background, even if another window is selected, such as the Client.
+- Under Config > Hotkeys, many hotkeys are listed, with many bound to common keys on the keyboard. You will likely want
+ to disable most of these, which you can do quickly using `Esc`.
+
+It is strongly recommended to associate GBA rom extensions (\*.gba) to the Bizhawk we've just installed.
+To do so, we simply have to search any GBA rom we happened to own, right click and select "Open with...", unfold
+the list that appears and select the bottom option "Look for another application", then browse to the Bizhawk folder
+and select EmuHawk.exe.
+
+## Configuring your YAML file
+
+### What is a YAML file and why do I need one?
+
+Your YAML file contains a set of configuration options which provide the generator with information about how it should
+generate your game. Each player of a multiworld will provide their own YAML file. This setup allows each player to enjoy
+an experience customized for their taste, and different players in the same multiworld can all have different options.
+
+### Where do I get a YAML file?
+
+You can customize your options by visiting the
+[Yu-Gi-Oh! 2006 Player Options Page](/games/Yu-Gi-Oh!%202006/player-options)
+
+## Joining a MultiWorld Game
+
+### Obtain your GBA patch file
+
+When you join a multiworld game, you will be asked to provide your YAML file to whoever is hosting. Once that is done,
+the host will provide you with either a link to download your data file, or with a zip file containing everyone's data
+files. Your data file should have a `.apygo06` extension.
+
+Double-click on your `.apygo06` file to start your client and start the ROM patch process. Once the process is finished
+(this can take a while), the client and the emulator will be started automatically (if you associated the extension
+to the emulator as recommended).
+
+### Connect to the Multiserver
+
+Once both the client and the emulator are started, you must connect them. Within the emulator click on the "Tools"
+menu and select "Lua Console". Click the folder button or press Ctrl+O to open a Lua script.
+
+Navigate to your Archipelago install folder and open `data/lua/connector_bizhawk_generic.lua`.
+
+To connect the client to the multiserver simply put `
:` on the textfield on top and press enter (if the
+server uses password, type in the bottom textfield `/connect : [password]`)
+
+Don't forget to start manipulating RNG early by shouting "Heart of the Cards!" during generation.
\ No newline at end of file
diff --git a/worlds/yugioh06/fusions.py b/worlds/yugioh06/fusions.py
new file mode 100644
index 00000000..22d03b38
--- /dev/null
+++ b/worlds/yugioh06/fusions.py
@@ -0,0 +1,72 @@
+from typing import List, NamedTuple
+
+
+class FusionData(NamedTuple):
+ name: str
+ materials: List[str]
+ replaceable: bool
+ additional_spells: List[str]
+
+
+fusions = {
+ "Elemental Hero Flame Wingman": FusionData(
+ "Elemental Hero Flame Wingman",
+ ["Elemental Hero Avian", "Elemental Hero Burstinatrix"],
+ True,
+ ["Miracle Fusion"]),
+ "Elemental Hero Madballman": FusionData(
+ "Elemental Hero Madballman",
+ ["Elemental Hero Bubbleman", "Elemental Hero Clayman"],
+ True,
+ ["Miracle Fusion"]),
+ "Elemental Hero Rampart Blaster": FusionData(
+ "Elemental Hero Rampart Blaster",
+ ["Elemental Hero Burstinatrix", "Elemental Hero Clayman"],
+ True,
+ ["Miracle Fusion"]),
+ "Elemental Hero Shining Flare Wingman": FusionData(
+ "Elemental Hero Shining Flare Wingman",
+ ["Elemental Hero Flame Wingman", "Elemental Hero Sparkman"],
+ True,
+ ["Miracle Fusion"]),
+ "Elemental Hero Steam Healer": FusionData(
+ "Elemental Hero Steam Healer",
+ ["Elemental Hero Burstinatrix", "Elemental Hero Bubbleman"],
+ True,
+ ["Miracle Fusion"]),
+ "Elemental Hero Wildedge": FusionData(
+ "Elemental Hero Wildedge",
+ ["Elemental Hero Wildheart", "Elemental Hero Bladedge"],
+ True,
+ ["Miracle Fusion"])
+}
+
+fusion_subs = ["The Dark - Hex-Sealed Fusion",
+ "The Earth - Hex-Sealed Fusion",
+ "The Light - Hex-Sealed Fusion",
+ "Goddess with the Third Eye",
+ "King of the Swamp",
+ "Versago the Destroyer",
+ # Only in All-packs
+ "Beastking of the Swamps",
+ "Mystical Sheep #1"]
+
+
+def has_all_materials(state, monster, player):
+ data = fusions.get(monster)
+ if not state.has(monster, player):
+ return False
+ if data is None:
+ return True
+ else:
+ materials = data.replaceable and state.has_any(fusion_subs, player)
+ for material in data.materials:
+ materials += has_all_materials(state, material, player)
+ return materials >= len(data.materials)
+
+
+def count_has_materials(state, monsters, player):
+ amount = 0
+ for monster in monsters:
+ amount += has_all_materials(state, monster, player)
+ return amount
diff --git a/worlds/yugioh06/items.py b/worlds/yugioh06/items.py
new file mode 100644
index 00000000..f0f877fd
--- /dev/null
+++ b/worlds/yugioh06/items.py
@@ -0,0 +1,369 @@
+from typing import Dict, List
+
+item_to_index: Dict[str, int] = {
+ "LEGEND OF B.E.W.D.": 1,
+ "METAL RAIDERS": 2,
+ "PHARAOH'S SERVANT": 3,
+ "PHARAONIC GUARDIAN": 4,
+ "SPELL RULER": 5,
+ "LABYRINTH OF NIGHTMARE": 6,
+ "LEGACY OF DARKNESS": 7,
+ "MAGICIAN'S FORCE": 8,
+ "DARK CRISIS": 9,
+ "INVASION OF CHAOS": 10,
+ "ANCIENT SANCTUARY": 11,
+ "SOUL OF THE DUELIST": 12,
+ "RISE OF DESTINY": 13,
+ "FLAMING ETERNITY": 14,
+ "THE LOST MILLENIUM": 15,
+ "CYBERNETIC REVOLUTION": 16,
+ "ELEMENTAL ENERGY": 17,
+ "SHADOW OF INFINITY": 18,
+ "GAME GIFT COLLECTION": 19,
+ "Special Gift Collection": 20,
+ "Fairy Collection": 21,
+ "Dragon Collection": 22,
+ "Warrior Collection A": 23,
+ "Warrior Collection B": 24,
+ "Fiend Collection A": 25,
+ "Fiend Collection B": 26,
+ "Machine Collection A": 27,
+ "Machine Collection B": 28,
+ "Spellcaster Collection A": 29,
+ "Spellcaster Collection B": 30,
+ "Zombie Collection": 31,
+ "Special Monsters A": 32,
+ "Special Monsters B": 33,
+ "Reverse Collection": 34,
+ "LP Recovery Collection": 35,
+ "Special Summon Collection A": 36,
+ "Special Summon Collection B": 37,
+ "Special Summon Collection C": 38,
+ "Equipment Collection": 39,
+ "Continuous Spell/Trap A": 40,
+ "Continuous Spell/Trap B": 41,
+ "Quick/Counter Collection": 42,
+ "Direct Damage Collection": 43,
+ "Direct Attack Collection": 44,
+ "Monster Destroy Collection": 45,
+ "All Normal Monsters": 46,
+ "All Effect Monsters": 47,
+ "All Fusion Monsters": 48,
+ "All Traps": 49,
+ "All Spells": 50,
+ "All at Random": 51,
+ "LD01 All except Level 4 forbidden Unlock": 52,
+ "LD02 Medium/high Level forbidden Unlock": 53,
+ "LD03 ATK 1500 or more forbidden Unlock": 54,
+ "LD04 Flip Effects forbidden Unlock": 55,
+ "LD05 Tributes forbidden Unlock": 56,
+ "LD06 Traps forbidden Unlock": 57,
+ "LD07 Large Deck A Unlock": 58,
+ "LD08 Large Deck B Unlock": 59,
+ "LD09 Sets Forbidden Unlock": 60,
+ "LD10 All except LV monsters forbidden Unlock": 61,
+ "LD11 All except Fairies forbidden Unlock": 62,
+ "LD12 All except Wind forbidden Unlock": 63,
+ "LD13 All except monsters forbidden Unlock": 64,
+ "LD14 Level 3 or below forbidden Unlock": 65,
+ "LD15 DEF 1500 or less forbidden Unlock": 66,
+ "LD16 Effect Monsters forbidden Unlock": 67,
+ "LD17 Spells forbidden Unlock": 68,
+ "LD18 Attacks forbidden Unlock": 69,
+ "LD19 All except E-Hero's forbidden Unlock": 70,
+ "LD20 All except Warriors forbidden Unlock": 71,
+ "LD21 All except Dark forbidden Unlock": 72,
+ "LD22 All limited cards forbidden Unlock": 73,
+ "LD23 Refer to Mar 05 Banlist Unlock": 74,
+ "LD24 Refer to Sept 04 Banlist Unlock": 75,
+ "LD25 Low Life Points Unlock": 76,
+ "LD26 All except Toons forbidden Unlock": 77,
+ "LD27 All except Spirits forbidden Unlock": 78,
+ "LD28 All except Dragons forbidden Unlock": 79,
+ "LD29 All except Spellcasters forbidden Unlock": 80,
+ "LD30 All except Light forbidden Unlock": 81,
+ "LD31 All except Fire forbidden Unlock": 82,
+ "LD32 Decks with multiples forbidden Unlock": 83,
+ "LD33 Special Summons forbidden Unlock": 84,
+ "LD34 Normal Summons forbidden Unlock": 85,
+ "LD35 All except Zombies forbidden Unlock": 86,
+ "LD36 All except Earth forbidden Unlock": 87,
+ "LD37 All except Water forbidden Unlock": 88,
+ "LD38 Refer to Mar 04 Banlist Unlock": 89,
+ "LD39 Monsters forbidden Unlock": 90,
+ "LD40 Refer to Sept 05 Banlist Unlock": 91,
+ "LD41 Refer to Sept 03 Banlist Unlock": 92,
+ "TD01 Battle Damage Unlock": 93,
+ "TD02 Deflected Damage Unlock": 94,
+ "TD03 Normal Summon Unlock": 95,
+ "TD04 Ritual Summon Unlock": 96,
+ "TD05 Special Summon A Unlock": 97,
+ "TD06 20x Spell Unlock": 98,
+ "TD07 10x Trap Unlock": 99,
+ "TD08 Draw Unlock": 100,
+ "TD09 Hand Destruction Unlock": 101,
+ "TD10 During Opponent's Turn Unlock": 102,
+ "TD11 Recover Unlock": 103,
+ "TD12 Remove Monsters by Effect Unlock": 104,
+ "TD13 Flip Summon Unlock": 105,
+ "TD14 Special Summon B Unlock": 106,
+ "TD15 Token Unlock": 107,
+ "TD16 Union Unlock": 108,
+ "TD17 10x Quick Spell Unlock": 109,
+ "TD18 The Forbidden Unlock": 110,
+ "TD19 20 Turns Unlock": 111,
+ "TD20 Deck Destruction Unlock": 112,
+ "TD21 Victory D. Unlock": 113,
+ "TD22 The Preventers Fight Back Unlock": 114,
+ "TD23 Huge Revolution Unlock": 115,
+ "TD24 Victory in 5 Turns Unlock": 116,
+ "TD25 Moth Grows Up Unlock": 117,
+ "TD26 Magnetic Power Unlock": 118,
+ "TD27 Dark Sage Unlock": 119,
+ "TD28 Direct Damage Unlock": 120,
+ "TD29 Destroy Monsters in Battle Unlock": 121,
+ "TD30 Tribute Summon Unlock": 122,
+ "TD31 Special Summon C Unlock": 123,
+ "TD32 Toon Unlock": 124,
+ "TD33 10x Counter Unlock": 125,
+ "TD34 Destiny Board Unlock": 126,
+ "TD35 Huge Damage in a Turn Unlock": 127,
+ "TD36 V-Z In the House Unlock": 128,
+ "TD37 Uria, Lord of Searing Flames Unlock": 129,
+ "TD38 Hamon, Lord of Striking Thunder Unlock": 130,
+ "TD39 Raviel, Lord of Phantasms Unlock": 131,
+ "TD40 Make a Chain Unlock": 132,
+ "TD41 The Gatekeeper Stands Tall Unlock": 133,
+ "TD42 Serious Damage Unlock": 134,
+ "TD43 Return Monsters with Effects Unlock": 135,
+ "TD44 Fusion Summon Unlock": 136,
+ "TD45 Big Damage at once Unlock": 137,
+ "TD46 XYZ In the House Unlock": 138,
+ "TD47 Spell Counter Unlock": 139,
+ "TD48 Destroy Monsters with Effects Unlock": 140,
+ "TD49 Plunder Unlock": 141,
+ "TD50 Dark Scorpion Combination Unlock": 142,
+ "Campaign Tier 1 Column 1": 143,
+ "Campaign Tier 1 Column 2": 144,
+ "Campaign Tier 1 Column 3": 145,
+ "Campaign Tier 1 Column 4": 146,
+ "Campaign Tier 1 Column 5": 147,
+ "Campaign Tier 2 Column 1": 148,
+ "Campaign Tier 2 Column 2": 149,
+ "Campaign Tier 2 Column 3": 150,
+ "Campaign Tier 2 Column 4": 151,
+ "Campaign Tier 2 Column 5": 152,
+ "Campaign Tier 3 Column 1": 153,
+ "Campaign Tier 3 Column 2": 154,
+ "Campaign Tier 3 Column 3": 155,
+ "Campaign Tier 3 Column 4": 156,
+ "Campaign Tier 3 Column 5": 157,
+ "Campaign Tier 4 Column 1": 158,
+ "Campaign Tier 4 Column 2": 159,
+ "Campaign Tier 4 Column 3": 160,
+ "Campaign Tier 4 Column 4": 161,
+ "Campaign Tier 4 Column 5": 162,
+ "Campaign Tier 5 Column 1": 163,
+ "Campaign Tier 5 Column 2": 164,
+ "No Banlist": 167,
+ "Banlist September 2003": 168,
+ "Banlist March 2004": 169,
+ "Banlist September 2004": 170,
+ "Banlist March 2005": 171,
+ "Banlist September 2005": 172,
+ "5000DP": 254,
+ "Remote": 255,
+}
+
+tier_1_opponents: List[str] = [
+ "Campaign Tier 1 Column 1",
+ "Campaign Tier 1 Column 2",
+ "Campaign Tier 1 Column 3",
+ "Campaign Tier 1 Column 4",
+ "Campaign Tier 1 Column 5",
+]
+
+Banlist_Items: List[str] = [
+ "No Banlist",
+ "Banlist September 2003",
+ "Banlist March 2004",
+ "Banlist September 2004",
+ "Banlist March 2005",
+ "Banlist September 2005",
+]
+
+draft_boosters: List[str] = [
+ "METAL RAIDERS",
+ "PHARAOH'S SERVANT",
+ "PHARAONIC GUARDIAN",
+ "LABYRINTH OF NIGHTMARE",
+ "LEGACY OF DARKNESS",
+ "MAGICIAN'S FORCE",
+ "DARK CRISIS",
+ "INVASION OF CHAOS",
+ "RISE OF DESTINY",
+ "ELEMENTAL ENERGY",
+ "SHADOW OF INFINITY",
+]
+
+draft_opponents: List[str] = ["Campaign Tier 1 Column 1", "Campaign Tier 1 Column 5"]
+
+booster_packs: List[str] = [
+ "LEGEND OF B.E.W.D.",
+ "METAL RAIDERS",
+ "PHARAOH'S SERVANT",
+ "PHARAONIC GUARDIAN",
+ "SPELL RULER",
+ "LABYRINTH OF NIGHTMARE",
+ "LEGACY OF DARKNESS",
+ "MAGICIAN'S FORCE",
+ "DARK CRISIS",
+ "INVASION OF CHAOS",
+ "ANCIENT SANCTUARY",
+ "SOUL OF THE DUELIST",
+ "RISE OF DESTINY",
+ "FLAMING ETERNITY",
+ "THE LOST MILLENIUM",
+ "CYBERNETIC REVOLUTION",
+ "ELEMENTAL ENERGY",
+ "SHADOW OF INFINITY",
+ "GAME GIFT COLLECTION",
+ "Special Gift Collection",
+ "Fairy Collection",
+ "Dragon Collection",
+ "Warrior Collection A",
+ "Warrior Collection B",
+ "Fiend Collection A",
+ "Fiend Collection B",
+ "Machine Collection A",
+ "Machine Collection B",
+ "Spellcaster Collection A",
+ "Spellcaster Collection B",
+ "Zombie Collection",
+ "Special Monsters A",
+ "Special Monsters B",
+ "Reverse Collection",
+ "LP Recovery Collection",
+ "Special Summon Collection A",
+ "Special Summon Collection B",
+ "Special Summon Collection C",
+ "Equipment Collection",
+ "Continuous Spell/Trap A",
+ "Continuous Spell/Trap B",
+ "Quick/Counter Collection",
+ "Direct Damage Collection",
+ "Direct Attack Collection",
+ "Monster Destroy Collection",
+]
+
+challenges: List[str] = [
+ "LD01 All except Level 4 forbidden Unlock",
+ "LD02 Medium/high Level forbidden Unlock",
+ "LD03 ATK 1500 or more forbidden Unlock",
+ "LD04 Flip Effects forbidden Unlock",
+ "LD05 Tributes forbidden Unlock",
+ "LD06 Traps forbidden Unlock",
+ "LD07 Large Deck A Unlock",
+ "LD08 Large Deck B Unlock",
+ "LD09 Sets Forbidden Unlock",
+ "LD10 All except LV monsters forbidden Unlock",
+ "LD11 All except Fairies forbidden Unlock",
+ "LD12 All except Wind forbidden Unlock",
+ "LD13 All except monsters forbidden Unlock",
+ "LD14 Level 3 or below forbidden Unlock",
+ "LD15 DEF 1500 or less forbidden Unlock",
+ "LD16 Effect Monsters forbidden Unlock",
+ "LD17 Spells forbidden Unlock",
+ "LD18 Attacks forbidden Unlock",
+ "LD19 All except E-Hero's forbidden Unlock",
+ "LD20 All except Warriors forbidden Unlock",
+ "LD21 All except Dark forbidden Unlock",
+ "LD22 All limited cards forbidden Unlock",
+ "LD23 Refer to Mar 05 Banlist Unlock",
+ "LD24 Refer to Sept 04 Banlist Unlock",
+ "LD25 Low Life Points Unlock",
+ "LD26 All except Toons forbidden Unlock",
+ "LD27 All except Spirits forbidden Unlock",
+ "LD28 All except Dragons forbidden Unlock",
+ "LD29 All except Spellcasters forbidden Unlock",
+ "LD30 All except Light forbidden Unlock",
+ "LD31 All except Fire forbidden Unlock",
+ "LD32 Decks with multiples forbidden Unlock",
+ "LD33 Special Summons forbidden Unlock",
+ "LD34 Normal Summons forbidden Unlock",
+ "LD35 All except Zombies forbidden Unlock",
+ "LD36 All except Earth forbidden Unlock",
+ "LD37 All except Water forbidden Unlock",
+ "LD38 Refer to Mar 04 Banlist Unlock",
+ "LD39 Monsters forbidden Unlock",
+ "LD40 Refer to Sept 05 Banlist Unlock",
+ "LD41 Refer to Sept 03 Banlist Unlock",
+ "TD01 Battle Damage Unlock",
+ "TD02 Deflected Damage Unlock",
+ "TD03 Normal Summon Unlock",
+ "TD04 Ritual Summon Unlock",
+ "TD05 Special Summon A Unlock",
+ "TD06 20x Spell Unlock",
+ "TD07 10x Trap Unlock",
+ "TD08 Draw Unlock",
+ "TD09 Hand Destruction Unlock",
+ "TD10 During Opponent's Turn Unlock",
+ "TD11 Recover Unlock",
+ "TD12 Remove Monsters by Effect Unlock",
+ "TD13 Flip Summon Unlock",
+ "TD14 Special Summon B Unlock",
+ "TD15 Token Unlock",
+ "TD16 Union Unlock",
+ "TD17 10x Quick Spell Unlock",
+ "TD18 The Forbidden Unlock",
+ "TD19 20 Turns Unlock",
+ "TD20 Deck Destruction Unlock",
+ "TD21 Victory D. Unlock",
+ "TD22 The Preventers Fight Back Unlock",
+ "TD23 Huge Revolution Unlock",
+ "TD24 Victory in 5 Turns Unlock",
+ "TD25 Moth Grows Up Unlock",
+ "TD26 Magnetic Power Unlock",
+ "TD27 Dark Sage Unlock",
+ "TD28 Direct Damage Unlock",
+ "TD29 Destroy Monsters in Battle Unlock",
+ "TD30 Tribute Summon Unlock",
+ "TD31 Special Summon C Unlock",
+ "TD32 Toon Unlock",
+ "TD33 10x Counter Unlock",
+ "TD34 Destiny Board Unlock",
+ "TD35 Huge Damage in a Turn Unlock",
+ "TD36 V-Z In the House Unlock",
+ "TD37 Uria, Lord of Searing Flames Unlock",
+ "TD38 Hamon, Lord of Striking Thunder Unlock",
+ "TD39 Raviel, Lord of Phantasms Unlock",
+ "TD40 Make a Chain Unlock",
+ "TD41 The Gatekeeper Stands Tall Unlock",
+ "TD42 Serious Damage Unlock",
+ "TD43 Return Monsters with Effects Unlock",
+ "TD44 Fusion Summon Unlock",
+ "TD45 Big Damage at once Unlock",
+ "TD46 XYZ In the House Unlock",
+ "TD47 Spell Counter Unlock",
+ "TD48 Destroy Monsters with Effects Unlock",
+ "TD49 Plunder Unlock",
+ "TD50 Dark Scorpion Combination Unlock",
+]
+
+excluded_items: List[str] = [
+ "All Normal Monsters",
+ "All Effect Monsters",
+ "All Fusion Monsters",
+ "All Traps",
+ "All Spells",
+ "All at Random",
+ "5000DP",
+ "Remote",
+]
+
+useful: List[str] = [
+ "Banlist March 2004",
+ "Banlist September 2004",
+ "Banlist March 2005",
+ "Banlist September 2005",
+]
diff --git a/worlds/yugioh06/locations.py b/worlds/yugioh06/locations.py
new file mode 100644
index 00000000..f495bfed
--- /dev/null
+++ b/worlds/yugioh06/locations.py
@@ -0,0 +1,213 @@
+Bonuses = {
+ "Duelist Bonus Level 1": 1,
+ "Duelist Bonus Level 2": 2,
+ "Duelist Bonus Level 3": 3,
+ "Duelist Bonus Level 4": 4,
+ "Duelist Bonus Level 5": 5,
+ "Battle Damage": 6,
+ "Battle Damage Only Bonus": 7,
+ "Max ATK Bonus": 8,
+ "Max Damage Bonus": 9,
+ "Destroyed in Battle Bonus": 10,
+ "Spell Card Bonus": 11,
+ "Trap Card Bonus": 12,
+ "Tribute Summon Bonus": 13,
+ "Fusion Summon Bonus": 14,
+ "Ritual Summon Bonus": 15,
+ "No Special Summon Bonus": 16,
+ "No Spell Cards Bonus": 17,
+ "No Trap Cards Bonus": 18,
+ "No Damage Bonus": 19,
+ "Over 20000 LP Bonus": 20,
+ "Low LP Bonus": 21,
+ "Extremely Low LP Bonus": 22,
+ "Low Deck Bonus": 23,
+ "Extremely Low Deck Bonus": 24,
+ "Effect Damage Only Bonus": 25,
+ "No More Cards Bonus": 26,
+ "Opponent's Turn Finish Bonus": 27,
+ "Exactly 0 LP Bonus": 28,
+ "Reversal Finish Bonus": 29,
+ "Quick Finish Bonus": 30,
+ "Exodia Finish Bonus": 31,
+ "Last Turn Finish Bonus": 32,
+ "Final Countdown Finish Bonus": 33,
+ "Destiny Board Finish Bonus": 34,
+ "Yata-Garasu Finish Bonus": 35,
+ "Skull Servant Finish Bonus": 36,
+ "Konami Bonus": 37,
+}
+
+Limited_Duels = {
+ "LD01 All except Level 4 forbidden": 38,
+ "LD02 Medium/high Level forbidden": 39,
+ "LD03 ATK 1500 or more forbidden": 40,
+ "LD04 Flip Effects forbidden": 41,
+ "LD05 Tributes forbidden": 42,
+ "LD06 Traps forbidden": 43,
+ "LD07 Large Deck A": 44,
+ "LD08 Large Deck B": 45,
+ "LD09 Sets Forbidden": 46,
+ "LD10 All except LV monsters forbidden": 47,
+ "LD11 All except Fairies forbidden": 48,
+ "LD12 All except Wind forbidden": 49,
+ "LD13 All except monsters forbidden": 50,
+ "LD14 Level 3 or below forbidden": 51,
+ "LD15 DEF 1500 or less forbidden": 52,
+ "LD16 Effect Monsters forbidden": 53,
+ "LD17 Spells forbidden": 54,
+ "LD18 Attacks forbidden": 55,
+ "LD19 All except E-Hero's forbidden": 56,
+ "LD20 All except Warriors forbidden": 57,
+ "LD21 All except Dark forbidden": 58,
+ "LD22 All limited cards forbidden": 59,
+ "LD23 Refer to Mar 05 Banlist": 60,
+ "LD24 Refer to Sept 04 Banlist": 61,
+ "LD25 Low Life Points": 62,
+ "LD26 All except Toons forbidden": 63,
+ "LD27 All except Spirits forbidden": 64,
+ "LD28 All except Dragons forbidden": 65,
+ "LD29 All except Spellcasters forbidden": 66,
+ "LD30 All except Light forbidden": 67,
+ "LD31 All except Fire forbidden": 68,
+ "LD32 Decks with multiples forbidden": 69,
+ "LD33 Special Summons forbidden": 70,
+ "LD34 Normal Summons forbidden": 71,
+ "LD35 All except Zombies forbidden": 72,
+ "LD36 All except Earth forbidden": 73,
+ "LD37 All except Water forbidden": 74,
+ "LD38 Refer to Mar 04 Banlist": 75,
+ "LD39 Monsters forbidden": 76,
+ "LD40 Refer to Sept 05 Banlist": 77,
+ "LD41 Refer to Sept 03 Banlist": 78,
+}
+
+Theme_Duels = {
+ "TD01 Battle Damage": 79,
+ "TD02 Deflected Damage": 80,
+ "TD03 Normal Summon": 81,
+ "TD04 Ritual Summon": 82,
+ "TD05 Special Summon A": 83,
+ "TD06 20x Spell": 84,
+ "TD07 10x Trap": 85,
+ "TD08 Draw": 86,
+ "TD09 Hand Destruction": 87,
+ "TD10 During Opponent's Turn": 88,
+ "TD11 Recover": 89,
+ "TD12 Remove Monsters by Effect": 90,
+ "TD13 Flip Summon": 91,
+ "TD14 Special Summon B": 92,
+ "TD15 Token": 93,
+ "TD16 Union": 94,
+ "TD17 10x Quick Spell": 95,
+ "TD18 The Forbidden": 96,
+ "TD19 20 Turns": 97,
+ "TD20 Deck Destruction": 98,
+ "TD21 Victory D.": 99,
+ "TD22 The Preventers Fight Back": 100,
+ "TD23 Huge Revolution": 101,
+ "TD24 Victory in 5 Turns": 102,
+ "TD25 Moth Grows Up": 103,
+ "TD26 Magnetic Power": 104,
+ "TD27 Dark Sage": 105,
+ "TD28 Direct Damage": 106,
+ "TD29 Destroy Monsters in Battle": 107,
+ "TD30 Tribute Summon": 108,
+ "TD31 Special Summon C": 109,
+ "TD32 Toon": 110,
+ "TD33 10x Counter": 111,
+ "TD34 Destiny Board": 112,
+ "TD35 Huge Damage in a Turn": 113,
+ "TD36 V-Z In the House": 114,
+ "TD37 Uria, Lord of Searing Flames": 115,
+ "TD38 Hamon, Lord of Striking Thunder": 116,
+ "TD39 Raviel, Lord of Phantasms": 117,
+ "TD40 Make a Chain": 118,
+ "TD41 The Gatekeeper Stands Tall": 119,
+ "TD42 Serious Damage": 120,
+ "TD43 Return Monsters with Effects": 121,
+ "TD44 Fusion Summon": 122,
+ "TD45 Big Damage at once": 123,
+ "TD46 XYZ In the House": 124,
+ "TD47 Spell Counter": 125,
+ "TD48 Destroy Monsters with Effects": 126,
+ "TD49 Plunder": 127,
+ "TD50 Dark Scorpion Combination": 128,
+}
+
+Campaign_Opponents = {
+ "Campaign Tier 1: 1 Win": 129,
+ "Campaign Tier 1: 3 Wins A": 130,
+ "Campaign Tier 1: 3 Wins B": 131,
+ "Campaign Tier 1: 5 Wins A": 132,
+ "Campaign Tier 1: 5 Wins B": 133,
+ "Campaign Tier 2: 1 Win": 134,
+ "Campaign Tier 2: 3 Wins A": 135,
+ "Campaign Tier 2: 3 Wins B": 136,
+ "Campaign Tier 2: 5 Wins A": 137,
+ "Campaign Tier 2: 5 Wins B": 138,
+ "Campaign Tier 3: 1 Win": 139,
+ "Campaign Tier 3: 3 Wins A": 140,
+ "Campaign Tier 3: 3 Wins B": 141,
+ "Campaign Tier 3: 5 Wins A": 142,
+ "Campaign Tier 3: 5 Wins B": 143,
+ "Campaign Tier 4: 5 Wins A": 144,
+ "Campaign Tier 4: 5 Wins B": 145,
+}
+
+special = {
+ "Campaign Tier 5: Column 1 Win": 146,
+ "Campaign Tier 5: Column 2 Win": 147,
+ "Campaign Tier 5: Column 3 Win": 148,
+ "Campaign Tier 5: Column 4 Win": 149,
+ # "Campaign Final Boss Win": 150,
+}
+
+Required_Cards = {
+ "Obtain all pieces of Exodia": 154,
+ "Obtain Final Countdown": 155,
+ "Obtain Victory Dragon": 156,
+ "Obtain Ojama Delta Hurricane and its required cards": 157,
+ "Obtain Huge Revolution and its required cards": 158,
+ "Obtain Perfectly Ultimate Great Moth and its required cards": 159,
+ "Obtain Valkyrion the Magna Warrior and its pieces": 160,
+ "Obtain Dark Sage and its required cards": 161,
+ "Obtain Destiny Board and its letters": 162,
+ "Obtain all XYZ-Dragon Cannon fusions and their materials": 163,
+ "Obtain VWXYZ-Dragon Catapult Cannon and the fusion materials": 164,
+ "Obtain Hamon, Lord of Striking Thunder": 165,
+ "Obtain Raviel, Lord of Phantasms": 166,
+ "Obtain Uria, Lord of Searing Flames": 167,
+ "Obtain Gate Guardian and its pieces": 168,
+ "Obtain Dark Scorpion Combination and its required cards": 169,
+}
+
+collection_events = {
+ "Ojama Delta Hurricane and required cards": None,
+ "Huge Revolution and its required cards": None,
+ "Perfectly Ultimate Great Moth and its required cards": None,
+ "Valkyrion the Magna Warrior and its pieces": None,
+ "Dark Sage and its required cards": None,
+ "Destiny Board and its letters": None,
+ "XYZ-Dragon Cannon fusions and their materials": None,
+ "VWXYZ-Dragon Catapult Cannon and the fusion materials": None,
+ "Gate Guardian and its pieces": None,
+ "Dark Scorpion Combination and its required cards": None,
+ "Can Exodia Win": None,
+ "Can Yata Lock": None,
+ "Can Stall with Monsters": None,
+ "Can Stall with ST": None,
+ "Can Last Turn Win": None,
+ "Has Back-row removal": None,
+}
+
+
+def get_beat_challenge_events(self):
+ beat_events = {}
+ for limited in Limited_Duels.keys():
+ if limited not in self.removed_challenges:
+ beat_events[limited + " Complete"] = None
+ for theme in Theme_Duels.keys():
+ if theme not in self.removed_challenges:
+ beat_events[theme + " Complete"] = None
+ return beat_events
diff --git a/worlds/yugioh06/logic.py b/worlds/yugioh06/logic.py
new file mode 100644
index 00000000..3227cbfe
--- /dev/null
+++ b/worlds/yugioh06/logic.py
@@ -0,0 +1,28 @@
+from typing import List
+
+from BaseClasses import CollectionState
+
+core_booster: List[str] = [
+ "LEGEND OF B.E.W.D.",
+ "METAL RAIDERS",
+ "PHARAOH'S SERVANT",
+ "PHARAONIC GUARDIAN",
+ "SPELL RULER",
+ "LABYRINTH OF NIGHTMARE",
+ "LEGACY OF DARKNESS",
+ "MAGICIAN'S FORCE",
+ "DARK CRISIS",
+ "INVASION OF CHAOS",
+ "ANCIENT SANCTUARY",
+ "SOUL OF THE DUELIST",
+ "RISE OF DESTINY",
+ "FLAMING ETERNITY",
+ "THE LOST MILLENIUM",
+ "CYBERNETIC REVOLUTION",
+ "ELEMENTAL ENERGY",
+ "SHADOW OF INFINITY",
+]
+
+
+def yugioh06_difficulty(state: CollectionState, player: int, amount: int):
+ return state.has_from_list(core_booster, player, amount)
diff --git a/worlds/yugioh06/opponents.py b/worlds/yugioh06/opponents.py
new file mode 100644
index 00000000..1746b565
--- /dev/null
+++ b/worlds/yugioh06/opponents.py
@@ -0,0 +1,264 @@
+from typing import Dict, List, NamedTuple, Optional, Union
+
+from BaseClasses import MultiWorld
+from worlds.generic.Rules import CollectionRule
+
+from worlds.yugioh06 import item_to_index, tier_1_opponents, yugioh06_difficulty
+from worlds.yugioh06.locations import special
+
+
+class OpponentData(NamedTuple):
+ id: int
+ name: str
+ campaign_info: List[str]
+ tier: int
+ column: int
+ card_id: int = 0
+ deck_name_id: int = 0
+ deck_file: str = ""
+ difficulty: int = 1
+ additional_info: List[str] = []
+
+ def tier(self, tier):
+ self.tier = tier
+
+ def column(self, column):
+ self.column = column
+
+
+challenge_opponents = [
+ # Theme
+ OpponentData(27, "Exarion Universe", [], 1, 1, 5452, 13001, "deck/theme_001.ydc\x00\x00\x00\x00", 0),
+ OpponentData(28, "Stone Statue of the Aztecs", [], 4, 1, 4754, 13002, "deck/theme_002.ydc\x00\x00\x00\x00", 3),
+ OpponentData(29, "Raging Flame Sprite", [], 1, 1, 6189, 13003, "deck/theme_003.ydc\x00\x00\x00\x00", 0),
+ OpponentData(30, "Princess Pikeru", [], 1, 1, 6605, 13004, "deck/theme_004.ydc\x00\x00\x00\x00", 0),
+ OpponentData(31, "Princess Curran", ["Quick-Finish"], 1, 1, 6606, 13005, "deck/theme_005.ydc\x00\x00\x00\x00", 0,
+ ["Has Back-row removal"]),
+ OpponentData(32, "Gearfried the Iron Knight", ["Quick-Finish"], 2, 1, 5059, 13006,
+ "deck/theme_006.ydc\x00\x00\x00\x00", 1),
+ OpponentData(33, "Zaborg the Thunder Monarch", [], 3, 1, 5965, 13007, "deck/theme_007.ydc\x00\x00\x00\x00", 2),
+ OpponentData(34, "Kycoo the Ghost Destroyer", ["Quick-Finish"], 3, 1, 5248, 13008,
+ "deck/theme_008.ydc\x00\x00\x00\x00"),
+ OpponentData(35, "Penguin Soldier", ["Quick-Finish"], 1, 1, 4608, 13009, "deck/theme_009.ydc\x00\x00\x00\x00", 0),
+ OpponentData(36, "Green Gadget", [], 5, 1, 6151, 13010, "deck/theme_010.ydc\x00\x00\x00\x00", 5),
+ OpponentData(37, "Guardian Sphinx", ["Quick-Finish"], 3, 1, 5422, 13011, "deck/theme_011.ydc\x00\x00\x00\x00", 3),
+ OpponentData(38, "Cyber-Tech Alligator", [], 2, 1, 4790, 13012, "deck/theme_012.ydc\x00\x00\x00\x00", 1),
+ OpponentData(39, "UFOroid Fighter", [], 3, 1, 6395, 13013, "deck/theme_013.ydc\x00\x00\x00\x00", 2),
+ OpponentData(40, "Relinquished", [], 3, 1, 4737, 13014, "deck/theme_014.ydc\x00\x00\x00\x00", 2),
+ OpponentData(41, "Manticore of Darkness", [], 2, 1, 5881, 13015, "deck/theme_015.ydc\x00\x00\x00\x00", 1),
+ OpponentData(42, "Vampire Lord", [], 3, 1, 5410, 13016, "deck/theme_016.ydc\x00\x00\x00\x00", 2),
+ OpponentData(43, "Gigantes", ["Quick-Finish"], 3, 1, 5831, 13017, "deck/theme_017.ydc\x00\x00\x00\x00", 2),
+ OpponentData(44, "Insect Queen", ["Quick-Finish"], 2, 1, 4768, 13018, "deck/theme_018.ydc\x00\x00\x00\x00", 1),
+ OpponentData(45, "Second Goblin", ["Quick-Finish"], 1, 1, 5587, 13019, "deck/theme_019.ydc\x00\x00\x00\x00", 0),
+ OpponentData(46, "Toon Summoned Skull", [], 4, 1, 4735, 13020, "deck/theme_020.ydc\x00\x00\x00\x00", 3),
+ OpponentData(47, "Iron Blacksmith Kotetsu", [], 2, 1, 5769, 13021, "deck/theme_021.ydc\x00\x00\x00\x00", 1),
+ OpponentData(48, "Magician of Faith", [], 1, 1, 4434, 13022, "deck/theme_022.ydc\x00\x00\x00\x00", 0),
+ OpponentData(49, "Mask of Darkness", [], 1, 1, 4108, 13023, "deck/theme_023.ydc\x00\x00\x00\x00", 0),
+ OpponentData(50, "Dark Ruler Vandalgyon", [], 3, 1, 6410, 13024, "deck/theme_024.ydc\x00\x00\x00\x00", 2),
+ OpponentData(51, "Aussa the Earth Charmer", ["Quick-Finish"], 2, 1, 6335, 13025,
+ "deck/theme_025.ydc\x00\x00\x00\x00", 1),
+ OpponentData(52, "Exodia Necross", ["Quick-Finish"], 2, 1, 5701, 13026, "deck/theme_026.ydc\x00\x00\x00\x00", 1),
+ OpponentData(53, "Dark Necrofear", [], 3, 1, 5222, 13027, "deck/theme_027.ydc\x00\x00\x00\x00", 2),
+ OpponentData(54, "Demise, King of Armageddon", [], 4, 1, 6613, 13028, "deck/theme_028.ydc\x00\x00\x00\x00", 2),
+ OpponentData(55, "Yamata Dragon", [], 3, 1, 5377, 13029, "deck/theme_029.ydc\x00\x00\x00\x00", 2),
+ OpponentData(56, "Blue-Eyes Ultimate Dragon", [], 3, 1, 4386, 13030, "deck/theme_030.ydc\x00\x00\x00\x00", 2),
+ OpponentData(57, "Wave-Motion Cannon", [], 4, 1, 5614, 13031, "deck/theme_031.ydc\x00\x00\x00\x00", 3,
+ ["Has Back-row removal"]),
+ # Unused opponent
+ # OpponentData(58, "Yata-Garasu", [], 1, 1, 5375, 13032, "deck/theme_031.ydc\x00\x00\x00\x00"),
+ # Unused opponent
+ # OpponentData(59, "Makyura the Destructor", [], 1, 1, 5285, 13033, "deck/theme_031.ydc\x00\x00\x00\x00"),
+ OpponentData(60, "Morphing Jar", [], 5, 1, 4597, 13034, "deck/theme_034.ydc\x00\x00\x00\x00", 4),
+ OpponentData(61, "Spirit Reaper", [], 2, 1, 5526, 13035, "deck/theme_035.ydc\x00\x00\x00\x00", 1),
+ OpponentData(62, "Victory D.", [], 3, 1, 5868, 13036, "deck/theme_036.ydc\x00\x00\x00\x00", 2),
+ OpponentData(63, "VWXYZ-Dragon Catapult Cannon", ["Quick-Finish"], 3, 1, 6484, 13037,
+ "deck/theme_037.ydc\x00\x00\x00\x00", 2),
+ OpponentData(64, "XYZ-Dragon Cannon", [], 2, 1, 5556, 13038, "deck/theme_038.ydc\x00\x00\x00\x00", 1),
+ OpponentData(65, "Uria, Lord of Searing Flames", [], 4, 1, 6563, 13039, "deck/theme_039.ydc\x00\x00\x00\x00", 3),
+ OpponentData(66, "Hamon, Lord of Striking Thunder", [], 4, 1, 6564, 13040, "deck/theme_040.ydc\x00\x00\x00\x00", 3),
+ OpponentData(67, "Raviel, Lord of Phantasms TD", [], 4, 1, 6565, 13041, "deck/theme_041.ydc\x00\x00\x00\x00", 3),
+ OpponentData(68, "Ojama Trio", [], 1, 1, 5738, 13042, "deck/theme_042.ydc\x00\x00\x00\x00", 0),
+ OpponentData(69, "People Running About", ["Quick-Finish"], 1, 1, 5578, 13043, "deck/theme_043.ydc\x00\x00\x00\x00",
+ 0),
+ OpponentData(70, "Cyber-Stein", [], 5, 1, 4426, 13044, "deck/theme_044.ydc\x00\x00\x00\x00", 4),
+ OpponentData(71, "Winged Kuriboh LV10", [], 4, 1, 6406, 13045, "deck/theme_045.ydc\x00\x00\x00\x00", 3),
+ OpponentData(72, "Blue-Eyes Shining Dragon", [], 3, 1, 6082, 13046, "deck/theme_046.ydc\x00\x00\x00\x00", 2),
+ OpponentData(73, "Perfectly Ultimate Great Moth", ["Quick-Finish"], 3, 1, 4073, 13047,
+ "deck/theme_047.ydc\x00\x00\x00\x00", 2),
+ OpponentData(74, "Gate Guardian", [], 4, 1, 4380, 13048, "deck/theme_048.ydc\x00\x00\x00\x00", 2),
+ OpponentData(75, "Valkyrion the Magna Warrior", [], 3, 1, 5002, 13049, "deck/theme_049.ydc\x00\x00\x00\x00", 2),
+ OpponentData(76, "Dark Sage", [], 4, 1, 5230, 13050, "deck/theme_050.ydc\x00\x00\x00\x00", 3),
+ OpponentData(77, "Don Zaloog", [], 4, 1, 5426, 13051, "deck/theme_051.ydc\x00\x00\x00\x00", 3),
+ OpponentData(78, "Blast Magician", ["Quick-Finish"], 2, 1, 6250, 13052, "deck/theme_052.ydc\x00\x00\x00\x00", 1),
+ # Limited
+ OpponentData(79, "Zombyra the Dark", [], 5, 1, 5245, 23000, "deck/limit_000.ydc\x00\x00\x00\x00", 5),
+ OpponentData(80, "Goblin Attack Force", [], 4, 1, 5145, 23001, "deck/limit_001.ydc\x00\x00\x00\x00", 3),
+ OpponentData(81, "Giant Kozaky", [], 4, 1, 6420, 23002, "deck/limit_002.ydc\x00\x00\x00\x00", 4),
+ OpponentData(82, "Big Shield Gardna", ["Quick-Finish"], 2, 1, 4764, 23003, "deck/limit_003.ydc\x00\x00\x00\x00", 1),
+ OpponentData(83, "Panther Warrior", [], 3, 1, 4751, 23004, "deck/limit_004.ydc\x00\x00\x00\x00", 2),
+ OpponentData(84, "Silent Magician LV4", ["Quick-Finish"], 2, 1, 6167, 23005, "deck/limit_005.ydc\x00\x00\x00\x00",
+ 1),
+ OpponentData(85, "Summoned Skull", [], 4, 1, 4028, 23006, "deck/limit_006.ydc\x00\x00\x00\x00", 3),
+ OpponentData(86, "Ancient Gear Golem", [], 5, 1, 6315, 23007, "deck/limit_007.ydc\x00\x00\x00\x00", 5),
+ OpponentData(87, "Chaos Sorcerer", [], 5, 1, 5833, 23008, "deck/limit_008.ydc\x00\x00\x00\x00", 5),
+ OpponentData(88, "Breaker the Magical Warrior", [], 5, 1, 5655, 23009, "deck/limit_009.ydc\x00\x00\x00\x00", 4),
+ OpponentData(89, "Dark Magician of Chaos", [], 4, 1, 5880, 23010, "deck/limit_010.ydc\x00\x00\x00\x00", 3),
+ OpponentData(90, "Stealth Bird", ["Quick-Finish"], 2, 1, 5882, 23011, "deck/limit_011.ydc\x00\x00\x00\x00", 1),
+ OpponentData(91, "Rapid-Fire Magician", ["Quick-Finish"], 2, 1, 6500, 23012, "deck/limit_012.ydc\x00\x00\x00\x00",
+ 1),
+ OpponentData(92, "Morphing Jar #2", [], 5, 1, 4969, 23013, "deck/limit_013.ydc\x00\x00\x00\x00", 4),
+ OpponentData(93, "Cyber Jar", [], 5, 1, 4913, 23014, "deck/limit_014.ydc\x00\x00\x00\x00", 4),
+ # Unused/Broken
+ # OpponentData(94, "Exodia the Forbidden One", [], 1, 1, 4027, 23015, "deck/limit_015.ydc\x00\x00\x00\x00"),
+ OpponentData(94, "Dark Paladin", [], 4, 1, 5628, 23016, "deck/limit_016.ydc\x00\x00\x00\x00", 3),
+ OpponentData(95, "F.G.D.", [], 5, 1, 5502, 23017, "deck/limit_017.ydc\x00\x00\x00\x00", 4),
+ OpponentData(96, "Blue-Eyes Toon Dragon", ["Quick-Finish"], 2, 1, 4773, 23018, "deck/limit_018.ydc\x00\x00\x00\x00",
+ 1),
+ OpponentData(97, "Tsukuyomi", [], 3, 1, 5780, 23019, "deck/limit_019.ydc\x00\x00\x00\x00", 2),
+ OpponentData(98, "Silent Swordsman LV3", ["Quick-Finish"], 2, 1, 6162, 23020, "deck/limit_020.ydc\x00\x00\x00\x00",
+ 2),
+ OpponentData(99, "Elemental Hero Flame Wingman", ["Quick-Finish"], 2, 1, 6344, 23021,
+ "deck/limit_021.ydc\x00\x00\x00\x00", 0),
+ OpponentData(100, "Armed Dragon LV7", ["Quick-Finish"], 2, 1, 6107, 23022, "deck/limit_022.ydc\x00\x00\x00\x00", 0),
+ OpponentData(101, "Alkana Knight Joker", ["Quick-Finish"], 1, 1, 6454, 23023, "deck/limit_023.ydc\x00\x00\x00\x00",
+ 0),
+ OpponentData(102, "Sorcerer of Dark Magic", [], 4, 1, 6086, 23024, "deck/limit_024.ydc\x00\x00\x00\x00", 3),
+ OpponentData(103, "Shinato, King of a Higher Plane", [], 4, 1, 5697, 23025, "deck/limit_025.ydc\x00\x00\x00\x00",
+ 3),
+ OpponentData(104, "Ryu Kokki", [], 5, 1, 5902, 23026, "deck/limit_026.ydc\x00\x00\x00\x00", 4),
+ OpponentData(105, "Cyber Dragon", [], 5, 1, 6390, 23027, "deck/limit_027.ydc\x00\x00\x00\x00", 4),
+ OpponentData(106, "Dark Dreadroute", ["Quick-Finish"], 3, 1, 6405, 23028, "deck/limit_028.ydc\x00\x00\x00\x00", 2),
+ OpponentData(107, "Ultimate Insect LV7", ["Quick-Finish"], 3, 1, 6319, 23029, "deck/limit_029.ydc\x00\x00\x00\x00",
+ 2),
+ OpponentData(108, "Thestalos the Firestorm Monarch", ["Quick-Finish"], 3, 1, 6190, 23030,
+ "deck/limit_030.ydc\x00\x00\x00\x00"),
+ OpponentData(109, "Master of Oz", ["Quick-Finish"], 3, 1, 6127, 23031, "deck/limit_031.ydc\x00\x00\x00\x00", 2),
+ OpponentData(110, "Orca Mega-Fortress of Darkness", ["Quick-Finish"], 3, 1, 5896, 23032,
+ "deck/limit_032.ydc\x00\x00\x00\x00", 2),
+ OpponentData(111, "Airknight Parshath", ["Quick-Finish"], 4, 1, 5023, 23033, "deck/limit_033.ydc\x00\x00\x00\x00",
+ 3),
+ OpponentData(112, "Dark Scorpion Burglars", ["Quick-Finish"], 4, 1, 5425, 23034,
+ "deck/limit_034.ydc\x00\x00\x00\x00", 3),
+ OpponentData(113, "Gilford the Lightning", [], 4, 1, 5451, 23035, "deck/limit_035.ydc\x00\x00\x00\x00", 3),
+ OpponentData(114, "Embodiment of Apophis", [], 2, 1, 5234, 23036, "deck/limit_036.ydc\x00\x00\x00\x00", 1),
+ OpponentData(115, "Great Maju Garzett", [], 5, 1, 5768, 23037, "deck/limit_037.ydc\x00\x00\x00\x00", 4),
+ OpponentData(116, "Black Luster Soldier - Envoy of the Beginning", [], 5, 1, 5835, 23038,
+ "deck/limit_038.ydc\x00\x00\x00\x00", 4),
+ OpponentData(117, "Red-Eyes B. Dragon", [], 4, 1, 4088, 23039, "deck/limit_039.ydc\x00\x00\x00\x00", 3),
+ OpponentData(118, "Blue-Eyes White Dragon", [], 4, 1, 4007, 23040, "deck/limit_040.ydc\x00\x00\x00\x00", 3),
+ OpponentData(119, "Dark Magician", [], 4, 1, 4041, 23041, "deck/limit_041.ydc\x00\x00\x00\x00", 3),
+ OpponentData(0, "Starter", ["Quick-Finish"], 1, 1, 4064, 1510, "deck/SD0_STARTER.ydc\x00\x00", 0),
+ OpponentData(10, "DRAGON'S ROAR", ["Quick-Finish"], 2, 1, 6292, 1511, "deck/SD1_DRAGON.ydc\x00\x00\x00", 1),
+ OpponentData(11, "ZOMBIE MADNESS", ["Quick-Finish"], 2, 1, 6293, 1512, "deck/SD2_UNDEAD.ydc\x00\x00\x00", 1),
+ OpponentData(12, "BLAZING DESTRUCTION", ["Quick-Finish"], 2, 1, 6368, 1513, "deck/SD3_FIRE.ydc\x00\x00\x00\x00\x00",
+ 1,
+ ["Has Back-row removal"]),
+ OpponentData(13, "FURY FROM THE DEEP", [], 2, 1, 6376, 1514,
+ "deck/SD4_UMI.ydc\x00\x00\x00\x00\x00\x00", 1, ["Has Back-row removal"]),
+ OpponentData(15, "WARRIORS TRIUMPH", ["Quick-Finish"], 2, 1, 6456, 1515, "deck/SD5_SOLDIER.ydc\x00\x00", 1),
+ OpponentData(16, "SPELLCASTERS JUDGEMENT", ["Quick-Finish"], 2, 1, 6530, 1516, "deck/SD6_MAGICIAN.ydc\x00", 1),
+ OpponentData(17, "INVICIBLE FORTRESS", [], 2, 1, 6640, 1517, "deck/SD7_GANSEKI.ydc\x00\x00", 1),
+ OpponentData(7, "Goblin King 2", ["Quick-Finish"], 3, 3, 5973, 8007, "deck/LV2_kingG2.ydc\x00\x00\x00", 2),
+]
+
+
+def get_opponents(multiworld: Optional[MultiWorld], player: Optional[int], randomize: bool = False) -> List[
+ OpponentData]:
+ opponents_table: List[OpponentData] = [
+ # Tier 1
+ OpponentData(0, "Kuriboh", [], 1, 1, 4064, 8000, "deck/LV1_kuriboh.ydc\x00\x00"),
+ OpponentData(1, "Scapegoat", [], 1, 2, 4818, 8001, "deck/LV1_sukego.ydc\x00\x00\x00", 0,
+ ["Has Back-row removal"]),
+ OpponentData(2, "Skull Servant", [], 1, 3, 4030, 8002, "deck/LV1_waito.ydc\x00\x00\x00\x00", 0,
+ ["Has Back-row removal"]),
+ OpponentData(3, "Watapon", [], 1, 4, 6092, 8003, "deck/LV1_watapon.ydc\x00\x00", 0, ["Has Back-row removal"]),
+ OpponentData(4, "White Magician Pikeru", [], 1, 5, 5975, 8004, "deck/LV1_pikeru.ydc\x00\x00\x00"),
+ # Tier 2
+ OpponentData(5, "Battery Man C", ["Quick-Finish"], 2, 1, 6428, 8005, "deck/LV2_denti.ydc\x00\x00\x00\x00", 1),
+ OpponentData(6, "Ojama Yellow", [], 2, 2, 5811, 8006, "deck/LV2_ojama.ydc\x00\x00\x00\x00", 1,
+ ["Has Back-row removal"]),
+ OpponentData(7, "Goblin King", ["Quick-Finish"], 2, 3, 5973, 8007, "deck/LV2_kingG.ydc\x00\x00\x00\x00", 1),
+ OpponentData(8, "Des Frog", ["Quick-Finish"], 2, 4, 6424, 8008, "deck/LV2_kaeru.ydc\x00\x00\x00\x00", 1),
+ OpponentData(9, "Water Dragon", ["Quick-Finish"], 2, 5, 6481, 8009, "deck/LV2_waterD.ydc\x00\x00\x00", 1),
+ # Tier 3
+ OpponentData(10, "Red-Eyes Darkness Dragon", ["Quick-Finish"], 3, 1, 6292, 8010, "deck/LV3_RedEyes.ydc\x00\x00",
+ 2),
+ OpponentData(11, "Vampire Genesis", ["Quick-Finish"], 3, 2, 6293, 8011, "deck/LV3_vamp.ydc\x00\x00\x00\x00\x00",
+ 2),
+ OpponentData(12, "Infernal Flame Emperor", [], 3, 3, 6368, 8012, "deck/LV3_flame.ydc\x00\x00\x00\x00", 2,
+ ["Has Back-row removal"]),
+ OpponentData(13, "Ocean Dragon Lord - Neo-Daedalus", [], 3, 4, 6376, 8013, "deck/LV3_daidaros.ydc\x00", 2,
+ ["Has Back-row removal"]),
+ OpponentData(14, "Helios Duo Megiste", ["Quick-Finish"], 3, 5, 6647, 8014, "deck/LV3_heriosu.ydc\x00\x00", 2),
+ # Tier 4
+ OpponentData(15, "Gilford the Legend", ["Quick-Finish"], 4, 1, 6456, 8015, "deck/LV4_gilfo.ydc\x00\x00\x00\x00",
+ 3),
+ OpponentData(16, "Dark Eradicator Warlock", ["Quick-Finish"], 4, 2, 6530, 8016, "deck/LV4_kuromadou.ydc", 3),
+ OpponentData(17, "Guardian Exode", [], 4, 3, 6640, 8017, "deck/LV4_exodo.ydc\x00\x00\x00\x00", 3),
+ OpponentData(18, "Goldd, Wu-Lord of Dark World", ["Quick-Finish"], 4, 4, 6505, 8018, "deck/LV4_ankokukai.ydc",
+ 3),
+ OpponentData(19, "Elemental Hero Erikshieler", ["Quick-Finish"], 4, 5, 6639, 8019,
+ "deck/LV4_Ehero.ydc\x00\x00\x00\x00", 3),
+ # Tier 5
+ OpponentData(20, "Raviel, Lord of Phantasms", [], 5, 1, 6565, 8020, "deck/LV5_ravieru.ydc\x00\x00", 4),
+ OpponentData(21, "Horus the Black Flame Dragon LV8", [], 5, 2, 6100, 8021, "deck/LV5_horus.ydc\x00\x00\x00\x00",
+ 4),
+ OpponentData(22, "Stronghold", [], 5, 3, 6153, 8022, "deck/LV5_gadget.ydc\x00\x00\x00", 5),
+ OpponentData(23, "Sacred Phoenix of Nephthys", [], 5, 4, 6236, 8023, "deck/LV5_nephthys.ydc\x00", 6),
+ OpponentData(24, "Cyber End Dragon", ["Goal"], 5, 5, 6397, 8024, "deck/LV5_cyber.ydc\x00\x00\x00\x00", 7),
+ ]
+ world = multiworld.worlds[player]
+ if not randomize:
+ return opponents_table
+ opponents = opponents_table + challenge_opponents
+ start = world.random.choice([o for o in opponents if o.tier == 1 and len(o.additional_info) == 0])
+ opponents.remove(start)
+ goal = world.random.choice([o for o in opponents if "Goal" in o.campaign_info])
+ opponents.remove(goal)
+ world.random.shuffle(opponents)
+ chosen_ones = opponents[:23]
+ for item in (multiworld.precollected_items[player]):
+ if item.name in tier_1_opponents:
+ # convert item index to opponent index
+ chosen_ones.insert(item_to_index[item.name] - item_to_index["Campaign Tier 1 Column 1"], start)
+ break
+ chosen_ones.append(goal)
+ tier = 1
+ column = 1
+ recreation = []
+ for opp in chosen_ones:
+ recreation.append(OpponentData(opp.id, opp.name, opp.campaign_info, tier, column, opp.card_id,
+ opp.deck_name_id, opp.deck_file, opp.difficulty))
+ column += 1
+ if column > 5:
+ column = 1
+ tier += 1
+
+ return recreation
+
+
+def get_opponent_locations(opponent: OpponentData) -> Dict[str, Optional[Union[str, int]]]:
+ location = {opponent.name + " Beaten": "Tier " + str(opponent.tier) + " Beaten"}
+ if opponent.tier > 4 and opponent.column != 5:
+ name = "Campaign Tier 5: Column " + str(opponent.column) + " Win"
+ # return a int instead so a item can be placed at this location later
+ location[name] = special[name]
+ for info in opponent.campaign_info:
+ location[opponent.name + "-> " + info] = info
+ return location
+
+
+def get_opponent_condition(opponent: OpponentData, unlock_item: str, unlock_amount: int, player: int,
+ is_challenge: bool) -> CollectionRule:
+ if is_challenge:
+ return lambda state: (
+ state.has(unlock_item, player, unlock_amount)
+ and yugioh06_difficulty(state, player, opponent.difficulty)
+ and state.has_all(opponent.additional_info, player)
+ )
+ else:
+ return lambda state: (
+ state.has_group(unlock_item, player, unlock_amount)
+ and yugioh06_difficulty(state, player, opponent.difficulty)
+ and state.has_all(opponent.additional_info, player)
+ )
diff --git a/worlds/yugioh06/options.py b/worlds/yugioh06/options.py
new file mode 100644
index 00000000..3100f517
--- /dev/null
+++ b/worlds/yugioh06/options.py
@@ -0,0 +1,195 @@
+from dataclasses import dataclass
+
+from Options import Choice, DefaultOnToggle, PerGameCommonOptions, Range, Toggle
+
+
+class StructureDeck(Choice):
+ """Which Structure Deck you start with"""
+
+ display_name = "Structure Deck"
+ option_dragons_roar = 0
+ option_zombie_madness = 1
+ option_blazing_destruction = 2
+ option_fury_from_the_deep = 3
+ option_warriors_triumph = 4
+ option_spellcasters_judgement = 5
+ option_none = 6
+ option_random_deck = 7
+ default = 7
+
+
+class Banlist(Choice):
+ """Which Banlist you start with"""
+
+ display_name = "Banlist"
+ option_no_banlist = 0
+ option_september_2003 = 1
+ option_march_2004 = 2
+ option_september_2004 = 3
+ option_march_2005 = 4
+ option_september_2005 = 5
+ default = option_september_2005
+
+
+class FinalCampaignBossUnlockCondition(Choice):
+ """How to unlock the final campaign boss and goal for the world"""
+
+ display_name = "Final Campaign Boss unlock Condition"
+ option_campaign_opponents = 0
+ option_challenges = 1
+
+
+class FourthTier5UnlockCondition(Choice):
+ """How to unlock the fourth campaign boss"""
+
+ display_name = "Fourth Tier 5 Campaign Boss unlock Condition"
+ option_campaign_opponents = 0
+ option_challenges = 1
+
+
+class ThirdTier5UnlockCondition(Choice):
+ """How to unlock the third campaign boss"""
+
+ display_name = "Third Tier 5 Campaign Boss unlock Condition"
+ option_campaign_opponents = 0
+ option_challenges = 1
+
+
+class FinalCampaignBossChallenges(Range):
+ """Number of Limited/Theme Duels completed for the Final Campaign Boss to appear"""
+
+ display_name = "Final Campaign Boss challenges unlock amount"
+ range_start = 0
+ range_end = 91
+ default = 10
+
+
+class FourthTier5CampaignBossChallenges(Range):
+ """Number of Limited/Theme Duels completed for the Fourth Level 5 Campaign Opponent to appear"""
+
+ display_name = "Fourth Tier 5 Campaign Boss unlock amount"
+ range_start = 0
+ range_end = 91
+ default = 5
+
+
+class ThirdTier5CampaignBossChallenges(Range):
+ """Number of Limited/Theme Duels completed for the Third Level 5 Campaign Opponent to appear"""
+
+ display_name = "Third Tier 5 Campaign Boss unlock amount"
+ range_start = 0
+ range_end = 91
+ default = 2
+
+
+class FinalCampaignBossCampaignOpponents(Range):
+ """Number of Campaign Opponents Duels defeated for the Final Campaign Boss to appear"""
+
+ display_name = "Final Campaign Boss campaign opponent unlock amount"
+ range_start = 0
+ range_end = 24
+ default = 12
+
+
+class FourthTier5CampaignBossCampaignOpponents(Range):
+ """Number of Campaign Opponents Duels defeated for the Fourth Level 5 Campaign Opponent to appear"""
+
+ display_name = "Fourth Tier 5 Campaign Boss campaign opponent unlock amount"
+ range_start = 0
+ range_end = 23
+ default = 7
+
+
+class ThirdTier5CampaignBossCampaignOpponents(Range):
+ """Number of Campaign Opponents Duels defeated for the Third Level 5 Campaign Opponent to appear"""
+
+ display_name = "Third Tier 5 Campaign Boss campaign opponent unlock amount"
+ range_start = 0
+ range_end = 22
+ default = 3
+
+
+class NumberOfChallenges(Range):
+ """Number of random Limited/Theme Duels that are included. The rest will be inaccessible."""
+
+ display_name = "Number of Challenges"
+ range_start = 0
+ range_end = 91
+ default = 10
+
+
+class StartingMoney(Range):
+ """The amount of money you start with"""
+
+ display_name = "Starting Money"
+ range_start = 0
+ range_end = 100000
+ default = 3000
+
+
+class MoneyRewardMultiplier(Range):
+ """By which amount the campaign reward money is multiplied"""
+
+ display_name = "Money Reward Multiplier"
+ range_start = 1
+ range_end = 255
+ default = 20
+
+
+class NormalizeBoostersPacks(DefaultOnToggle):
+ """If enabled every booster pack costs the same otherwise vanilla cost is used"""
+
+ display_name = "Normalize Booster Packs"
+
+
+class BoosterPackPrices(Range):
+ """
+ Only Works if normalize booster packs is enabled.
+ Sets the amount that what every booster pack costs.
+ """
+
+ display_name = "Booster Pack Prices"
+ range_start = 1
+ range_end = 3000
+ default = 100
+
+
+class AddEmptyBanList(Toggle):
+ """Adds a Ban List where everything is at 3 to the item pool"""
+
+ display_name = "Add Empty Ban List"
+
+
+class CampaignOpponentsShuffle(Toggle):
+ """Replaces the campaign with random opponents from the entire game"""
+
+ display_name = "Campaign Opponents Shuffle"
+
+
+class OCGArts(Toggle):
+ """Always use the OCG artworks for cards"""
+
+ display_name = "OCG Arts"
+
+
+@dataclass
+class Yugioh06Options(PerGameCommonOptions):
+ structure_deck: StructureDeck
+ banlist: Banlist
+ final_campaign_boss_unlock_condition: FinalCampaignBossUnlockCondition
+ fourth_tier_5_campaign_boss_unlock_condition: FourthTier5UnlockCondition
+ third_tier_5_campaign_boss_unlock_condition: ThirdTier5UnlockCondition
+ final_campaign_boss_challenges: FinalCampaignBossChallenges
+ fourth_tier_5_campaign_boss_challenges: FourthTier5CampaignBossChallenges
+ third_tier_5_campaign_boss_challenges: ThirdTier5CampaignBossChallenges
+ final_campaign_boss_campaign_opponents: FinalCampaignBossCampaignOpponents
+ fourth_tier_5_campaign_boss_campaign_opponents: FourthTier5CampaignBossCampaignOpponents
+ third_tier_5_campaign_boss_campaign_opponents: ThirdTier5CampaignBossCampaignOpponents
+ number_of_challenges: NumberOfChallenges
+ starting_money: StartingMoney
+ money_reward_multiplier: MoneyRewardMultiplier
+ normalize_boosters_packs: NormalizeBoostersPacks
+ booster_pack_prices: BoosterPackPrices
+ add_empty_banlist: AddEmptyBanList
+ campaign_opponents_shuffle: CampaignOpponentsShuffle
+ ocg_arts: OCGArts
diff --git a/worlds/yugioh06/patch.bsdiff4 b/worlds/yugioh06/patch.bsdiff4
new file mode 100644
index 00000000..877884d5
Binary files /dev/null and b/worlds/yugioh06/patch.bsdiff4 differ
diff --git a/worlds/yugioh06/patches/draft.bsdiff4 b/worlds/yugioh06/patches/draft.bsdiff4
new file mode 100644
index 00000000..73980b58
Binary files /dev/null and b/worlds/yugioh06/patches/draft.bsdiff4 differ
diff --git a/worlds/yugioh06/patches/ocg.bsdiff4 b/worlds/yugioh06/patches/ocg.bsdiff4
new file mode 100644
index 00000000..8b4b6979
Binary files /dev/null and b/worlds/yugioh06/patches/ocg.bsdiff4 differ
diff --git a/worlds/yugioh06/rom.py b/worlds/yugioh06/rom.py
new file mode 100644
index 00000000..0bd3f1cb
--- /dev/null
+++ b/worlds/yugioh06/rom.py
@@ -0,0 +1,163 @@
+import hashlib
+import math
+import os
+import struct
+
+from settings import get_settings
+
+import Utils
+from worlds.Files import APProcedurePatch, APTokenMixin, APTokenTypes
+
+from worlds.AutoWorld import World
+from .items import item_to_index
+from .rom_values import banlist_ids, function_addresses, structure_deck_selection
+
+MD5Europe = "020411d3b08f5639eb8cb878283f84bf"
+MD5America = "b8a7c976b28172995fe9e465d654297a"
+
+
+class YGO06ProcedurePatch(APProcedurePatch, APTokenMixin):
+ game = "Yu-Gi-Oh! 2006"
+ hash = MD5America
+ patch_file_ending = ".apygo06"
+ result_file_ending = ".gba"
+
+ procedure = [("apply_bsdiff4", ["base_patch.bsdiff4"]), ("apply_tokens", ["token_data.bin"])]
+
+ @classmethod
+ def get_source_data(cls) -> bytes:
+ return get_base_rom_bytes()
+
+
+def write_tokens(world: World, patch: YGO06ProcedurePatch):
+ structure_deck = structure_deck_selection.get(world.options.structure_deck.value)
+ # set structure deck
+ patch.write_token(APTokenTypes.WRITE, 0x000FD0AA, struct.pack(" bytes:
+ base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None)
+ if not base_rom_bytes:
+ file_name = get_base_rom_path(file_name)
+ base_rom_bytes = bytes(Utils.read_snes_rom(open(file_name, "rb")))
+
+ basemd5 = hashlib.md5()
+ basemd5.update(base_rom_bytes)
+ md5hash = basemd5.hexdigest()
+ if MD5Europe != md5hash and MD5America != md5hash:
+ raise Exception(
+ "Supplied Base Rom does not match known MD5 for"
+ "Yu-Gi-Oh! World Championship 2006 America or Europe "
+ "Get the correct game and version, then dump it"
+ )
+ get_base_rom_bytes.base_rom_bytes = base_rom_bytes
+ return base_rom_bytes
+
+
+def get_base_rom_path(file_name: str = "") -> str:
+ if not file_name:
+ file_name = get_settings().yugioh06_settings.rom_file
+ if not os.path.exists(file_name):
+ file_name = Utils.user_path(file_name)
+ return file_name
diff --git a/worlds/yugioh06/rom_values.py b/worlds/yugioh06/rom_values.py
new file mode 100644
index 00000000..4fb31008
--- /dev/null
+++ b/worlds/yugioh06/rom_values.py
@@ -0,0 +1,38 @@
+structure_deck_selection = {
+ # DRAGON'S ROAR
+ 0: 0x1,
+ # ZOMBIE MADNESS
+ 1: 0x5,
+ # BLAZING DESTRUCTION
+ 2: 0x9,
+ # FURY FROM THE DEEP
+ 3: 0xD,
+ # Warrior'S TRIUMPH
+ 4: 0x11,
+ # SPELLCASTER'S JUDGEMENT
+ 5: 0x15,
+ # Draft Mode
+ 6: 0x1,
+}
+
+banlist_ids = {
+ # NoList
+ 0: 0x0,
+ # September 2003
+ 1: 0x5,
+ # March 2004
+ 2: 0x6,
+ # September 2004
+ 3: 0x7,
+ # March 2005
+ 4: 0x8,
+ # September 2005
+ 5: 0x9,
+}
+
+function_addresses = {
+ # Count Campaign Opponents
+ 0: 0xF0C8,
+ # Count Challenges
+ 1: 0xEF3A,
+}
diff --git a/worlds/yugioh06/ruff.toml b/worlds/yugioh06/ruff.toml
new file mode 100644
index 00000000..8acb3b14
--- /dev/null
+++ b/worlds/yugioh06/ruff.toml
@@ -0,0 +1,12 @@
+line-length = 120
+
+[lint]
+preview = true
+select = ["E", "F", "W", "I", "N", "Q", "UP", "RUF", "ISC", "T20"]
+ignore = ["RUF012", "RUF100"]
+
+[per-file-ignores]
+# The way options definitions work right now, world devs are forced to break line length requirements.
+"options.py" = ["E501"]
+# Yu Gi Oh specific: The structure of the Opponents.py file makes the line length violations acceptable.
+"Opponents.py" = ["E501"]
\ No newline at end of file
diff --git a/worlds/yugioh06/rules.py b/worlds/yugioh06/rules.py
new file mode 100644
index 00000000..53ea95b2
--- /dev/null
+++ b/worlds/yugioh06/rules.py
@@ -0,0 +1,868 @@
+from worlds.generic.Rules import add_rule
+
+from . import yugioh06_difficulty
+from .fusions import count_has_materials
+
+
+def set_rules(world):
+ player = world.player
+ multiworld = world.multiworld
+
+ location_rules = {
+ # Campaign
+ "Campaign Tier 1: 1 Win": lambda state: state.has("Tier 1 Beaten", player),
+ "Campaign Tier 1: 3 Wins A": lambda state: state.has("Tier 1 Beaten", player, 3),
+ "Campaign Tier 1: 3 Wins B": lambda state: state.has("Tier 1 Beaten", player, 3),
+ "Campaign Tier 1: 5 Wins A": lambda state: state.has("Tier 1 Beaten", player, 5),
+ "Campaign Tier 1: 5 Wins B": lambda state: state.has("Tier 1 Beaten", player, 5),
+ "Campaign Tier 2: 1 Win": lambda state: state.has("Tier 2 Beaten", player),
+ "Campaign Tier 2: 3 Wins A": lambda state: state.has("Tier 2 Beaten", player, 3),
+ "Campaign Tier 2: 3 Wins B": lambda state: state.has("Tier 2 Beaten", player, 3),
+ "Campaign Tier 2: 5 Wins A": lambda state: state.has("Tier 2 Beaten", player, 5),
+ "Campaign Tier 2: 5 Wins B": lambda state: state.has("Tier 2 Beaten", player, 5),
+ "Campaign Tier 3: 1 Win": lambda state: state.has("Tier 3 Beaten", player),
+ "Campaign Tier 3: 3 Wins A": lambda state: state.has("Tier 3 Beaten", player, 3),
+ "Campaign Tier 3: 3 Wins B": lambda state: state.has("Tier 3 Beaten", player, 3),
+ "Campaign Tier 3: 5 Wins A": lambda state: state.has("Tier 3 Beaten", player, 5),
+ "Campaign Tier 3: 5 Wins B": lambda state: state.has("Tier 3 Beaten", player, 5),
+ "Campaign Tier 4: 5 Wins A": lambda state: state.has("Tier 4 Beaten", player, 5),
+ "Campaign Tier 4: 5 Wins B": lambda state: state.has("Tier 4 Beaten", player, 5),
+
+ # Bonuses
+ "Duelist Bonus Level 1": lambda state: state.has("Tier 1 Beaten", player),
+ "Duelist Bonus Level 2": lambda state: state.has("Tier 2 Beaten", player),
+ "Duelist Bonus Level 3": lambda state: state.has("Tier 3 Beaten", player),
+ "Duelist Bonus Level 4": lambda state: state.has("Tier 4 Beaten", player),
+ "Duelist Bonus Level 5": lambda state: state.has("Tier 5 Beaten", player),
+ "Max ATK Bonus": lambda state: yugioh06_difficulty(state, player, 2),
+ "No Spell Cards Bonus": lambda state: yugioh06_difficulty(state, player, 2),
+ "No Trap Cards Bonus": lambda state: yugioh06_difficulty(state, player, 2),
+ "No Damage Bonus": lambda state: state.has_group("Campaign Boss Beaten", player, 3),
+ "Low Deck Bonus": lambda state: state.has_any(["Reasoning", "Monster Gate", "Magical Merchant"], player) and
+ yugioh06_difficulty(state, player, 3),
+ "Extremely Low Deck Bonus":
+ lambda state: state.has_any(["Reasoning", "Monster Gate", "Magical Merchant"], player) and
+ yugioh06_difficulty(state, player, 2),
+ "Opponent's Turn Finish Bonus": lambda state: yugioh06_difficulty(state, player, 2),
+ "Exactly 0 LP Bonus": lambda state: yugioh06_difficulty(state, player, 2),
+ "Reversal Finish Bonus": lambda state: yugioh06_difficulty(state, player, 2),
+ "Quick Finish Bonus": lambda state: state.has("Quick-Finish", player) or yugioh06_difficulty(state, player, 6),
+ "Exodia Finish Bonus": lambda state: state.has("Can Exodia Win", player),
+ "Last Turn Finish Bonus": lambda state: state.has("Can Last Turn Win", player),
+ "Yata-Garasu Finish Bonus": lambda state: state.has("Can Yata Lock", player),
+ "Skull Servant Finish Bonus": lambda state: state.has("Skull Servant", player) and
+ yugioh06_difficulty(state, player, 3),
+ "Konami Bonus": lambda state: state.has_all(["Messenger of Peace", "Castle of Dark Illusions", "Mystik Wok"],
+ player) or (state.has_all(["Mystik Wok", "Barox", "Cyber-Stein",
+ "Poison of the Old Man"],
+ player) and yugioh06_difficulty(state,
+ player, 8)),
+ "Max Damage Bonus": lambda state: state.has_any(["Wave-Motion Cannon", "Megamorph", "United We Stand",
+ "Mage Power"], player),
+ "Fusion Summon Bonus": lambda state: state.has_any(["Polymerization", "Fusion Gate", "Power Bond"], player),
+ "Ritual Summon Bonus": lambda state: state.has("Ritual", player),
+ "Over 20000 LP Bonus": lambda state: can_gain_lp_every_turn(state, player)
+ and state.has("Can Stall with ST", player),
+ "Low LP Bonus": lambda state: state.has("Wall of Revealing Light", player) and yugioh06_difficulty(state, player,
+ 2),
+ "Extremely Low LP Bonus": lambda state: state.has_all(["Wall of Revealing Light", "Messenger of Peace"], player)
+ and yugioh06_difficulty(state, player, 4),
+ "Effect Damage Only Bonus": lambda state: state.has_all(["Solar Flare Dragon", "UFO Turtle"], player)
+ or state.has("Wave-Motion Cannon", player)
+ or state.can_reach("Final Countdown Finish Bonus", "Location", player)
+ or state.can_reach("Destiny Board Finish Bonus", "Location", player)
+ or state.has("Can Exodia Win", player)
+ or state.has("Can Last Turn Win", player),
+ "No More Cards Bonus": lambda state: state.has_any(["Cyber Jar", "Morphing Jar",
+ "Morphing Jar #2", "Needle Worm"], player)
+ and state.has_any(["The Shallow Grave", "Spear Cretin"],
+ player) and yugioh06_difficulty(state, player, 5),
+ "Final Countdown Finish Bonus": lambda state: state.has("Final Countdown", player)
+ and state.has("Can Stall with ST", player),
+ "Destiny Board Finish Bonus": lambda state: state.has("Can Stall with Monsters", player) and
+ state.has("Destiny Board and its letters", player) and
+ state.has("A Cat of Ill Omen", player),
+
+ # Cards
+ "Obtain all pieces of Exodia": lambda state: state.has("Exodia", player),
+ "Obtain Final Countdown": lambda state: state.has("Final Countdown", player),
+ "Obtain Victory Dragon": lambda state: state.has("Victory D.", player),
+ "Obtain Ojama Delta Hurricane and its required cards":
+ lambda state: state.has("Ojama Delta Hurricane and required cards", player),
+ "Obtain Huge Revolution and its required cards":
+ lambda state: state.has("Huge Revolution and its required cards", player),
+ "Obtain Perfectly Ultimate Great Moth and its required cards":
+ lambda state: state.has("Perfectly Ultimate Great Moth and its required cards", player),
+ "Obtain Valkyrion the Magna Warrior and its pieces":
+ lambda state: state.has("Valkyrion the Magna Warrior and its pieces", player),
+ "Obtain Dark Sage and its required cards": lambda state: state.has("Dark Sage and its required cards", player),
+ "Obtain Destiny Board and its letters": lambda state: state.has("Destiny Board and its letters", player),
+ "Obtain all XYZ-Dragon Cannon fusions and their materials":
+ lambda state: state.has("XYZ-Dragon Cannon fusions and their materials", player),
+ "Obtain VWXYZ-Dragon Catapult Cannon and the fusion materials":
+ lambda state: state.has("VWXYZ-Dragon Catapult Cannon and the fusion materials", player),
+ "Obtain Hamon, Lord of Striking Thunder":
+ lambda state: state.has("Hamon, Lord of Striking Thunder", player),
+ "Obtain Raviel, Lord of Phantasms":
+ lambda state: state.has("Raviel, Lord of Phantasms", player),
+ "Obtain Uria, Lord of Searing Flames":
+ lambda state: state.has("Uria, Lord of Searing Flames", player),
+ "Obtain Gate Guardian and its pieces":
+ lambda state: state.has("Gate Guardian and its pieces", player),
+ "Obtain Dark Scorpion Combination and its required cards":
+ lambda state: state.has("Dark Scorpion Combination and its required cards", player),
+ # Collection Events
+ "Ojama Delta Hurricane and required cards":
+ lambda state: state.has_all(["Ojama Delta Hurricane", "Ojama Green", "Ojama Yellow", "Ojama Black"],
+ player),
+ "Huge Revolution and its required cards":
+ lambda state: state.has_all(["Huge Revolution", "Oppressed People", "United Resistance",
+ "People Running About"], player),
+ "Perfectly Ultimate Great Moth and its required cards":
+ lambda state: state.has_all(["Perfectly Ultimate Great Moth", "Petit Moth", "Cocoon of Evolution"], player),
+ "Valkyrion the Magna Warrior and its pieces":
+ lambda state: state.has_all(["Valkyrion the Magna Warrior", "Alpha the Magnet Warrior",
+ "Beta the Magnet Warrior", "Gamma the Magnet Warrior"], player),
+ "Dark Sage and its required cards":
+ lambda state: state.has_all(["Dark Sage", "Dark Magician", "Time Wizard"], player),
+ "Destiny Board and its letters":
+ lambda state: state.has_all(["Destiny Board", "Spirit Message 'I'", "Spirit Message 'N'",
+ "Spirit Message 'A'", "Spirit Message 'L'"], player),
+ "XYZ-Dragon Cannon fusions and their materials":
+ lambda state: state.has_all(["X-Head Cannon", "Y-Dragon Head", "Z-Metal Tank",
+ "XY-Dragon Cannon", "XZ-Tank Cannon", "YZ-Tank Dragon", "XYZ-Dragon Cannon"],
+ player),
+ "VWXYZ-Dragon Catapult Cannon and the fusion materials":
+ lambda state: state.has_all(["X-Head Cannon", "Y-Dragon Head", "Z-Metal Tank", "XYZ-Dragon Cannon",
+ "V-Tiger Jet", "W-Wing Catapult", "VW-Tiger Catapult",
+ "VWXYZ-Dragon Catapult Cannon"],
+ player),
+ "Gate Guardian and its pieces":
+ lambda state: state.has_all(["Gate Guardian", "Kazejin", "Suijin", "Sanga of the Thunder"], player),
+ "Dark Scorpion Combination and its required cards":
+ lambda state: state.has_all(["Dark Scorpion Combination", "Don Zaloog", "Dark Scorpion - Chick the Yellow",
+ "Dark Scorpion - Meanae the Thorn", "Dark Scorpion - Gorg the Strong",
+ "Cliff the Trap Remover"], player),
+ "Can Exodia Win":
+ lambda state: state.has_all(["Exodia", "Heart of the Underdog"], player),
+ "Can Last Turn Win":
+ lambda state: state.has_all(["Last Turn", "Wall of Revealing Light"], player) and
+ (state.has_any(["Jowgen the Spiritualist", "Jowls of Dark Demise", "Non Aggression Area"],
+ player)
+ or state.has_all(["Cyber-Stein", "The Last Warrior from Another Planet"], player)),
+ "Can Yata Lock":
+ lambda state: state.has_all(["Yata-Garasu", "Chaos Emperor Dragon - Envoy of the End", "Sangan"], player)
+ and state.has_any(["No Banlist", "Banlist September 2003"], player),
+ "Can Stall with Monsters":
+ lambda state: state.count_from_list_exclusive(
+ ["Spirit Reaper", "Giant Germ", "Marshmallon", "Nimble Momonga"], player) >= 2,
+ "Can Stall with ST":
+ lambda state: state.count_from_list_exclusive(["Level Limit - Area B", "Gravity Bind", "Messenger of Peace"],
+ player) >= 2,
+ "Has Back-row removal":
+ lambda state: back_row_removal(state, player)
+
+ }
+ access_rules = {
+ # Limited
+ "LD01 All except Level 4 forbidden":
+ lambda state: yugioh06_difficulty(state, player, 2),
+ "LD02 Medium/high Level forbidden":
+ lambda state: yugioh06_difficulty(state, player, 1),
+ "LD03 ATK 1500 or more forbidden":
+ lambda state: yugioh06_difficulty(state, player, 4),
+ "LD04 Flip Effects forbidden":
+ lambda state: yugioh06_difficulty(state, player, 1),
+ "LD05 Tributes forbidden":
+ lambda state: yugioh06_difficulty(state, player, 1),
+ "LD06 Traps forbidden":
+ lambda state: yugioh06_difficulty(state, player, 1),
+ "LD07 Large Deck A":
+ lambda state: yugioh06_difficulty(state, player, 4),
+ "LD08 Large Deck B":
+ lambda state: yugioh06_difficulty(state, player, 4),
+ "LD09 Sets Forbidden":
+ lambda state: yugioh06_difficulty(state, player, 1),
+ "LD10 All except LV monsters forbidden":
+ lambda state: only_level(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD11 All except Fairies forbidden":
+ lambda state: only_fairy(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD12 All except Wind forbidden":
+ lambda state: only_wind(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD13 All except monsters forbidden":
+ lambda state: yugioh06_difficulty(state, player, 3),
+ "LD14 Level 3 or below forbidden":
+ lambda state: yugioh06_difficulty(state, player, 1),
+ "LD15 DEF 1500 or less forbidden":
+ lambda state: yugioh06_difficulty(state, player, 3),
+ "LD16 Effect Monsters forbidden":
+ lambda state: only_normal(state, player) and yugioh06_difficulty(state, player, 4),
+ "LD17 Spells forbidden":
+ lambda state: yugioh06_difficulty(state, player, 3),
+ "LD18 Attacks forbidden":
+ lambda state: state.has_all(["Wave-Motion Cannon", "Stealth Bird"], player)
+ and state.count_from_list_exclusive(["Dark World Lightning", "Nobleman of Crossout",
+ "Shield Crash", "Tribute to the Doomed"], player) >= 2
+ and yugioh06_difficulty(state, player, 3),
+ "LD19 All except E-Hero's forbidden":
+ lambda state: state.has_any(["Polymerization", "Fusion Gate"], player) and
+ count_has_materials(state, ["Elemental Hero Flame Wingman",
+ "Elemental Hero Madballman",
+ "Elemental Hero Rampart Blaster",
+ "Elemental Hero Steam Healer",
+ "Elemental Hero Shining Flare Wingman",
+ "Elemental Hero Wildedge"], player) >= 3 and
+ yugioh06_difficulty(state, player, 3),
+ "LD20 All except Warriors forbidden":
+ lambda state: only_warrior(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD21 All except Dark forbidden":
+ lambda state: only_dark(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD22 All limited cards forbidden":
+ lambda state: yugioh06_difficulty(state, player, 3),
+ "LD23 Refer to Mar 05 Banlist":
+ lambda state: yugioh06_difficulty(state, player, 5),
+ "LD24 Refer to Sept 04 Banlist":
+ lambda state: yugioh06_difficulty(state, player, 5),
+ "LD25 Low Life Points":
+ lambda state: yugioh06_difficulty(state, player, 5),
+ "LD26 All except Toons forbidden":
+ lambda state: only_toons(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD27 All except Spirits forbidden":
+ lambda state: only_spirit(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD28 All except Dragons forbidden":
+ lambda state: only_dragon(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD29 All except Spellcasters forbidden":
+ lambda state: only_spellcaster(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD30 All except Light forbidden":
+ lambda state: only_light(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD31 All except Fire forbidden":
+ lambda state: only_fire(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD32 Decks with multiples forbidden":
+ lambda state: yugioh06_difficulty(state, player, 4),
+ "LD33 Special Summons forbidden":
+ lambda state: yugioh06_difficulty(state, player, 2),
+ "LD34 Normal Summons forbidden":
+ lambda state: state.has_all(["Polymerization", "King of the Swamp"], player) and
+ count_has_materials(state, ["Elemental Hero Flame Wingman",
+ "Elemental Hero Madballman",
+ "Elemental Hero Rampart Blaster",
+ "Elemental Hero Steam Healer",
+ "Elemental Hero Shining Flare Wingman",
+ "Elemental Hero Wildedge"], player) >= 3 and
+ yugioh06_difficulty(state, player, 4),
+ "LD35 All except Zombies forbidden":
+ lambda state: only_zombie(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD36 All except Earth forbidden":
+ lambda state: only_earth(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD37 All except Water forbidden":
+ lambda state: only_water(state, player) and yugioh06_difficulty(state, player, 2),
+ "LD38 Refer to Mar 04 Banlist":
+ lambda state: yugioh06_difficulty(state, player, 4),
+ "LD39 Monsters forbidden":
+ lambda state: state.has_all(["Skull Zoma", "Embodiment of Apophis"], player)
+ and yugioh06_difficulty(state, player, 5),
+ "LD40 Refer to Sept 05 Banlist":
+ lambda state: yugioh06_difficulty(state, player, 5),
+ "LD41 Refer to Sept 03 Banlist":
+ lambda state: yugioh06_difficulty(state, player, 5),
+ # Theme Duels
+ "TD01 Battle Damage":
+ lambda state: yugioh06_difficulty(state, player, 1),
+ "TD02 Deflected Damage":
+ lambda state: state.has("Fairy Box", player) and yugioh06_difficulty(state, player, 1),
+ "TD03 Normal Summon":
+ lambda state: yugioh06_difficulty(state, player, 3),
+ "TD04 Ritual Summon":
+ lambda state: yugioh06_difficulty(state, player, 3) and
+ state.has_all(["Contract with the Abyss",
+ "Manju of the Ten Thousand Hands",
+ "Senju of the Thousand Hands",
+ "Sonic Bird",
+ "Pot of Avarice",
+ "Dark Master - Zorc",
+ "Demise, King of Armageddon",
+ "The Masked Beast",
+ "Magician of Black Chaos",
+ "Dark Magic Ritual"], player),
+ "TD05 Special Summon A":
+ lambda state: yugioh06_difficulty(state, player, 3),
+ "TD06 20x Spell":
+ lambda state: state.has("Magical Blast", player) and yugioh06_difficulty(state, player, 3),
+ "TD07 10x Trap":
+ lambda state: yugioh06_difficulty(state, player, 3),
+ "TD08 Draw":
+ lambda state: state.has_any(["Self-Destruct Button", "Dark Snake Syndrome"], player) and
+ yugioh06_difficulty(state, player, 3),
+ "TD09 Hand Destruction":
+ lambda state: state.has_all(["Cyber Jar",
+ "Morphing Jar",
+ "Book of Moon",
+ "Book of Taiyou",
+ "Card Destruction",
+ "Serial Spell",
+ "Spell Reproduction",
+ "The Shallow Grave"], player) and yugioh06_difficulty(state, player, 3),
+ "TD10 During Opponent's Turn":
+ lambda state: yugioh06_difficulty(state, player, 3),
+ "TD11 Recover":
+ lambda state: can_gain_lp_every_turn(state, player) and yugioh06_difficulty(state, player, 3),
+ "TD12 Remove Monsters by Effect":
+ lambda state: state.has("Soul Release", player) and yugioh06_difficulty(state, player, 2),
+ "TD13 Flip Summon":
+ lambda state: pacman_deck(state, player) and yugioh06_difficulty(state, player, 2),
+ "TD14 Special Summon B":
+ lambda state: state.has_any(["Manticore of Darkness", "Treeborn Frog"], player) and
+ state.has("Foolish Burial", player) and
+ yugioh06_difficulty(state, player, 2),
+ "TD15 Token":
+ lambda state: state.has_all(["Dandylion", "Ojama Trio", "Stray Lambs"], player) and
+ yugioh06_difficulty(state, player, 3),
+ "TD16 Union":
+ lambda state: equip_unions(state, player) and
+ yugioh06_difficulty(state, player, 2),
+ "TD17 10x Quick Spell":
+ lambda state: quick_plays(state, player) and
+ yugioh06_difficulty(state, player, 3),
+ "TD18 The Forbidden":
+ lambda state: state.has("Can Exodia Win", player),
+ "TD19 20 Turns":
+ lambda state: state.has("Final Countdown", player) and state.has("Can Stall with ST", player) and
+ yugioh06_difficulty(state, player, 3),
+ "TD20 Deck Destruction":
+ lambda state: state.has_any(["Cyber Jar", "Morphing Jar", "Morphing Jar #2", "Needle Worm"], player)
+ and state.has_any(["The Shallow Grave", "Spear Cretin"],
+ player) and yugioh06_difficulty(state, player, 2),
+ "TD21 Victory D.":
+ lambda state: state.has("Victory D.", player) and only_dragon(state, player)
+ and yugioh06_difficulty(state, player, 3),
+ "TD22 The Preventers Fight Back":
+ lambda state: state.has("Ojama Delta Hurricane and required cards", player) and
+ state.has_all(["Rescue Cat", "Enchanting Fitting Room", "Jerry Beans Man"], player) and
+ yugioh06_difficulty(state, player, 3),
+ "TD23 Huge Revolution":
+ lambda state: state.has("Huge Revolution and its required cards", player) and
+ state.has_all(["Enchanting Fitting Room", "Jerry Beans Man"], player) and
+ yugioh06_difficulty(state, player, 3),
+ "TD24 Victory in 5 Turns":
+ lambda state: yugioh06_difficulty(state, player, 3),
+ "TD25 Moth Grows Up":
+ lambda state: state.has("Perfectly Ultimate Great Moth and its required cards", player) and
+ state.has_all(["Gokipon", "Howling Insect"], player) and
+ yugioh06_difficulty(state, player, 3),
+ "TD26 Magnetic Power":
+ lambda state: state.has("Valkyrion the Magna Warrior and its pieces", player) and
+ yugioh06_difficulty(state, player, 2),
+ "TD27 Dark Sage":
+ lambda state: state.has("Dark Sage and its required cards", player) and
+ state.has_any(["Skilled Dark Magician", "Dark Magic Curtain"], player) and
+ yugioh06_difficulty(state, player, 2),
+ "TD28 Direct Damage":
+ lambda state: yugioh06_difficulty(state, player, 2),
+ "TD29 Destroy Monsters in Battle":
+ lambda state: yugioh06_difficulty(state, player, 2),
+ "TD30 Tribute Summon":
+ lambda state: state.has("Treeborn Frog", player) and yugioh06_difficulty(state, player, 2),
+ "TD31 Special Summon C":
+ lambda state: state.count_from_list_exclusive(
+ ["Aqua Spirit", "Rock Spirit", "Spirit of Flames",
+ "Garuda the Wind Spirit", "Gigantes", "Inferno", "Megarock Dragon", "Silpheed"],
+ player) > 4 and yugioh06_difficulty(state, player, 3),
+ "TD32 Toon":
+ lambda state: only_toons(state, player) and yugioh06_difficulty(state, player, 3),
+ "TD33 10x Counter":
+ lambda state: counter_traps(state, player) and yugioh06_difficulty(state, player, 2),
+ "TD34 Destiny Board":
+ lambda state: state.has("Destiny Board and its letters", player)
+ and state.has("Can Stall with Monsters", player)
+ and state.has("A Cat of Ill Omen", player)
+ and yugioh06_difficulty(state, player, 2),
+ "TD35 Huge Damage in a Turn":
+ lambda state: state.has_all(["Cyber-Stein", "Cyber Twin Dragon", "Megamorph"], player)
+ and yugioh06_difficulty(state, player, 3),
+ "TD36 V-Z In the House":
+ lambda state: state.has("VWXYZ-Dragon Catapult Cannon and the fusion materials", player)
+ and yugioh06_difficulty(state, player, 3),
+ "TD37 Uria, Lord of Searing Flames":
+ lambda state: state.has_all(["Uria, Lord of Searing Flames",
+ "Embodiment of Apophis",
+ "Skull Zoma",
+ "Metal Reflect Slime"], player)
+ and yugioh06_difficulty(state, player, 3),
+ "TD38 Hamon, Lord of Striking Thunder":
+ lambda state: state.has("Hamon, Lord of Striking Thunder", player)
+ and yugioh06_difficulty(state, player, 3),
+ "TD39 Raviel, Lord of Phantasms":
+ lambda state: state.has_all(["Raviel, Lord of Phantasms", "Giant Germ"], player) and
+ state.count_from_list_exclusive(["Archfiend Soldier",
+ "Skull Descovery Knight",
+ "Slate Warrior",
+ "D. D. Trainer",
+ "Earthbound Spirit"], player) >= 3
+ and yugioh06_difficulty(state, player, 3),
+ "TD40 Make a Chain":
+ lambda state: state.has("Ultimate Offering", player)
+ and yugioh06_difficulty(state, player, 4),
+ "TD41 The Gatekeeper Stands Tall":
+ lambda state: state.has("Gate Guardian and its pieces", player) and
+ state.has_all(["Treeborn Frog", "Tribute Doll"], player)
+ and yugioh06_difficulty(state, player, 4),
+ "TD42 Serious Damage":
+ lambda state: yugioh06_difficulty(state, player, 3),
+ "TD43 Return Monsters with Effects":
+ lambda state: state.has_all(["Penguin Soldier", "Messenger of Peace"], player)
+ and yugioh06_difficulty(state, player, 4),
+ "TD44 Fusion Summon":
+ lambda state: state.has_all(["Fusion Gate", "Terraforming", "Dimension Fusion",
+ "Return from the Different Dimension"], player) and
+ count_has_materials(state, ["Elemental Hero Flame Wingman",
+ "Elemental Hero Madballman",
+ "Elemental Hero Rampart Blaster",
+ "Elemental Hero Steam Healer",
+ "Elemental Hero Shining Flare Wingman",
+ "Elemental Hero Wildedge"], player) >= 4 and
+ yugioh06_difficulty(state, player, 7),
+ "TD45 Big Damage at once":
+ lambda state: state.has("Wave-Motion Cannon", player)
+ and yugioh06_difficulty(state, player, 3),
+ "TD46 XYZ In the House":
+ lambda state: state.has("XYZ-Dragon Cannon fusions and their materials", player) and
+ state.has("Dimension Fusion", player),
+ "TD47 Spell Counter":
+ lambda state: spell_counter(state, player) and yugioh06_difficulty(state, player, 3),
+ "TD48 Destroy Monsters with Effects":
+ lambda state: state.has_all(["Blade Rabbit", "Dream Clown"], player) and
+ state.has("Can Stall with ST", player) and
+ yugioh06_difficulty(state, player, 3),
+ "TD49 Plunder":
+ lambda state: take_control(state, player) and yugioh06_difficulty(state, player, 5),
+ "TD50 Dark Scorpion Combination":
+ lambda state: state.has("Dark Scorpion Combination and its required cards", player) and
+ state.has_all(["Reinforcement of the Army", "Mystic Tomato"], player) and
+ yugioh06_difficulty(state, player, 3)
+ }
+ multiworld.completion_condition[player] = lambda state: state.has("Goal", player)
+
+ for loc in multiworld.get_locations(player):
+ if loc.name in location_rules:
+ add_rule(loc, location_rules[loc.name])
+ if loc.name in access_rules:
+ add_rule(multiworld.get_entrance(loc.name, player), access_rules[loc.name])
+
+
+def only_light(state, player):
+ return state.has_from_list_exclusive([
+ "Dunames Dark Witch",
+ "X-Head Cannon",
+ "Homunculus the Alchemic Being",
+ "Hysteric Fairy",
+ "Ninja Grandmaster Sasuke"], player, 2)\
+ and state.has_from_list_exclusive([
+ "Chaos Command Magician",
+ "Cybernetic Magician",
+ "Kaiser Glider",
+ "The Agent of Judgment - Saturn",
+ "Zaborg the Thunder Monarch",
+ "Cyber Dragon"], player, 1) \
+ and state.has_from_list_exclusive([
+ "D.D. Warrior Lady",
+ "Mystic Swordsman LV2",
+ "Y-Dragon Head",
+ "Z-Metal Tank",
+ ], player, 2) and state.has("Shining Angel", player)
+
+
+def only_dark(state, player):
+ return state.has_from_list_exclusive([
+ "Dark Elf",
+ "Archfiend Soldier",
+ "Mad Dog of Darkness",
+ "Vorse Raider",
+ "Skilled Dark Magician",
+ "Skull Descovery Knight",
+ "Mechanicalchaser",
+ "Dark Blade",
+ "Gil Garth",
+ "La Jinn the Mystical Genie of the Lamp",
+ "Opticlops",
+ "Zure, Knight of Dark World",
+ "Brron, Mad King of Dark World",
+ "D.D. Survivor",
+ "Exarion Universe",
+ "Kycoo the Ghost Destroyer",
+ "Regenerating Mummy"
+ ], player, 2) \
+ and state.has_any([
+ "Summoned Skull",
+ "Skull Archfiend of Lightning",
+ "The End of Anubis",
+ "Dark Ruler Ha Des",
+ "Beast of Talwar",
+ "Inferno Hammer",
+ "Jinzo",
+ "Ryu Kokki"
+ ], player) \
+ and state.has_from_list_exclusive([
+ "Legendary Fiend",
+ "Don Zaloog",
+ "Newdoria",
+ "Sangan",
+ "Spirit Reaper",
+ "Giant Germ"
+ ], player, 2) and state.has("Mystic Tomato", player)
+
+
+def only_earth(state, player):
+ return state.has_from_list_exclusive([
+ "Berserk Gorilla",
+ "Gemini Elf",
+ "Insect Knight",
+ "Toon Gemini Elf",
+ "Familiar-Possessed - Aussa",
+ "Neo Bug",
+ "Blindly Loyal Goblin",
+ "Chiron the Mage",
+ "Gearfried the Iron Knight"
+ ], player, 2) and state.has_any([
+ "Dark Driceratops",
+ "Granmarg the Rock Monarch",
+ "Hieracosphinx",
+ "Saber Beetle"
+ ], player) and state.has_from_list_exclusive([
+ "Hyper Hammerhead",
+ "Green Gadget",
+ "Red Gadget",
+ "Yellow Gadget",
+ "Dimensional Warrior",
+ "Enraged Muka Muka",
+ "Exiled Force"
+ ], player, 2) and state.has("Giant Rat", player)
+
+
+def only_water(state, player):
+ return state.has_from_list_exclusive([
+ "Gagagigo",
+ "Familiar-Possessed - Eria",
+ "7 Colored Fish",
+ "Sea Serpent Warrior of Darkness",
+ "Abyss Soldier"
+ ], player, 2) and state.has_any([
+ "Giga Gagagigo",
+ "Amphibian Beast",
+ "Terrorking Salmon",
+ "Mobius the Frost Monarch"
+ ], player) and state.has_from_list_exclusive([
+ "Revival Jam",
+ "Yomi Ship",
+ "Treeborn Frog"
+ ], player, 2) and state.has("Mother Grizzly", player)
+
+
+def only_fire(state, player):
+ return state.has_from_list_exclusive([
+ "Blazing Inpachi",
+ "Familiar-Possessed - Hiita",
+ "Great Angus",
+ "Fire Beaters"
+ ], player, 2) and state.has_any([
+ "Thestalos the Firestorm Monarch",
+ "Horus the Black Flame Dragon LV6"
+ ], player) and state.has_from_list_exclusive([
+ "Solar Flare Dragon",
+ "Tenkabito Shien",
+ "Ultimate Baseball Kid"
+ ], player, 2) and state.has("UFO Turtle", player)
+
+
+def only_wind(state, player):
+ return state.has_from_list_exclusive([
+ "Luster Dragon",
+ "Slate Warrior",
+ "Spear Dragon",
+ "Familiar-Possessed - Wynn",
+ "Harpie's Brother",
+ "Nin-Ken Dog",
+ "Cyber Harpie Lady",
+ "Oxygeddon"
+ ], player, 2) and state.has_any([
+ "Cyber-Tech Alligator",
+ "Luster Dragon #2",
+ "Armed Dragon LV5",
+ "Roc from the Valley of Haze"
+ ], player) and state.has_from_list_exclusive([
+ "Armed Dragon LV3",
+ "Twin-Headed Behemoth",
+ "Harpie Lady 1"
+ ], player, 2) and state.has("Flying Kamakiri 1", player)
+
+
+def only_fairy(state, player):
+ return state.has_any([
+ "Dunames Dark Witch",
+ "Hysteric Fairy"
+ ], player) and (state.count_from_list_exclusive([
+ "Dunames Dark Witch",
+ "Hysteric Fairy",
+ "Dancing Fairy",
+ "Zolga",
+ "Shining Angel",
+ "Kelbek",
+ "Mudora",
+ "Asura Priest",
+ "Cestus of Dagla"
+ ], player) + (state.has_any([
+ "The Agent of Judgment - Saturn",
+ "Airknight Parshath"
+ ], player))) >= 7
+
+
+def only_warrior(state, player):
+ return state.has_any([
+ "Dark Blade",
+ "Blindly Loyal Goblin",
+ "D.D. Survivor",
+ "Gearfried the Iron knight",
+ "Ninja Grandmaster Sasuke",
+ "Warrior Beaters"
+ ], player) and (state.count_from_list_exclusive([
+ "Warrior Lady of the Wasteland",
+ "Exiled Force",
+ "Mystic Swordsman LV2",
+ "Dimensional Warrior",
+ "Dandylion",
+ "D.D. Assailant",
+ "Blade Knight",
+ "D.D. Warrior Lady",
+ "Marauding Captain",
+ "Command Knight",
+ "Reinforcement of the Army"
+ ], player) + (state.has_any([
+ "Freed the Matchless General",
+ "Holy Knight Ishzark",
+ "Silent Swordsman Lv5"
+ ], player))) >= 7
+
+
+def only_zombie(state, player):
+ return state.has("Pyramid Turtle", player) \
+ and state.has_from_list_exclusive([
+ "Regenerating Mummy",
+ "Ryu Kokki",
+ "Spirit Reaper",
+ "Master Kyonshee",
+ "Curse of Vampire",
+ "Vampire Lord",
+ "Goblin Zombie",
+ "Curse of Vampire",
+ "Vampire Lord",
+ "Goblin Zombie",
+ "Book of Life",
+ "Call of the Mummy"
+ ], player, 6)
+
+
+def only_dragon(state, player):
+ return state.has_any([
+ "Luster Dragon",
+ "Spear Dragon",
+ "Cave Dragon"
+ ], player) and (state.count_from_list_exclusive([
+ "Luster Dragon",
+ "Spear Dragon",
+ "Cave Dragon"
+ "Armed Dragon LV3",
+ "Masked Dragon",
+ "Twin-Headed Behemoth",
+ "Element Dragon",
+ "Troop Dragon",
+ "Horus the Black Flame Dragon LV4",
+ "Stamping Destruction"
+ ], player) + (state.has_any([
+ "Luster Dragon #2",
+ "Armed Dragon LV5",
+ "Kaiser Glider",
+ "Horus the Black Flame Dragon LV6"
+ ], player))) >= 7
+
+
+def only_spellcaster(state, player):
+ return state.has_any([
+ "Dark Elf",
+ "Gemini Elf",
+ "Skilled Dark Magician",
+ "Toon Gemini Elf",
+ "Kycoo the Ghost Destroyer",
+ "Familiar-Possessed - Aussa"
+ ], player) and (state.count_from_list_exclusive([
+ "Dark Elf",
+ "Gemini Elf",
+ "Skilled Dark Magician",
+ "Toon Gemini Elf",
+ "Kycoo the Ghost Destroyer",
+ "Familiar-Possessed - Aussa",
+ "Breaker the magical Warrior",
+ "The Tricky",
+ "Injection Fairy Lily",
+ "Magician of Faith",
+ "Tsukuyomi",
+ "Gravekeeper's Spy",
+ "Gravekeeper's Guard",
+ "Summon Priest",
+ "Old Vindictive Magician",
+ "Apprentice Magician",
+ "Magical Dimension"
+ ], player) + (state.has_any([
+ "Chaos Command Magician",
+ "Cybernetic Magician"
+ ], player))) >= 7
+
+
+def equip_unions(state, player):
+ return (state.has_all(["Burning Beast", "Freezing Beast",
+ "Metallizing Parasite - Lunatite", "Mother Grizzly"], player) or
+ state.has_all(["Dark Blade", "Pitch-Dark Dragon",
+ "Giant Orc", "Second Goblin", "Mystic Tomato"], player) or
+ state.has_all(["Decayed Commander", "Zombie Tiger",
+ "Vampire Orchis", "Des Dendle", "Giant Rat"], player) or
+ state.has_all(["Indomitable Fighter Lei Lei", "Protective Soul Ailin",
+ "V-Tiger Jet", "W-Wing Catapult", "Shining Angel"], player) or
+ state.has_all(["X-Head Cannon", "Y-Dragon Head", "Z-Metal Tank", "Shining Angel"], player)) and\
+ state.has_any(["Frontline Base", "Formation Union", "Roll Out!"], player)
+
+
+def can_gain_lp_every_turn(state, player):
+ return state.count_from_list_exclusive([
+ "Solemn Wishes",
+ "Cure Mermaid",
+ "Dancing Fairy",
+ "Princess Pikeru",
+ "Kiseitai"], player) >= 3
+
+
+def only_normal(state, player):
+ return (state.has_from_list_exclusive([
+ "Archfiend Soldier",
+ "Gemini Elf",
+ "Insect Knight",
+ "Luster Dragon",
+ "Mad Dog of Darkness",
+ "Vorse Raider",
+ "Blazing Inpachi",
+ "Gagagigo",
+ "Mechanicalchaser",
+ "7 Colored Fish",
+ "Dark Blade",
+ "Dunames Dark Witch",
+ "Giant Red Snake",
+ "Gil Garth",
+ "Great Angus",
+ "Harpie's Brother",
+ "La Jinn the Mystical Genie of the Lamp",
+ "Neo Bug",
+ "Nin-Ken Dog",
+ "Opticlops",
+ "Sea Serpent Warrior of Darkness",
+ "X-Head Cannon",
+ "Zure, Knight of Dark World"], player, 6) and
+ state.has_any([
+ "Cyber-Tech Alligator",
+ "Summoned Skull",
+ "Giga Gagagigo",
+ "Amphibian Beast",
+ "Beast of Talwar",
+ "Luster Dragon #2",
+ "Terrorking Salmon"], player))
+
+
+def only_level(state, player):
+ return (state.has("Level Up!", player) and
+ (state.has_all(["Armed Dragon LV3", "Armed Dragon LV5"], player) +
+ state.has_all(["Horus the Black Flame Dragon LV4", "Horus the Black Flame Dragon LV6"], player) +
+ state.has_all(["Mystic Swordsman LV4", "Mystic Swordsman LV6"], player) +
+ state.has_all(["Silent Swordsman Lv3", "Silent Swordsman Lv5"], player) +
+ state.has_all(["Ultimate Insect Lv3", "Ultimate Insect Lv5"], player)) >= 3)
+
+
+def spell_counter(state, player):
+ return (state.has("Pitch-Black Power Stone", player) and
+ state.has_from_list_exclusive(["Blast Magician",
+ "Magical Marionette",
+ "Mythical Beast Cerberus",
+ "Royal Magical Library",
+ "Spell-Counter Cards"], player, 2))
+
+
+def take_control(state, player):
+ return state.has_from_list_exclusive(["Aussa the Earth Charmer",
+ "Jowls of Dark Demise",
+ "Brain Control",
+ "Creature Swap",
+ "Enemy Controller",
+ "Mind Control",
+ "Magician of Faith"], player, 5)
+
+
+def only_toons(state, player):
+ return state.has_all(["Toon Gemini Elf",
+ "Toon Goblin Attack Force",
+ "Toon Masked Sorcerer",
+ "Toon Mermaid",
+ "Toon Dark Magician Girl",
+ "Toon World"], player)
+
+
+def only_spirit(state, player):
+ return state.has_all(["Asura Priest",
+ "Fushi No Tori",
+ "Maharaghi",
+ "Susa Soldier"], player)
+
+
+def pacman_deck(state, player):
+ return state.has_from_list_exclusive(["Des Lacooda",
+ "Swarm of Locusts",
+ "Swarm of Scarabs",
+ "Wandering Mummy",
+ "Golem Sentry",
+ "Great Spirit",
+ "Royal Keeper",
+ "Stealth Bird"], player, 4)
+
+
+def quick_plays(state, player):
+ return state.has_from_list_exclusive(["Collapse",
+ "Emergency Provisions",
+ "Enemy Controller",
+ "Graceful Dice",
+ "Mystik Wok",
+ "Offerings to the Doomed",
+ "Poison of the Old Man",
+ "Reload",
+ "Rush Recklessly",
+ "The Reliable Guardian"], player, 4)
+
+
+def counter_traps(state, player):
+ return state.has_from_list_exclusive(["Cursed Seal of the Forbidden Spell",
+ "Divine Wrath",
+ "Horn of Heaven",
+ "Magic Drain",
+ "Magic Jammer",
+ "Negate Attack",
+ "Seven Tools of the Bandit",
+ "Solemn Judgment",
+ "Spell Shield Type-8"], player, 5)
+
+
+def back_row_removal(state, player):
+ return state.has_from_list_exclusive(["Anteatereatingant",
+ "B.E.S. Tetran",
+ "Breaker the Magical Warrior",
+ "Calamity of the Wicked",
+ "Chiron the Mage",
+ "Dust Tornado",
+ "Heavy Storm",
+ "Mystical Space Typhoon",
+ "Mobius the Frost Monarch",
+ "Raigeki Break",
+ "Stamping Destruction",
+ "Swarm of Locusts"], player, 2)
diff --git a/worlds/yugioh06/structure_deck.py b/worlds/yugioh06/structure_deck.py
new file mode 100644
index 00000000..8454c55e
--- /dev/null
+++ b/worlds/yugioh06/structure_deck.py
@@ -0,0 +1,81 @@
+structure_contents: dict[str, set] = {
+ "dragons_roar": {
+ "Luster Dragon",
+ "Armed Dragon LV3",
+ "Armed Dragon LV5",
+ "Masked Dragon",
+ "Twin-Headed Behemoth",
+ "Stamping Destruction",
+ "Nobleman of Crossout",
+ "Creature Swap",
+ "Reload",
+ "Stamping Destruction",
+ "Heavy Storm",
+ "Dust Tornado",
+ "Mystical Space Typhoon",
+ },
+ "zombie_madness": {
+ "Pyramid Turtle",
+ "Regenerating Mummy",
+ "Ryu Kokki",
+ "Book of Life",
+ "Call of the Mummy",
+ "Creature Swap",
+ "Reload",
+ "Heavy Storm",
+ "Dust Tornado",
+ "Mystical Space Typhoon",
+ },
+ "blazing_destruction": {
+ "Inferno",
+ "Solar Flare Dragon",
+ "UFO Turtle",
+ "Ultimate Baseball Kid",
+ "Fire Beaters",
+ "Tribute to The Doomed",
+ "Level Limit - Area B",
+ "Heavy Storm",
+ "Dust Tornado",
+ "Mystical Space Typhoon",
+ },
+ "fury_from_the_deep": {
+ "Mother Grizzly",
+ "Water Beaters",
+ "Gravity Bind",
+ "Reload",
+ "Mobius the Frost Monarch",
+ "Heavy Storm",
+ "Dust Tornado",
+ "Mystical Space Typhoon",
+ },
+ "warriors_triumph": {
+ "Gearfried the Iron Knight",
+ "D.D. Warrior Lady",
+ "Marauding Captain",
+ "Exiled Force",
+ "Reinforcement of the Army",
+ "Warrior Beaters",
+ "Reload",
+ "Heavy Storm",
+ "Dust Tornado",
+ "Mystical Space Typhoon",
+ },
+ "spellcasters_judgement": {
+ "Dark Magician",
+ "Apprentice Magician",
+ "Breaker the Magical Warrior",
+ "Magician of Faith",
+ "Skilled Dark Magician",
+ "Tsukuyomi",
+ "Magical Dimension",
+ "Mage PowerSpell-Counter Cards",
+ "Heavy Storm",
+ "Dust Tornado",
+ "Mystical Space Typhoon",
+ },
+ "none": {},
+}
+
+
+def get_deck_content_locations(deck: str) -> dict[str, str]:
+ return {f"{deck} {i}": content for i, content in enumerate(structure_contents[deck])}