diff --git a/.gitignore b/.gitignore index bc4845cf..a332f0ff 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ weights/ /Players/ /QUsb2Snes/ /output/ +/enemizer/ base2current.json diff --git a/BaseClasses.py b/BaseClasses.py index 9ac4a9cb..50a48039 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -143,6 +143,7 @@ class World(object): set_player_attr('potshuffle', False) set_player_attr('pot_contents', None) set_player_attr('pseudoboots', False) + set_player_attr('mirrorscroll', False) set_player_attr('collection_rate', False) set_player_attr('colorizepots', True) set_player_attr('pot_pool', {}) @@ -2588,6 +2589,7 @@ class Spoiler(object): 'potshuffle': self.world.potshuffle, 'shopsanity': self.world.shopsanity, 'pseudoboots': self.world.pseudoboots, + 'mirrorscroll': self.world.mirrorscroll, 'triforcegoal': self.world.treasure_hunt_count, 'triforcepool': self.world.treasure_hunt_total, 'race': self.world.settings.world_rep['meta']['race'], @@ -2766,6 +2768,7 @@ class Spoiler(object): outfile.write(f"Bow Mode: {self.metadata['bow_mode'][player]}\n") outfile.write(f"Bombbag: {yn(self.metadata['bombbag'][player])}\n") outfile.write(f"Pseudoboots: {yn(self.metadata['pseudoboots'][player])}\n") + outfile.write(f"Mirror Scroll: {yn(self.metadata['mirrorscroll'][player])}\n") outfile.write('\n') # Item Pool Settings @@ -3120,7 +3123,7 @@ take_any_mode = {'none': 0, 'random': 1, 'fixed': 2} bow_mode = {'progressive': 0, 'silvers': 1, 'retro': 2, 'retro_silvers': 3} # additions -# byte 12: POOT TKKK (pseudoboots, overworld_map, trap_door_mode, key_logic_algo) +# byte 12: POOT TKKK (mirrorscroll, pseudoboots, overworld_map, trap_door_mode, key_logic_algo) overworld_map_mode = {'default': 0, 'compass': 1, 'map': 2} trap_door_mode = {'vanilla': 0, 'optional': 1, 'boss': 2, 'oneway': 3} key_logic_algo = {'dangerous': 0, 'partial': 1, 'strict': 2} @@ -3176,7 +3179,7 @@ class Settings(object): (flute_mode[w.flute_mode[p]] << 7 | bow_mode[w.bow_mode[p]] << 4 | take_any_mode[w.take_any[p]] << 2 | keyshuffle_mode[w.keyshuffle[p]]), - ((0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 5 + ((0xF0 if w.mirrorscroll[p] else 0) | (0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 5 | trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]), (skullwoods_mode[w.skullwoods[p]] << 6 | linked_drops_mode[w.linked_drops[p]] << 4), @@ -3249,6 +3252,7 @@ class Settings(object): args.take_any[p] = r(take_any_mode)[(settings[11] & 0xC) >> 2] args.keyshuffle[p] = r(keyshuffle_mode)[settings[11] & 0x3] if len(settings) > 12: + args.mirrorscroll[p] = True if settings[12] & 0xF0 else False args.pseudoboots[p] = True if settings[12] & 0x80 else False args.overworld_map[p] = r(overworld_map_mode)[(settings[12] & 0x60) >> 5] args.trap_door_mode[p] = r(trap_door_mode)[(settings[12] & 0x18) >> 3] diff --git a/CLI.py b/CLI.py index 4efc85b6..b2cc20e3 100644 --- a/CLI.py +++ b/CLI.py @@ -136,7 +136,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', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', + 'pseudoboots', 'mirrorscroll', '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', @@ -197,6 +197,7 @@ def parse_settings(): "overworld_map": "default", 'take_any': 'none', "pseudoboots": False, + "mirrorscroll": False, "shuffleenemies": "none", "shufflebosses": "none", diff --git a/Main.py b/Main.py index 3614c00b..ea6820ec 100644 --- a/Main.py +++ b/Main.py @@ -138,6 +138,7 @@ def main(args, seed=None, fish=None): world.skullwoods = args.skullwoods.copy() world.linked_drops = args.linked_drops.copy() world.pseudoboots = args.pseudoboots.copy() + world.mirrorscroll = args.mirrorscroll.copy() world.overworld_map = args.overworld_map.copy() world.take_any = args.take_any.copy() world.restrict_boss_items = args.restrict_boss_items.copy() diff --git a/README.md b/README.md index 20d75b00..4e4f98af 100644 --- a/README.md +++ b/README.md @@ -300,6 +300,12 @@ Dashing is allowed without the boots item however doors and certain rocks remain CLI `--pseudoboots` +#### Mirror Scroll + +Mirror is usable inside dungeons. Locations that require the mirror are still unattainable. + +CLI `--mirrorscroll` + #### Flute Mode Normal mode for flute means you need to activate it at the village statue after finding it like usual. Activated flute mode mean you can use it immediately upon finding it. The flute SFX plays to let you know this is the case. diff --git a/Rom.py b/Rom.py index a3654c8c..72403c5e 100644 --- a/Rom.py +++ b/Rom.py @@ -545,7 +545,11 @@ def patch_rom(world, rom, player, team, is_mystery=False): patch_shuffled_dark_sanc(world, rom, player) # setup dr option flags based on experimental, etc. - dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' else DROptions.Town_Portal + dr_flags = DROptions.NoOptions + if world.mirrorscroll[player]: + dr_flags = DROptions.Town_Portal + if world.doorShuffle[player] == 'vanilla': + dr_flags |= DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] not in ['vanilla', 'basic']: dr_flags |= DROptions.Map_Info if ((world.collection_rate[player] or world.goal[player] == 'completionist') diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index d855857c..53c04ae4 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -465,6 +465,10 @@ "action": "store_true", "type": "bool" }, + "mirrorscroll": { + "action": "store_true", + "type": "bool" + }, "calc_playthrough": { "action": "store_false", "type": "bool" diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 113b5efe..7c70461d 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -348,6 +348,7 @@ "Fixed: Take any caves will replace certain location. See documentation for full list" ], "pseudoboots": [ " Players starts with pseudo boots that allow dashing but no item checks (default: %(default)s"], + "mirrorscroll": [ " Players starts with mirror scroll that allows mirror in dungeons but not overworld (default: %(default)s"], "bombbag": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ], "any_enemy_logic": [ "How to handle potential traversal between dungeon in Crossed door shuffle", diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index e841d2fa..92318d72 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -241,6 +241,7 @@ "randomizer.item.race": "Generate \"Race\" ROM", "randomizer.item.retro": "Retro mode", "randomizer.item.pseudoboots": "Pseudoboots", + "randomizer.item.mirrorscroll": "Mirror Scroll", "randomizer.item.worldstate": "World State", "randomizer.item.worldstate.standard": "Standard", diff --git a/resources/app/gui/randomize/item/widgets.json b/resources/app/gui/randomize/item/widgets.json index 0a895d3c..b0b5551f 100644 --- a/resources/app/gui/randomize/item/widgets.json +++ b/resources/app/gui/randomize/item/widgets.json @@ -2,6 +2,7 @@ "checkboxes": { "hints": { "type": "checkbox" }, "pseudoboots": { "type": "checkbox" }, + "mirrorscroll": { "type": "checkbox" }, "race": { "type": "checkbox" } }, "leftItemFrame": { diff --git a/source/classes/CustomSettings.py b/source/classes/CustomSettings.py index 37bb0206..26771c60 100644 --- a/source/classes/CustomSettings.py +++ b/source/classes/CustomSettings.py @@ -150,6 +150,7 @@ class CustomSettings(object): args.restrict_boss_items[p] = get_setting(settings['restrict_boss_items'], args.restrict_boss_items[p]) args.overworld_map[p] = get_setting(settings['overworld_map'], args.overworld_map[p]) args.pseudoboots[p] = get_setting(settings['pseudoboots'], args.pseudoboots[p]) + args.mirrorscroll[p] = get_setting(settings['mirrorscroll'], args.mirrorscroll[p]) args.triforce_goal[p] = get_setting(settings['triforce_goal'], args.triforce_goal[p]) args.triforce_pool[p] = get_setting(settings['triforce_pool'], args.triforce_pool[p]) args.triforce_goal_min[p] = get_setting(settings['triforce_goal_min'], args.triforce_goal_min[p]) @@ -287,6 +288,7 @@ class CustomSettings(object): settings_dict[p]['linked_drops'] = world.linked_drops[p] settings_dict[p]['overworld_map'] = world.overworld_map[p] settings_dict[p]['pseudoboots'] = world.pseudoboots[p] + settings_dict[p]['mirrorscroll'] = world.mirrorscroll[p] settings_dict[p]['triforce_goal'] = world.treasure_hunt_count[p] settings_dict[p]['triforce_pool'] = world.treasure_hunt_total[p] settings_dict[p]['beemizer'] = world.beemizer[p] diff --git a/source/classes/constants.py b/source/classes/constants.py index 7c213ff9..54f4f2fb 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -57,6 +57,7 @@ SETTINGSTOPROCESS = { "item": { "hints": "hints", "pseudoboots": "pseudoboots", + "mirrorscroll": "mirrorscroll", "race": "race", "worldstate": "mode", diff --git a/source/tools/MysteryUtils.py b/source/tools/MysteryUtils.py index aa0d9127..0d9bd8b7 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -134,6 +134,7 @@ def roll_settings(weights): ret.skullwoods = get_choice('skullwoods') ret.linked_drops = get_choice('linked_drops') ret.pseudoboots = get_choice_bool('pseudoboots') + ret.mirrorscroll = get_choice_bool('mirrorscroll') ret.shopsanity = get_choice_bool('shopsanity') keydropshuffle = get_choice_bool('keydropshuffle') ret.dropshuffle = get_choice('dropshuffle') if 'dropshuffle' in weights else 'none'