* 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>
164 lines
6.9 KiB
Python
164 lines
6.9 KiB
Python
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
|