diff --git a/BaseClasses.py b/BaseClasses.py index b5322b5a..125bd09e 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -128,6 +128,7 @@ class World(object): set_player_attr('open_pyramid', False) set_player_attr('treasure_hunt_icon', 'Triforce Piece') set_player_attr('treasure_hunt_count', 0) + set_player_attr('treasure_hunt_total', 0) set_player_attr('potshuffle', False) set_player_attr('pot_contents', None) @@ -1976,6 +1977,8 @@ class Spoiler(object): 'experimental': self.world.experimental, 'keydropshuffle': self.world.keydropshuffle, 'shopsanity': self.world.shopsanity, + 'triforcegoal': self.world.treasure_hunt_count, + 'triforcepool': self.world.treasure_hunt_total, 'code': {p: Settings.make_code(self.world, p) for p in range(1, self.world.players + 1)} } @@ -2019,6 +2022,9 @@ class Spoiler(object): outfile.write('Retro: %s\n' % ('Yes' if self.metadata['retro'][player] else 'No')) outfile.write('Swords: %s\n' % self.metadata['weapons'][player]) outfile.write('Goal: %s\n' % self.metadata['goal'][player]) + if self.metadata['goal'][player] == 'triforcehunt': + outfile.write('Triforce Pieces Required: %s\n' % self.metadata['triforcegoal'][player]) + outfile.write('Triforce Pieces Total: %s\n' % self.metadata['triforcepool'][player]) outfile.write('Difficulty: %s\n' % self.metadata['item_pool'][player]) outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player]) outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player]) diff --git a/CLI.py b/CLI.py index a48031b5..2dcbd809 100644 --- a/CLI.py +++ b/CLI.py @@ -97,6 +97,8 @@ def parse_cli(argv, no_defaults=False): for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', + 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', + 'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', 'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots', 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep', @@ -162,6 +164,14 @@ def parse_settings(): "dungeon_counters": "default", "mixed_travel": "prevent", "standardize_palettes": "standardize", + + "triforce_pool": 30, + "triforce_goal": 20, + "triforce_pool_min": 0, + "triforce_pool_max": 0, + "triforce_goal_min": 0, + "triforce_goal_max": 0, + "triforce_min_difference": 10, "code": "", "multi": 1, diff --git a/ItemList.py b/ItemList.py index 5ab84300..5ce4ed64 100644 --- a/ItemList.py +++ b/ItemList.py @@ -37,7 +37,7 @@ Difficulty = namedtuple('Difficulty', ['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield', 'basicshield', 'progressivearmor', 'basicarmor', 'swordless', 'progressivesword', 'basicsword', 'basicbow', 'timedohko', 'timedother', - 'triforcehunt', 'triforce_pieces_required', 'retro', + 'retro', 'extras', 'progressive_sword_limit', 'progressive_shield_limit', 'progressive_armor_limit', 'progressive_bottle_limit', 'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit']) @@ -60,8 +60,6 @@ difficulties = { basicbow = ['Bow', 'Silver Arrows'], timedohko = ['Green Clock'] * 25, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, - triforcehunt = ['Triforce Piece'] * 30, - triforce_pieces_required = 20, retro = ['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 4, @@ -87,8 +85,6 @@ difficulties = { basicbow = ['Bow'] * 2, timedohko = ['Green Clock'] * 25, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, - triforcehunt = ['Triforce Piece'] * 30, - triforce_pieces_required = 20, retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 3, @@ -114,8 +110,6 @@ difficulties = { basicbow = ['Bow'] * 2, timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, - triforcehunt = ['Triforce Piece'] * 30, - triforce_pieces_required = 20, retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 2, @@ -196,7 +190,6 @@ def generate_itempool(world, player): if world.goal[player] in ['triforcehunt']: region = world.get_region('Light World',player) - loc = Location(player, "Murahdahla", parent=region) region.locations.append(loc) world.dynamic_locations.append(loc) @@ -261,7 +254,7 @@ def generate_itempool(world, player): (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.customitemarray) world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999) else: - (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.doorShuffle[player]) + (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.doorShuffle[player]) if player in world.pool_adjustment.keys(): amt = world.pool_adjustment[player] @@ -319,10 +312,14 @@ def generate_itempool(world, player): if clock_mode is not None: world.clock_mode = clock_mode - if treasure_hunt_count is not None: - world.treasure_hunt_count[player] = treasure_hunt_count - if treasure_hunt_icon is not None: - world.treasure_hunt_icon[player] = treasure_hunt_icon + if world.goal[player] == 'triforcehunt': + if world.treasure_hunt_count[player] == 0: + world.treasure_hunt_count[player] = 20 + if world.treasure_hunt_total[player] == 0: + world.treasure_hunt_total[player] = 30 + world.treasure_hunt_icon[player] = 'Triforce Piece' + if world.custom: + world.treasure_hunt_count[player] = treasure_hunt_count world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player and ((item.smallkey and world.keyshuffle[player]) @@ -639,13 +636,15 @@ shop_transfer = {'Red Potion': 'Rupees (100)', 'Bee': 'Rupees (5)', 'Blue Potion } -def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, door_shuffle): +def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, door_shuffle): pool = [] placed_items = {} precollected_items = [] clock_mode = None - treasure_hunt_count = None - treasure_hunt_icon = None + if goal == 'triforcehunt': + if treasure_hunt_total == 0: + treasure_hunt_total = 30 + triforcepool = ['Triforce Piece'] * int(treasure_hunt_total) pool.extend(alwaysitems) @@ -739,13 +738,13 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r extraitems -= len(diff.timedohko) clock_mode = 'countdown-ohko' if goal == 'triforcehunt': - pool.extend(diff.triforcehunt) - extraitems -= len(diff.triforcehunt) - treasure_hunt_count = diff.triforce_pieces_required - treasure_hunt_icon = 'Triforce Piece' + pool.extend(triforcepool) + extraitems -= len(triforcepool) for extra in diff.extras: if extraitems > 0: + if len(extra) > extraitems: + extra = random.choices(extra, k=extraitems) pool.extend(extra) extraitems -= len(extra) @@ -770,7 +769,7 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r pool.extend(['Small Key (Universal)']) else: pool.extend(['Small Key (Universal)']) - return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) + return (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, customitemarray): if isinstance(customitemarray,dict) and 1 in customitemarray: diff --git a/Main.py b/Main.py index 4fd1cef2..78f2b4c1 100644 --- a/Main.py +++ b/Main.py @@ -86,6 +86,8 @@ def main(args, seed=None, fish=None): world.keydropshuffle = args.keydropshuffle.copy() world.mixed_travel = args.mixed_travel.copy() world.standardize_palettes = args.standardize_palettes.copy() + world.treasure_hunt_count = args.triforce_goal.copy() + world.treasure_hunt_total = args.triforce_pool.copy() world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)} diff --git a/Mystery.py b/Mystery.py index 77ab630a..2d827747 100644 --- a/Mystery.py +++ b/Mystery.py @@ -126,6 +126,12 @@ def roll_settings(weights): return None return random.choices(list(root[option].keys()), weights=list(map(int,root[option].values())))[0] + def get_choice_default(option, root=weights, default=None): + choice = get_choice(option, root) + if choice is None and default is not None: + return default + return choice + ret = argparse.Namespace() glitches_required = get_choice('glitches_required') @@ -173,7 +179,15 @@ def roll_settings(weights): ret.crystals_gt = get_choice('tower_open') ret.crystals_ganon = get_choice('ganon_open') - + + if ret.goal == 'triforcehunt': + goal_min = get_choice_default('triforce_goal_min', default=20) + goal_max = get_choice_default('triforce_goal_max', default=20) + pool_min = get_choice_default('triforce_pool_min', default=30) + pool_max = get_choice_default('triforce_pool_max', default=30) + ret.triforce_goal = random.randint(int(goal_min), int(goal_max)) + min_diff = get_choice_default('triforce_min_difference', default=10) + ret.triforce_pool = random.randint(max(int(pool_min), ret.triforce_goal + int(min_diff)), int(pool_max)) ret.mode = get_choice('world_state') if ret.mode == 'retro': ret.mode = 'open' diff --git a/Rom.py b/Rom.py index b03cbcae..f38d3ae4 100644 --- a/Rom.py +++ b/Rom.py @@ -1117,7 +1117,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): # set up goals for treasure hunt rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon[player] == 'Triforce Piece' else [0x0D, 0x28]) - rom.write_byte(0x180167, world.treasure_hunt_count[player] % 256) + rom.write_byte(0x180167, int(world.treasure_hunt_count[player]) % 256) rom.write_byte(0x180194, 1) # Must turn in triforced pieces (instant win not enabled) rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed @@ -2064,7 +2064,7 @@ def write_strings(rom, world, player, team): tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Get the Triforce Pieces.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invincible!' - tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % world.treasure_hunt_count[player] + tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % int(world.treasure_hunt_count[player]) elif world.goal[player] in ['pedestal']: tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Your goal is at the pedestal.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' diff --git a/Rules.py b/Rules.py index ff1e7024..07a7d2f2 100644 --- a/Rules.py +++ b/Rules.py @@ -42,7 +42,7 @@ def set_rules(world, player): # require aga2 to beat ganon add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player)) elif world.goal[player] == 'triforcehunt': - add_rule(world.get_location('Murahdahla', player), lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) >= state.world.treasure_hunt_count[player]) + add_rule(world.get_location('Murahdahla', player), lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) >= int(state.world.treasure_hunt_count[player])) if world.mode[player] != 'inverted': set_big_bomb_rules(world, player) diff --git a/mystery_example.yml b/mystery_example.yml index 40416ff3..bd19e6f5 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -39,6 +39,11 @@ dungeons: 1 pedestal: 2 triforce-hunt: 0 + triforce_goal_min: 10 + triforce_goal_max: 30 + triforce_pool_min: 20 + triforce_pool_max: 40 + triforce_min_difference: 10 dungeon_items: standard: 10 mc: 3 diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index c112651c..937160ab 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -218,6 +218,13 @@ "usestartinventory": { "type": "bool" }, + "triforce_pool": {}, + "triforce_goal": {}, + "triforce_pool_min": {}, + "triforce_pool_max": {}, + "triforce_goal_min": {}, + "triforce_goal_max": {}, + "triforce_min_difference": {}, "custom": { "type": "bool", "help": "suppress"