Yu-Gi-oh! 2006: implement new game (#2795)

* Initial implementation of Yu-Gi-Oh! WC 2006

* Added Opponents and banlists

* Initial implementation of Yu-Gi-Oh! WC 2006

* Added Opponents and banlists

* Added Campaign Logic

* Added Bonuses Logic

* Added challenge logic

* fixed yugioh client

* ygo06 rom cleanup and include lua

* ygo06 patch cleanup

* ygo06 move client to world folder

* lots of small changes

* bug fixes

* implemented filler item for yugioh06

* BizHawkClient: Add client and connector

* BizHawkClient: Add launcher component and inno_setup lines

* BizHawkClient: Misc stability updates and small improvements

Bad commit organization a consequence of working with two different branches and not keeping the commits separated

* BizHawkClient: Add docstrings

* BizHawkClient: Pull in changes from other branch

* BizHawkClient: Fix no handler message not displaying after changed ROMs

* BizHawkClient: Remove extra print statement from lua

* BizHawkClient: Change version command to use raw strings

* BizHawkClient: Change script version to single integer

* YGO06: added logic for "all expect type forbidden" limited duels

* YGO06: Structure Deck choice now affects logic. Fixed a bug with tier 5 campaign opponents. Added logic for TD16 Union.

* BizHawkClient: Add newline to version for lua script

* BizHawkClient: Call send_connect from BizHawkClient's watcher loop

* BizHawkClient: Add handling for failed request getting script version

* BizHawkClient: Have base64.lua check lua version explicitly for bit operations

On 2.9, it would detect LuaJIT and flood the console with deprecation warnings

* BizHawkClient: Update connector script for slightly better errors and address Gambatte frame sync issue

* BizHawkClient: Remove accidentally added print statements

* BizHawkClient: Fix connector server not closing correctly

* BizHawkClient: Move some connector code around, some linting

* BizHawkClient: Small cleanup in lua

* BizHawkClient: Lua linting

* BizHawkClient: Remove outdated sentences in docstrings

* YGO06: Logic additions and bug fixes

* BizHawkClient: Correctly null check patch file arg

* BizHawkClient: Initialize logging

* BizHawkClient: Move code to worlds/_bizhawk

Also splits out BizHawk communication functions to their own file for use outside this client

* BizHawkClient: Add license to connector lua, add types to docs

* BizHawkClient: Add module docstrings

* YGO06: Logic additions

* BizHawkClient: Allow clients to define multiple systems

* BizHawkClient: Better logging and handling of interruptions to connection to script

* YGO06: Logic additions

* YGO06: Added text to options

* YGO06: Ported to bizhawk client

* YGO06: fix goal not being detected

* YGO06: fix access item rule for tier 5 column 1 and 2

* YGO06: docu and bug fixes

* YGO06: change name

* YGO06: some fixes

* YGO06: fix starting opponent and booster not applying

* YGO06: added option to reduce the amount of challenges and remove the no ban list from pool.

* YGO06: added rom being asked for on first use

* YGO06: fix rules for challenges

* YGO06: create proper rules for TD04 Ritual Summon

* YGO06: mark most banlists as usefull instead of progression

* YGO06: reduce the required core boosters across the board

* YGO06: fix client not loading if another game already loaded the bizhawk client

* YGO06: fix client not finding the bizhawk client.

* YGO06: fix TD08 Draw not giving out an item

* YGO06: small text changes

* YGO06: update to version 0.4.4

* YGO06: logic mixin clean-up

* YGO06: added option for campaign opponents as goal

* Pokemon Emerald add encounter table randomization

* Pokemon Emerald: Item ball randomization working

* Pokemon Emerald: Clean up code a little

* Pokemon Emerald: Partial rework of region/location creation

* Pokemon Emerald: Dedupe items and add more readable names

* Refactor region creation to manually defined regions

* Split region json

* Use new data.json with flattened constants and add HM locations

* YGO06: bug fixes

* YGO06: bug fix

* YGO06: changes default options to be more beginner friendly

* YGO06: attempt at universal tracker support. Settings are stored in slot data now.

* YGO06: fix for older python versions

* YGO06: fix slot data

* YGO06: added diiferent opponents to the campaign

* YGO06: fix small bug with opponent icons

* YGO06: fix unwanted changes

* YGO06: repair merge with main

* YGO06: map out all of the opponents

* YGO06: added opponent shuffle

* YGO06: added logic to opponent shuffle

* YGO06: added option to use ocg art

* YGO06: bug_fixes

* YGO06: removed todos, since they are not needed anymore

* YGO06: added draft mode

* YGO06: added logic to draft mode

* YGO06: Added Money multiplier when you lose

* YGO06: Fixed Unit Test errors

* YGO06: Added Random deck option

* YGO06: Bug fix with registering client

* YGO06: client clean-up

* YGO06: fixed card misspellings

* YGO06: removed unused imports and other small changes

* YGO06: small changes

* YGO06: fix generation error when the combination of starting with "No Banlist" and not adding "No Banlist" to the pool is selected

* YGO06: fix ocg art path overwriting Huge Revolution bugfix

* YGO06: added comments and other minor changes

* YGO06: fixed byte length in client for money

* YGO06: fixes for webhost and options

* YGO06: use the proper random function

* YGO06: change settings to options

* YGO06: move to procedure patch

* YGO06: fix imports

* YGO06: fix download link for patch not showing

* YGO06: remove unnecessary Optional

* YGO06: fix universal tracker stuff

* YGO06: add typings

* YGO06: small cleanup

* yugioh06:  small change to setup

Co-authored-by: Scipio Wright <scipiowright@gmail.com>

* YGO06: remove logic mixin

* YGO06: fix create item and implement create filler and get filler item name

* YGO06: remove double lambdas

* YGO06: use pkgutil.get_data instaed pf zipFile

* YGO06: fix starting items being duplicated

* YGO06: lots of small changes

* YGO06: moved functions to match execution order

* YGO06: run ruff

* YGO06: run ruff format

* YGO06: fix ruff errors

* YGO06: undo ruff format for rules

* YGO06: move import to prevent circular dependency

* YGO06: remove unused class

* YGO06: optimizing rules

* YGO06: some optimization and small bug fix

---------

Co-authored-by: Zunawe <gyroscope15@gmail.com>
Co-authored-by: Scipio Wright <scipiowright@gmail.com>
This commit is contained in:
Rensen3 2024-05-17 19:23:05 +02:00 committed by GitHub
parent 5fb1d0f98a
commit 539ee1c5da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 3946 additions and 0 deletions

View File

@ -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):

