diff --git a/BaseClasses.py b/BaseClasses.py index 101e06cd..4e4b8827 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -73,6 +73,7 @@ class World(object): self.shuffle_ganon = shuffle_ganon self.dark_rooms = {} self.damage_challenge = {} + self.shuffle_damage_table = {} self.custom = custom self.customitemarray = customitemarray self.can_take_damage = {} @@ -174,6 +175,7 @@ class World(object): set_player_attr('can_take_damage', True) set_player_attr('dark_rooms', 'require_lamp') set_player_attr('damage_challenge', 'normal') + set_player_attr('shuffle_damage_table', 'vanilla') set_player_attr('crystal_book', False) set_player_attr('collection_rate', False) set_player_attr('colorizepots', True) @@ -3075,6 +3077,7 @@ class Spoiler(object): 'can_take_damage': self.world.can_take_damage, 'dark_rooms': self.world.dark_rooms, 'damage_challenge': self.world.damage_challenge, + 'shuffle_damage_table': self.world.shuffle_damage_table, 'crystal_book': self.world.crystal_book, 'triforcegoal': self.world.treasure_hunt_count, 'triforcepool': self.world.treasure_hunt_total, @@ -3328,6 +3331,7 @@ class Spoiler(object): outfile.write('Mirror Scroll:'.ljust(line_width) + '%s\n' % yn(self.metadata['mirrorscroll'][player])) outfile.write('Dark Rooms:'.ljust(line_width) + '%s\n' % self.metadata['dark_rooms'][player]) outfile.write('Damage Challenge:'.ljust(line_width) + '%s\n' % self.metadata['damage_challenge'][player]) + outfile.write('Damage Table Randomization:'.ljust(line_width) + '%s\n' % self.metadata['shuffle_damage_table'][player]) outfile.write('Crystal Book:'.ljust(line_width) + '%s\n' % yn(self.metadata['crystal_book'][player])) outfile.write('Hints:'.ljust(line_width) + '%s\n' % yn(self.metadata['hints'][player])) outfile.write('Race:'.ljust(line_width) + '%s\n' % yn(self.world.settings.world_rep['meta']['race'])) diff --git a/CLI.py b/CLI.py index abf5245d..8f51760c 100644 --- a/CLI.py +++ b/CLI.py @@ -139,7 +139,7 @@ def parse_cli(argv, no_defaults=False): 'triforce_max_difference', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern', 'skullwoods', 'linked_drops', - 'pseudoboots', 'mirrorscroll', 'dark_rooms', 'damage_challenge', 'crystal_book', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', + 'pseudoboots', 'mirrorscroll', 'dark_rooms', 'damage_challenge', 'shuffle_damage_table', 'crystal_book', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', 'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots', 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle', @@ -213,6 +213,7 @@ def parse_settings(): "mirrorscroll": False, "dark_rooms": "require_lamp", "damage_challenge": "normal", + "shuffle_damage_table": "vanilla", "crystal_book": False, "shuffleenemies": "none", diff --git a/DamageTable.py b/DamageTable.py new file mode 100644 index 00000000..b3fb8d0a --- /dev/null +++ b/DamageTable.py @@ -0,0 +1,49 @@ +from dataclasses import dataclass, field +from typing import List + +import RaceRandom as random + +def _load_entries(): + entries = [] + with open("data/damage_table.bin", 'rb') as stream: + for sprite in range(0x100): + entries.append([]) + for pair in range(8): + byte = stream.read(1)[0] + entries[sprite].append(byte >> 4) + entries[sprite].append(byte & 0x0F) + return entries + +@dataclass +class DamageTable: + _entries: List[int] = field(default_factory=_load_entries) + + def randomize_sword_subclasses(self): + for sprite in range(0xD8): + for dclass in range(1, 6): + if self._entries[sprite][dclass] > 0: + self._entries[sprite][dclass] = random.randint(1, 7) + + def get_bytes(self): + values = [] + for sprite in range(0x100): + for dclass in range(0, 16, 2): + cur = self._entries[sprite][dclass] & 0xF + nex = self._entries[sprite][dclass + 1] & 0xF + values.append((cur << 4) | nex) + return values + + +if __name__ == '__main__': + def print_table(table): + bytelist = table.get_bytes() + for sprite in range(0x100): + for byte in range(8): + print(format(bytelist.pop(0), "02X"), end=" ") + print() + print() + + dmg = DamageTable() + print_table(dmg) + dmg.randomize_sword_subclasses() + print_table(dmg) diff --git a/Main.py b/Main.py index 127146d2..67c4c01e 100644 --- a/Main.py +++ b/Main.py @@ -515,6 +515,7 @@ def init_world(args, fish): world.mirrorscroll = args.mirrorscroll.copy() world.dark_rooms = args.dark_rooms.copy() world.damage_challenge = args.damage_challenge.copy() + world.shuffle_damage_table = args.shuffle_damage_table.copy() world.crystal_book = args.crystal_book.copy() world.overworld_map = args.overworld_map.copy() world.take_any = args.take_any.copy() @@ -621,6 +622,7 @@ def copy_world(world): ret.mirrorscroll = world.mirrorscroll.copy() ret.dark_rooms = world.dark_rooms.copy() ret.damage_challenge = world.damage_challenge.copy() + ret.shuffle_damage_table = world.shuffle_damage_table.copy() ret.crystal_book = world.crystal_book.copy() ret.overworld_map = world.overworld_map.copy() ret.take_any = world.take_any.copy() @@ -845,6 +847,7 @@ def copy_world_premature(world, player): ret.mirrorscroll = world.mirrorscroll.copy() ret.dark_rooms = world.dark_rooms.copy() ret.damage_challenge = world.damage_challenge.copy() + ret.shuffle_damage_table = world.shuffle_damage_table.copy() ret.crystal_book = world.crystal_book.copy() ret.overworld_map = world.overworld_map.copy() ret.take_any = world.take_any.copy() diff --git a/Rom.py b/Rom.py index 714ce580..1198be21 100644 --- a/Rom.py +++ b/Rom.py @@ -33,6 +33,7 @@ from source.overworld.EntranceData import door_addresses, ow_prize_table from source.overworld.EntranceShuffle2 import exit_ids from OverworldShuffle import default_flute_connections, flute_data from InitialSram import InitialSram +from DamageTable import DamageTable from source.classes.SFX import randomize_sfx, randomize_sfxinstruments, randomize_songinstruments from source.item.FillUtil import valid_pot_items @@ -1667,6 +1668,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): world.data_tables[player].write_to_rom(rom, colorize_pots, world.enemy_shuffle[player] == 'random') write_enemizer_tweaks(rom, world, player) + randomize_damage_table(rom, world, player) write_strings(rom, world, player, team) # write initial sram @@ -1766,6 +1768,19 @@ def write_enemizer_tweaks(rom, world, player): rom.write_byte(snes_to_pc(0x0DB6B3), 0x82) # hovers don't need water necessarily? +def randomize_damage_table(rom, world, player): + if world.shuffle_damage_table[player] == 'randomized': + rom.write_bytes(snes_to_pc(0x0DB8F9), + [0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x10, 0x40, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x10, 0x40, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x10, 0x40, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x10, 0x40, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x10, 0x40]) + dmg_table = DamageTable() + dmg_table.randomize_sword_subclasses() + rom.write_bytes(snes_to_pc(0x31C800), dmg_table.get_bytes()) + + def hud_format_text(text): output = bytes() for char in text.lower(): diff --git a/data/damage_table.bin b/data/damage_table.bin new file mode 100644 index 00000000..fe0d0465 Binary files /dev/null and b/data/damage_table.bin differ diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index bc5a989b..94308c69 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -616,6 +616,12 @@ "mimics" ] }, + "shuffle_damage_table": { + "choices": [ + "vanilla", + "randomized" + ] + }, "enemy_health": { "choices": [ "default",