diff --git a/CHANGELOG.md b/CHANGELOG.md index 113ed8cf..2e06e72c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +# 0.6.1.11 +- Fixed bonk drops duplicate counting and potentially overwriting arbitrary values +- Fixed boss icons on dungeon map check +- Fixed dungeon counters to autotrack correctly +- Key and chest counts in menu now display consistently (must have dungeon item or have visited dungeon to see the HUD) +- Money balancing will fail in less scenarios +- Fixed issue with Sanc pots not collecting +- Enemizer now allows more enemies on water +- Fix infinite pit fall issue with Old Man follower location +- Fix bad overworld tilemap drawing on HC and Pyramid screens in OW Layout Shuffle + ## 0.6.1.10 - Emergency fix for bonk functionality diff --git a/Fill.py b/Fill.py index 43915406..52021036 100644 --- a/Fill.py +++ b/Fill.py @@ -1024,7 +1024,10 @@ def balance_money_progression(world): 'Rupees (100)': 100, 'Rupees (300)': 300} rupee_rooms = {'Eastern Rupees': 90, 'Mire Key Rupees': 45, 'Mire Shooter Rupees': 90, 'TR Rupees': 270, 'PoD Dark Basement': 270} - acceptable_balancers = ['Bombs (3)', 'Arrows (10)', 'Bombs (10)'] + acceptable_balancers = ['Single Bomb', 'Bombs (3)', 'Bombs (10)', + 'Single Arrow', 'Arrows (5)', 'Arrows (10)', + 'Small Magic', 'Big Magic', 'Small Heart', + 'Fairy', 'Chicken', 'Nothing'] base_value = sum(rupee_rooms.values()) available_money = {player: base_value for player in range(1, world.players+1)} @@ -1125,10 +1128,11 @@ def balance_money_progression(world): unchecked_locations.remove(location) if location.item: if location.item.name.startswith('Rupee'): - wallet[location.item.player] += rupee_chart[location.item.name] - if location.item.name != 'Rupees (300)': - balance_locations[location.item.player].add(location) - if interesting_item(location, location.item, world, location.item.player): + if not (location.item.name == 'Rupee (1)' and world.algorithm != 'district'): + wallet[location.item.player] += rupee_chart[location.item.name] + if location.item.name != 'Rupees (300)': + balance_locations[location.item.player].add(location) + elif interesting_item(location, location.item, world, location.item.player): checked_locations.append(location) elif location.item.name in acceptable_balancers: balance_locations[location.item.player].add(location) @@ -1172,7 +1176,11 @@ def balance_money_progression(world): if len(increase_targets) == 0: increase_targets = [x for x in balance_locations[target_player] if (rupee_chart[x.item.name] if x.item.name in rupee_chart else 0) < best_value] if len(increase_targets) == 0: - raise Exception('No early sphere swaps for rupees - money grind would be required - bailing for now') + if state.can_farm_rupees(target_player): + logger.warning(f'No more swap targets available. Short by {difference} rupees, but continuing (player can farm)') + break + else: + raise Exception(f'No early sphere swaps for rupees - money grind would be required - bailing for now') best_target = min(increase_targets, key=lambda t: rupee_chart[t.item.name] if t.item.name in rupee_chart else 0) make_item_free = wallet[target_player] < 20 old_value = 0 if make_item_free else (rupee_chart[best_target.item.name] if best_target.item.name in rupee_chart else 0) diff --git a/Main.py b/Main.py index 15b5099f..bf5fc65c 100644 --- a/Main.py +++ b/Main.py @@ -40,7 +40,7 @@ from source.enemizer.DamageTables import DamageTable from source.enemizer.Enemizer import randomize_enemies from source.rom.DataTables import init_data_tables -version_number = '1.5.0' +version_number = '1.5.2' version_branch = '-u' __version__ = f'{version_number}{version_branch}' diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 1f79cc48..0311a20e 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -8,7 +8,7 @@ from OWEdges import OWTileRegions, OWEdgeGroups, OWEdgeGroupsTerrain, OWExitType from OverworldGlitchRules import create_owg_connections from Utils import bidict -version_number = '0.6.1.10' +version_number = '0.6.1.11' # branch indicator is intentionally different across branches version_branch = '-u' diff --git a/Rom.py b/Rom.py index 374ea4a6..4e467661 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'f2eebfbec9c8ad638e922ed1047d1c10' +RANDOMIZERBASEHASH = '34c9d7b09fad982dea9e7c9e3ae885ee' class JsonRom(object): @@ -1411,9 +1411,15 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None): | (0x04 if world.mapshuffle[player] != 'none' else 0x00) | (0x08 if world.bigkeyshuffle[player] != 'none' else 0x00))) # free roaming item text boxes rom.write_byte(0x18003B, 0x01 if world.mapshuffle[player] not in ['none', 'nearby'] else 0x00) # maps showing crystals on overworld - if world.keyshuffle[player] != 'universal' and (world.mapshuffle[player] not in ['none', 'nearby'] or world.doorShuffle[player] != 'vanilla' - or world.dropshuffle[player] != 'none' or world.pottery[player] not in ['none', 'cave']): - rom.write_byte(0x18003A, 0x01) # show key counts on map pickup + map_hud_mode = 0x00 + if world.dungeon_counters[player] == 'on': + map_hud_mode = 0x02 # always on + elif world.dungeon_counters[player] == 'off': + pass + elif world.keyshuffle[player] != 'universal' and (world.mapshuffle[player] not in ['none', 'nearby'] or world.doorShuffle[player] != 'vanilla' + or world.dropshuffle[player] != 'none' or world.pottery[player] not in ['none', 'cave'] or world.dungeon_counters[player] == 'pickup'): + map_hud_mode = 0x01 # show on pickup + rom.write_byte(0x18003A, map_hud_mode) # compasses showing dungeon count compass_mode = 0x80 if world.compassshuffle[player] not in ['none', 'nearby'] else 0x00 diff --git a/data/base2current.bps b/data/base2current.bps index 06b22326..1d7cbcfa 100644 Binary files a/data/base2current.bps and b/data/base2current.bps differ diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py index f558eb7d..15b81f51 100644 --- a/source/enemizer/SpriteSheets.py +++ b/source/enemizer/SpriteSheets.py @@ -177,9 +177,9 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.Vulture).no_drop().sub_group(2, 0x12).exclude(NoFlyingRooms), SpriteRequirement(EnemySprite.CorrectPullSwitch).affix().sub_group(3, [0x52, 0x53]), SpriteRequirement(EnemySprite.WrongPullSwitch).affix().sub_group(3, [0x52, 0x53]), - SpriteRequirement(EnemySprite.Octorok).aquaphobia().sub_group(2, [0xc, 0x18]), + SpriteRequirement(EnemySprite.Octorok).sub_group(2, [0xc, 0x18]), SpriteRequirement(EnemySprite.Moldorm).exalt().sub_group(2, 0x30), - SpriteRequirement(EnemySprite.Octorok4Way).aquaphobia().sub_group(2, 0xc), + SpriteRequirement(EnemySprite.Octorok4Way).sub_group(2, 0xc), SpriteRequirement(EnemySprite.Cucco).immune().sub_group(3, [0x15, 0x50]).exclude(NoFlyingRooms), SpriteRequirement(EnemySprite.Buzzblob).sub_group(3, 0x11), SpriteRequirement(EnemySprite.Snapdragon).sub_group(0, 0x16).sub_group(2, 0x17), @@ -191,7 +191,7 @@ def init_sprite_requirements(): .exclude(NoFlyingRooms).exclude({0x40}), # no anti-fairies in aga tower bridge room SpriteRequirement(EnemySprite.Wiseman).affix().sub_group(2, 0x4c), SpriteRequirement(EnemySprite.Hoarder).sub_group(3, 0x11).exclude({0x10c}), - SpriteRequirement(EnemySprite.MiniMoldorm).aquaphobia().sub_group(1, 0x1e), + SpriteRequirement(EnemySprite.MiniMoldorm).sub_group(1, 0x1e), SpriteRequirement(EnemySprite.Poe).no_drop().sub_group(3, 0x15).exclude(NoFlyingRooms), SpriteRequirement(EnemySprite.Smithy).affix().sub_group(1, 0x1d).sub_group(3, 0x15), SpriteRequirement(EnemySprite.Statue).stasis().immune().sub_group(3, [0x52, 0x53]), @@ -225,12 +225,12 @@ def init_sprite_requirements(): SpriteRequirement(EnemySprite.Hoarder2).sub_group(3, 0x11).exclude({0x10c}), SpriteRequirement(EnemySprite.TutorialGuard).affix(), SpriteRequirement(EnemySprite.LightningGate).affix().sub_group(3, 0x3f), - SpriteRequirement(EnemySprite.BlueGuard).aquaphobia().sub_group(1, [0xd, 0x49]).exclude(PitRooms), - SpriteRequirement(EnemySprite.BlueGuard).aquaphobia().sub_group(1, [0xd, 0x49]).sub_group(2, [0x29, 0x13]), - SpriteRequirement(EnemySprite.GreenGuard).aquaphobia().sub_group(1, 0x49).exclude(PitRooms), - SpriteRequirement(EnemySprite.GreenGuard).aquaphobia().sub_group(1, 0x49).sub_group(2, 0x13), - SpriteRequirement(EnemySprite.RedSpearGuard).aquaphobia().sub_group(1, [0xd, 0x49]).exclude(PitRooms), - SpriteRequirement(EnemySprite.RedSpearGuard).aquaphobia().sub_group(1, [0xd, 0x49]).sub_group(2, [0x29, 0x13]), + SpriteRequirement(EnemySprite.BlueGuard).sub_group(1, [0xd, 0x49]).exclude(PitRooms), + SpriteRequirement(EnemySprite.BlueGuard).sub_group(1, [0xd, 0x49]).sub_group(2, [0x29, 0x13]), + SpriteRequirement(EnemySprite.GreenGuard).sub_group(1, 0x49).exclude(PitRooms), + SpriteRequirement(EnemySprite.GreenGuard).sub_group(1, 0x49).sub_group(2, 0x13), + SpriteRequirement(EnemySprite.RedSpearGuard).sub_group(1, [0xd, 0x49]).exclude(PitRooms), + SpriteRequirement(EnemySprite.RedSpearGuard).sub_group(1, [0xd, 0x49]).sub_group(2, [0x29, 0x13]), SpriteRequirement(EnemySprite.BluesainBolt).aquaphobia().sub_group(0, 0x46).sub_group(1, [0xd, 0x49]), SpriteRequirement(EnemySprite.UsainBolt).aquaphobia().sub_group(1, [0xd, 0x49]), SpriteRequirement(EnemySprite.BlueArcher).sub_group(0, 0x48).sub_group(1, 0x49), diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py index 2ecfc518..2139066a 100644 --- a/source/rom/DataTables.py +++ b/source/rom/DataTables.py @@ -141,10 +141,11 @@ class DataTables: bytes = sum(1+len(x)*3 for x in self.ow_enemy_table.values() if len(x) > 0)+1 self.pointer_addresses['ow_sprites'][1] = bytes # ending_byte = 0x09CB3B + bytes - max_per_state = {0: 0x40, 1: 0x90, 2: 0x90} + max_per_state = {0: 0x40, 1: 0x90, 2: 0x81} # dropped max on state 2 to steal space for extra sprites (Murahdahla, extra tutorial guard) pointer_address = snes_to_pc(self.pointer_addresses['ow_sprites'][2][0]) - data_pointer = snes_to_pc(self.pointer_addresses['ow_sprites'][0]) + self.pointer_addresses['ow_sprites'][0] = pointer_address + ((max_per_state[0] + max_per_state[1] + max_per_state[2]) * 2) + data_pointer = self.pointer_addresses['ow_sprites'][0] empty_pointer = pc_to_snes(data_pointer) & 0xFFFF rom.write_byte(data_pointer, 0xff) cached_dark_world = {} @@ -177,6 +178,10 @@ class DataTables: data_pointer += len(data) rom.write_byte(data_pointer, 0xff) data_pointer += 1 + # Check if OW sprite data has overwritten the UW sprite pointer table + max_allowed_address = snes_to_pc(0x09D62E) + if data_pointer > max_allowed_address: + raise Exception(f'OW sprite data will cause the UW sprite pointer table to overwrite the pots pointer table. Data end: {hex(pc_to_snes(data_pointer))}, Max allowed: $09D62E') special_health_table = { diff --git a/source/tools/MysteryUtils.py b/source/tools/MysteryUtils.py index d73f9d67..4d0b72a8 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -156,11 +156,7 @@ def roll_settings(weights): ret.door_self_loops = get_choice_bool('door_self_loops') ret.experimental = get_choice_bool('experimental') ret.collection_rate = get_choice_bool('collection_rate') - ret.dungeon_counters = get_choice_non_bool('dungeon_counters') if 'dungeon_counters' in weights else 'default' - if ret.dungeon_counters == 'default': - ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle != 'none' else 'off' - ret.pseudoboots = get_choice_bool('pseudoboots') ret.mirrorscroll = get_choice_bool('mirrorscroll') ret.shopsanity = get_choice_bool('shopsanity')