454
worlds/yugioh06/__init__.py Normal file
View File

@ -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"

View File

@ -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])
}

View File

@ -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

View File

@ -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.

View File

@ -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 `<address>:<port>` on the textfield on top and press enter (if the
server uses password, type in the bottom textfield `/connect <address>:<port> [password]`)
Don't forget to start manipulating RNG early by shouting "Heart of the Cards!" during generation.

View File

@ -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

369
worlds/yugioh06/items.py Normal file
View File

@ -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",
]

View File

@ -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

28
worlds/yugioh06/logic.py Normal file
View File

@ -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)

View File

@ -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)
)

195
worlds/yugioh06/options.py Normal file
View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

163
worlds/yugioh06/rom.py Normal file
View File

@ -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("<B", structure_deck))
# set banlist
banlist = world.options.banlist
patch.write_token(APTokenTypes.WRITE, 0xF4496, struct.pack("<B", banlist_ids.get(banlist.value)))
# set items to locations map
randomizer_data_start = 0x0000F310
for location in world.multiworld.get_locations(world.player):
item = location.item.name
if location.item.player != world.player:
item = "Remote"
item_id = item_to_index.get(item)
if item_id is None:
continue
location_id = world.location_name_to_id[location.name] - 5730000
patch.write_token(APTokenTypes.WRITE, randomizer_data_start + location_id, struct.pack("<B", item_id))
# set starting inventory
inventory_map = [0 for i in range(32)]
starting_inventory = list(map(lambda i: i.name, world.multiworld.precollected_items[world.player]))
starting_inventory += world.options.start_inventory.value
for start_inventory in starting_inventory:
item_id = world.item_name_to_id[start_inventory] - 5730001
index = math.floor(item_id / 8)
bit = item_id % 8
inventory_map[index] = inventory_map[index] | (1 << bit)
for i in range(32):
patch.write_token(APTokenTypes.WRITE, 0xE9DC + i, struct.pack("<B", inventory_map[i]))
# set unlock conditions for the last 3 campaign opponents
third_tier_5 = (
world.options.third_tier_5_campaign_boss_challenges.value
if world.options.third_tier_5_campaign_boss_unlock_condition.value == 1
else world.options.third_tier_5_campaign_boss_campaign_opponents.value
)
patch.write_token(APTokenTypes.WRITE, 0xEEFA, struct.pack("<B", third_tier_5))
fourth_tier_5 = (
world.options.fourth_tier_5_campaign_boss_challenges.value
if world.options.fourth_tier_5_campaign_boss_unlock_condition.value == 1
else world.options.fourth_tier_5_campaign_boss_campaign_opponents.value
)
patch.write_token(APTokenTypes.WRITE, 0xEF10, struct.pack("<B", fourth_tier_5))
final = (
world.options.final_campaign_boss_challenges.value
if world.options.final_campaign_boss_unlock_condition.value == 1
else world.options.final_campaign_boss_campaign_opponents.value
)
patch.write_token(APTokenTypes.WRITE, 0xEF22, struct.pack("<B", final))
patch.write_token(
APTokenTypes.WRITE,
0xEEF8,
struct.pack(
"<B",
int((function_addresses.get(world.options.third_tier_5_campaign_boss_unlock_condition.value) - 0xEEFA) / 2),
),
)
patch.write_token(
APTokenTypes.WRITE,
0xEF0E,
struct.pack(
"<B",
int(
(function_addresses.get(world.options.fourth_tier_5_campaign_boss_unlock_condition.value) - 0xEF10) / 2
),
),
)
patch.write_token(
APTokenTypes.WRITE,
0xEF20,
struct.pack(
"<B", int((function_addresses.get(world.options.final_campaign_boss_unlock_condition.value) - 0xEF22) / 2)
),
)
# set starting money
patch.write_token(APTokenTypes.WRITE, 0xF4734, struct.pack("<I", world.options.starting_money))
patch.write_token(APTokenTypes.WRITE, 0xE70C, struct.pack("<B", world.options.money_reward_multiplier.value))
patch.write_token(APTokenTypes.WRITE, 0xE6E4, struct.pack("<B", world.options.money_reward_multiplier.value))
# normalize booster packs if option is set
if world.options.normalize_boosters_packs.value:
booster_pack_price = world.options.booster_pack_prices.value.to_bytes(2, "little")
for booster in range(51):
space = booster * 16
patch.write_token(APTokenTypes.WRITE, 0x1E5E2E8 + space, struct.pack("<B", booster_pack_price[0]))
patch.write_token(APTokenTypes.WRITE, 0x1E5E2E9 + space, struct.pack("<B", booster_pack_price[1]))
patch.write_token(APTokenTypes.WRITE, 0x1E5E2EA + space, struct.pack("<B", 5))
# set shuffled campaign opponents if option is set
if world.options.campaign_opponents_shuffle.value:
i = 0
for opp in world.campaign_opponents:
space = i * 32
patch.write_token(APTokenTypes.WRITE, 0x000F3BA + i, struct.pack("<B", opp.id))
patch.write_token(APTokenTypes.WRITE, 0x1E58D0E + space, struct.pack("<H", opp.card_id))
patch.write_token(APTokenTypes.WRITE, 0x1E58D10 + space, struct.pack("<H", opp.deck_name_id))
for j, b in enumerate(opp.deck_file.encode("ascii")):
patch.write_token(APTokenTypes.WRITE, 0x1E58D12 + space + j, struct.pack("<B", b))
i = i + 1
for j, b in enumerate(world.romName):
patch.write_token(APTokenTypes.WRITE, 0x10 + j, struct.pack("<B", b))
for j, b in enumerate(world.playerName):
patch.write_token(APTokenTypes.WRITE, 0x30 + j, struct.pack("<B", b))
patch.write_file("token_data.bin", patch.get_token_binary())
def get_base_rom_bytes(file_name: str = "") -> 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

View File

@ -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,
}

12
worlds/yugioh06/ruff.toml Normal file
View File

@ -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"]

868
worlds/yugioh06/rules.py Normal file
View File

@ -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)

View File

@ -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])}