diff --git a/Adjuster.py b/Adjuster.py index 01bfdcf8..f269073c 100755 --- a/Adjuster.py +++ b/Adjuster.py @@ -28,6 +28,13 @@ def main(): ''') parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true') parser.add_argument('--disablemusic', help='Disables game music.', action='store_true') + parser.add_argument('--triforcehud', default='hide_goal', const='hide_goal', nargs='?', choices=['normal', 'hide_goal', 'hide_required', 'hide_both'], + help='''\ + Hide the triforce hud in certain circumstances. + hide_goal will hide the hud until finding a triforce piece, hide_required will hide the total amount needed to win + (Both can be revealed when speaking to Murahalda) + (default: %(default)s) + ''') parser.add_argument('--enableflashing', help='Reenable flashing animations (unfriendly to epilepsy, always disabled in race roms)', action='store_false', dest="reduceflashing") parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['double', 'normal', 'half', 'quarter', 'off'], help='''\ diff --git a/Main.py b/Main.py index 6b82a4a0..9d2fc658 100644 --- a/Main.py +++ b/Main.py @@ -7,6 +7,7 @@ import time import zlib import concurrent.futures import pickle +from typing import Dict from BaseClasses import MultiWorld, CollectionState, Region, Item from worlds.alttp import ALttPLocation @@ -38,6 +39,14 @@ def get_seed(seed=None): return seed +seeds: Dict[tuple, str] = dict() +def get_same_seed(world: MultiWorld, seed_def: tuple) -> str: + if seed_def in seeds: + return seeds[seed_def] + seeds[seed_def] = str(world.random.randint(0, 2 ** 64)) + return seeds[seed_def] + + def main(args, seed=None): if args.outputpath: os.makedirs(args.outputpath, exist_ok=True) @@ -108,9 +117,16 @@ def main(args, seed=None): world.er_seeds[player] = str(world.random.randint(0, 2 ** 64)) if "-" in world.shuffle[player]: - shuffle, seed = world.shuffle[player].split("-") + shuffle, seed = world.shuffle[player].split("-", 1) world.shuffle[player] = shuffle - world.er_seeds[player] = seed + if shuffle == "vanilla": + world.er_seeds[player] = "vanilla" + elif seed.startswith("team-"): + world.er_seeds[player] = get_same_seed(world, (shuffle, seed, world.retro[player], world.mode[player], world.logic[player])) + else: + world.er_seeds[player] = seed + elif world.shuffle[player] == "vanilla": + world.er_seeds[player] = "vanilla" logger.info('Archipelago Version %s - Seed: %s\n', __version__, world.seed) @@ -289,7 +305,7 @@ def main(args, seed=None): palettes_options['link']=args.link_palettes[player] apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player], - args.fastmenu[player], args.disablemusic[player], args.sprite[player], + args.fastmenu[player], args.disablemusic[player], args.triforcehud[player], args.sprite[player], palettes_options, world, player, True, reduceflashing=args.reduceflashing[player] if not args.race else True) mcsb_name = '' diff --git a/Mystery.py b/Mystery.py index 373f58f6..880a33a1 100644 --- a/Mystery.py +++ b/Mystery.py @@ -402,8 +402,11 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b ret.accessibility = get_choice('accessibility', weights) - entrance_shuffle = get_choice('entrance_shuffle', weights) - ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla' + entrance_shuffle = get_choice('entrance_shuffle', weights, 'vanilla') + if entrance_shuffle.startswith('none-'): + ret.shuffle = 'vanilla' + else: + ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla' goal = get_choice('goals', weights, 'ganon') ret.goal = {'ganon': 'ganon', @@ -692,6 +695,7 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b ret.sprite_pool += [key] * int(value) ret.disablemusic = get_choice('disablemusic', romweights, False) + ret.triforcehud = get_choice('triforcehud', romweights, 'hide_goal') ret.quickswap = get_choice('quickswap', romweights, True) ret.fastmenu = get_choice('menuspeed', romweights, "normal") ret.reduceflashing = get_choice('reduceflashing', romweights, False) diff --git a/WebHostLib/static/assets/playerTracker.js b/WebHostLib/static/assets/playerTracker.js new file mode 100644 index 00000000..3f01f93c --- /dev/null +++ b/WebHostLib/static/assets/playerTracker.js @@ -0,0 +1,20 @@ +window.addEventListener('load', () => { + const url = window.location; + setInterval(() => { + const ajax = new XMLHttpRequest(); + ajax.onreadystatechange = () => { + if (ajax.readyState !== 4) { return; } + + // Create a fake DOM using the returned HTML + const domParser = new DOMParser(); + const fakeDOM = domParser.parseFromString(ajax.responseText, 'text/html'); + + // Update item and location trackers + document.getElementById('inventory-table').innerHTML = fakeDOM.getElementById('inventory-table').innerHTML; + document.getElementById('location-table').innerHTML = fakeDOM.getElementById('location-table').innerHTML; + + }; + ajax.open('GET', url); + ajax.send(); + }, 15000) +}); diff --git a/WebHostLib/static/static/weightedSettings.json b/WebHostLib/static/static/weightedSettings.json index cd0bbfbf..f1f7706c 100644 --- a/WebHostLib/static/static/weightedSettings.json +++ b/WebHostLib/static/static/weightedSettings.json @@ -75,19 +75,19 @@ "restrict_dungeon_item_on_boss": { "keyString": "restrict_dungeon_item_on_boss", "friendlyName": "Dungeon Item on Boss", - "description": "Defeating a dungeon bosses always awards a dungeon item.", + "description": "Prevent dungeon bosses from dropping maps, compasses, and keys.", "inputType": "range", "subOptions": { "on": { "keyString": "restrict_dungeon_item_on_boss.on", "friendlyName": "On", - "description": "Dungeon bosses will always drop a dungeon item.", + "description": "Dungeon bosses will never drop maps, compasses, or keys.", "defaultValue": 0 }, "off": { "keyString": "restrict_dungeon_item_on_boss.off", "friendlyName": "Off", - "description": "Dungeon bosses may not drop a dungeon item.", + "description": "Dungeon bosses may drop any item.", "defaultValue": 0 } } @@ -1602,6 +1602,37 @@ } } }, + "triforcehud": { + "keyString": "rom.triforcehud", + "friendlyName": "Triforce Hud Options", + "description": "Hide the triforce hud in certain circumstances.", + "inputType": "range", + "subOptions": { + "normal": { + "keyString": "rom.triforcehud.normal", + "friendlyName": "Normal", + "description": "Always displays HUD as usual.", + "defaultValue": 0 + }, + "hide_goal": { + "keyString": "rom.triforcehud.hide_goal", + "friendlyName": "Hide Goal", + "description": "Hide Triforce Hud elements until a single triforce piece is acquired or spoken to Murahadala", + "defaultValue": 50 + }, + "hide_total": { + "keyString": "rom.triforcehud.hide_required", + "friendlyName": "Hide Required Total", + "description": "Hide total amount needed to win the game (unless spoken to Murahadala)", + "defaultValue": 0 + }, + "hide_both": { + "keyString": "rom.triforcehud.hide_both", + "friendlyName": "Hide Both", + "description": "Hide both of the above options", + "defaultValue": 0 + } + }, "reduceflashing": { "keyString": "rom.reduceflashing", "friendlyName": "Full-Screen Flashing Effects", diff --git a/WebHostLib/static/styles/playerTracker.css b/WebHostLib/static/styles/playerTracker.css new file mode 100644 index 00000000..8c3c97b9 --- /dev/null +++ b/WebHostLib/static/styles/playerTracker.css @@ -0,0 +1,72 @@ +#player-tracker-wrapper{ + margin: 0; +} + +#inventory-table{ + border-top: 2px solid #000000; + border-left: 2px solid #000000; + border-right: 2px solid #000000; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + padding: 3px 3px 10px; + width: 260px; + background-color: #42b149; +} + +#inventory-table td{ + width: 40px; + height: 40px; + text-align: center; + vertical-align: middle; +} + +#inventory-table img{ + height: 100%; + max-width: 40px; + max-height: 40px; + filter: grayscale(100%); +} + +#inventory-table img.acquired{ + filter: none; +} + +#inventory-table img.powder-fix{ + width: 35px; + height: 35px; +} + +#location-table{ + width: 260px; + border-left: 2px solid #000000; + border-right: 2px solid #000000; + border-bottom: 2px solid #000000; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + background-color: #42b149; + padding: 0 3px 3px; +} + +#location-table th{ + vertical-align: middle; + text-align: center; + padding-right: 10px; +} + +#location-table td{ + padding-top: 2px; + padding-bottom: 2px; + padding-right: 5px; + line-height: 20px; +} + +#location-table td.counter{ + padding-right: 10px; + text-align: right; +} + +#location-table img{ + height: 100%; + max-width: 30px; + max-height: 30px; +} diff --git a/WebHostLib/templates/playerTracker.html b/WebHostLib/templates/playerTracker.html new file mode 100644 index 00000000..b297f7cd --- /dev/null +++ b/WebHostLib/templates/playerTracker.html @@ -0,0 +1,77 @@ + + + + {{ player_name }}'s Tracker + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + {% for area in sp_areas %} + + + + + + + {% endfor %} +
{{ area }}{{ checks_done[area] }} / {{ checks_in_area[area] }} + {{ inventory[small_key_ids[area]] if area in key_locations else 'N/A' }} + + {{ '✔' if area in big_key_locations and inventory[big_key_ids[area]] else ('N/A' if area not in big_key_locations else '') }} +
+
+ + diff --git a/WebHostLib/templates/tracker.html b/WebHostLib/templates/tracker.html index 1ba56337..626510fc 100644 --- a/WebHostLib/templates/tracker.html +++ b/WebHostLib/templates/tracker.html @@ -35,16 +35,16 @@ {{ name|e }} - {%- else -%} - {{ name|e }} - {%- endif -%} - {%- endfor -%} + {%- else -%} + {{ name|e }} + {%- endif -%} + {%- endfor -%} {%- for player, items in players.items() -%} - {{ loop.index }} + {{ loop.index }} {%- if (team, loop.index) in video -%} {%- if video[(team, loop.index)][0] == "Twitch" -%} @@ -120,7 +120,7 @@ {%- for player, checks in players.items() -%} - {{ loop.index }} + {{ loop.index }} {{ player_names[(team, loop.index)]|e }} {%- for area in ordered_areas -%} {%- set checks_done = checks[area] -%} diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py index 144c3085..1fd297ba 100644 --- a/WebHostLib/tracker.py +++ b/WebHostLib/tracker.py @@ -18,14 +18,28 @@ app.jinja_env.filters["location_name"] = lambda location: Regions.lookup_id_to_n app.jinja_env.filters['item_name'] = lambda id: Items.lookup_id_to_name.get(id, id) icons = { + "Blue Shield": r"https://www.zeldadungeon.net/wiki/images/8/85/Fighters-Shield.png", + "Red Shield": r"https://www.zeldadungeon.net/wiki/images/5/55/Fire-Shield.png", + "Mirror Shield": r"https://www.zeldadungeon.net/wiki/images/8/84/Mirror-Shield.png", + "Fighter Sword": r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/4/40/SFighterSword.png?width=1920", + "Master Sword": r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/6/65/SMasterSword.png?width=1920", + "Tempered Sword": r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/9/92/STemperedSword.png?width=1920", + "Golden Sword": r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/2/28/SGoldenSword.png?width=1920", + "Bow": r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/b/bc/ALttP_Bow_%26_Arrows_Sprite.png?version=5f85a70e6366bf473544ef93b274f74c", + "Silver Bow": r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/6/65/Bow.png?width=1920", + "Green Mail": r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/c/c9/SGreenTunic.png?width=1920", + "Blue Mail": r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/9/98/SBlueTunic.png?width=1920", + "Red Mail": r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/7/74/SRedTunic.png?width=1920", + "Power Glove": r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/f/f5/SPowerGlove.png?width=1920", + "Titan Mitts": r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/c/c1/STitanMitt.png?width=1920", "Progressive Sword": r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/c/cc/ALttP_Master_Sword_Sprite.png?version=55869db2a20e157cd3b5c8f556097725", "Pegasus Boots": r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/ed/ALttP_Pegasus_Shoes_Sprite.png?version=405f42f97240c9dcd2b71ffc4bebc7f9", "Progressive Glove": - r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/5/53/ALttP_Titan's_Mitt_Sprite.png?version=6ac54c3016a23b94413784881fcd3c75", + r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/c/c1/STitanMitt.png?width=1920", "Flippers": - r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/8/88/ALttP_Zora's_Flippers_Sprite.png?version=b9d7521bb3a5a4d986879f70a70bc3da", + r"https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/4/4c/ZoraFlippers.png?width=1920", "Moon Pearl": r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/6/63/ALttP_Moon_Pearl_Sprite.png?version=d601542d5abcc3e006ee163254bea77e", "Progressive Bow": @@ -246,6 +260,14 @@ def attribute_item(inventory, team, recipient, item): inventory[team][recipient][target_item] += 1 +def attribute_item_solo(inventory, item): + target_item = links.get(item, item) + if item in levels: # non-progressive + inventory[target_item] = max(inventory[target_item], levels[item]) + else: + inventory[target_item] += 1 + + @app.template_filter() def render_timedelta(delta: datetime.timedelta): hours, minutes = divmod(delta.total_seconds() / 60, 60) @@ -301,6 +323,145 @@ def get_static_room_data(room: Room): return result +@app.route('/tracker///') +@cache.memoize(timeout=15) +def getPlayerTracker(tracker: UUID, team: int, player: int): + # Team and player must be positive and greater than zero + if team < 1 or player < 1: + abort(404) + + room = Room.get(tracker=tracker) + if not room: + abort(404) + + # Collect seed information and pare it down to a single player + locations, names, use_door_tracker, seed_checks_in_area, player_location_to_area = get_static_room_data(room) + player_name = names[team - 1][player - 1] + seed_checks_in_area = seed_checks_in_area[player] + player_location_to_area = player_location_to_area[player] + inventory = collections.Counter() + checks_done = {loc_name: 0 for loc_name in default_locations} + + # Add starting items to inventory + starting_items = room.seed.multidata.get("precollected_items", None)[player - 1] + if starting_items: + for item_id in starting_items: + attribute_item_solo(inventory, item_id) + + # Add items to player inventory + for (ms_team, ms_player), locations_checked in room.multisave.get("location_checks", {}): + # Skip teams and players not matching the request + if ms_team != (team - 1) or ms_player != player: + continue + + # If the player does not have the item, do nothing + for location in locations_checked: + if (location, ms_player) not in locations or location not in player_location_to_area: + continue + + item, recipient = locations[location, ms_player] + attribute_item_solo(inventory, item) + checks_done[player_location_to_area[location]] += 1 + checks_done["Total"] += 1 + + # Note the presence of the triforce item + for (ms_team, ms_player), game_state in room.multisave.get("client_game_state", []): + # Skip teams and players not matching the request + if ms_team != (team - 1) or ms_player != player: + continue + + if game_state: + inventory[106] = 1 # Triforce + + acquired_items = [] + for itm in inventory: + acquired_items.append(get_item_name_from_id(itm)) + + # Progressive items need special handling for icons and class + progressive_items = { + "Progressive Sword": 94, + "Progressive Glove": 97, + "Progressive Bow": 100, + "Progressive Mail": 96, + "Progressive Shield": 95, + } + + # Determine which icon to use for the sword + sword_url = icons["Fighter Sword"] + sword_acquired = False + sword_names = ['Fighter Sword', 'Master Sword', 'Tempered Sword', 'Golden Sword'] + if "Progressive Sword" in acquired_items: + sword_url = icons[sword_names[inventory[progressive_items["Progressive Sword"]] - 1]] + sword_acquired = True + else: + for sword in reversed(sword_names): + if sword in acquired_items: + sword_url = icons[sword] + sword_acquired = True + break + + gloves_url = icons["Power Glove"] + gloves_acquired = False + glove_names = ["Power Glove", "Titan Mitts"] + if "Progressive Glove" in acquired_items: + gloves_url = icons[glove_names[inventory[progressive_items["Progressive Glove"]] - 1]] + gloves_acquired = True + else: + for glove in reversed(glove_names): + if glove in acquired_items: + gloves_url = icons[glove] + gloves_acquired = True + break + + bow_url = icons["Bow"] + bow_acquired = False + bow_names = ["Bow", "Silver Bow"] + if "Progressive Bow" in acquired_items: + bow_url = icons[bow_names[inventory[progressive_items["Progressive Bow"]] - 1]] + bow_acquired = True + else: + for bow in reversed(bow_names): + if bow in acquired_items: + bow_url = icons[bow] + bow_acquired = True + break + + mail_url = icons["Green Mail"] + mail_names = ["Blue Mail", "Red Mail"] + if "Progressive Mail" in acquired_items: + mail_url = icons[mail_names[inventory[progressive_items["Progressive Mail"]] - 1]] + else: + for mail in reversed(mail_names): + if mail in acquired_items: + mail_url = icons[mail] + break + + shield_url = icons["Blue Shield"] + shield_acquired = False + shield_names = ["Blue Shield", "Red Shield", "Mirror Shield"] + if "Progressive Shield" in acquired_items: + shield_url = icons[shield_names[inventory[progressive_items["Progressive Shield"]] - 1]] + shield_acquired = True + else: + for shield in reversed(shield_names): + if shield in acquired_items: + shield_url = icons[shield] + shield_acquired = True + break + + # The single player tracker doesn't care about overworld, underworld, and total checks. Maybe it should? + sp_areas = ordered_areas[2:15] + + return render_template("playerTracker.html", inventory=inventory, get_item_name_from_id=get_item_name_from_id, + player_name=player_name, room=room, icons=icons, checks_done=checks_done, + checks_in_area=seed_checks_in_area, acquired_items=acquired_items, + sword_url=sword_url, sword_acquired=sword_acquired, gloves_url=gloves_url, + gloves_acquired=gloves_acquired, bow_url=bow_url, bow_acquired=bow_acquired, + small_key_ids=small_key_ids, big_key_ids=big_key_ids, sp_areas=sp_areas, + key_locations=key_locations, big_key_locations=big_key_locations, mail_url=mail_url, + shield_url=shield_url, shield_acquired=shield_acquired) + + @app.route('/tracker/') @cache.memoize(timeout=30) # update every 30 seconds def getTracker(tracker: UUID): diff --git a/data/basepatch.apbp b/data/basepatch.apbp index caeb24ee..08776a33 100644 Binary files a/data/basepatch.apbp and b/data/basepatch.apbp differ diff --git a/playerSettings.yaml b/playerSettings.yaml index d9186d11..bc667b85 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -404,6 +404,11 @@ rom: quickswap: # Enable switching items by pressing the L+R shoulder buttons on: 50 off: 0 + triforcehud: # Disable visibility of the triforce hud unless collecting a piece or speaking to Murahalda + normal: 0 # original behavior (always visible) + hide_goal: 50 # hide counter until a piece is collected or speaking to Murahalda + hide_required: 0 # Always visible, but required amount is invisible until determined by Murahalda + hide_both: 0 # Hide both under above circumstances reduceflashing: # Reduces instances of flashing such as lightning attacks, weather, ether and more. on: 50 off: 0 diff --git a/worlds/alttp/EntranceRandomizer.py b/worlds/alttp/EntranceRandomizer.py index 4699668a..57f1e772 100644 --- a/worlds/alttp/EntranceRandomizer.py +++ b/worlds/alttp/EntranceRandomizer.py @@ -244,6 +244,13 @@ def parse_arguments(argv, no_defaults=False): ''') parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true') parser.add_argument('--disablemusic', help='Disables game music.', action='store_true') + parser.add_argument('--triforcehud', default='hide_goal', const='hide_goal', nargs='?', choices=['normal', 'hide_goal', 'hide_required', 'hide_both'], + help='''\ + Hide the triforce hud in certain circumstances. + hide_goal will hide the hud until finding a triforce piece, hide_required will hide the total amount needed to win + (Both can be revealed when speaking to Murahalda) + (default: %(default)s) + ''') parser.add_argument('--enableflashing', help='Reenable flashing animations (unfriendly to epilepsy, always disabled in race roms)', action='store_false', dest="reduceflashing") parser.add_argument('--mapshuffle', default=defval(False), help='Maps are no longer restricted to their dungeons, but can be anywhere', @@ -407,7 +414,7 @@ def parse_arguments(argv, no_defaults=False): 'remote_items', 'progressive', 'dungeon_counters', 'glitch_boots', 'killable_thieves', 'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool', 'dark_room_logic', 'restrict_dungeon_item_on_boss', 'reduceflashing', 'game', - 'hud_palettes', 'sword_palettes', 'shield_palettes', 'link_palettes']: + 'hud_palettes', 'sword_palettes', 'shield_palettes', 'link_palettes', 'triforcehud']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) if player == 1: setattr(ret, name, {1: value}) diff --git a/worlds/alttp/ItemPool.py b/worlds/alttp/ItemPool.py index 85ef3d30..cc730c4e 100644 --- a/worlds/alttp/ItemPool.py +++ b/worlds/alttp/ItemPool.py @@ -361,7 +361,7 @@ def generate_itempool(world, player: int): world.clock_mode[player] = clock_mode if treasure_hunt_count is not None: - world.treasure_hunt_count[player] = treasure_hunt_count + world.treasure_hunt_count[player] = treasure_hunt_count % 999 if treasure_hunt_icon is not None: world.treasure_hunt_icon[player] = treasure_hunt_icon diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py index c3dff2c4..be1a1c50 100644 --- a/worlds/alttp/Rom.py +++ b/worlds/alttp/Rom.py @@ -1,7 +1,7 @@ from __future__ import annotations JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'a0a9511a2a59e5e8009b38718f8da1bf' +RANDOMIZERBASEHASH = 'b2201c6fa50ba7a6ac42d73f37d75493' import io import json @@ -1153,8 +1153,8 @@ def patch_rom(world, rom, player, team, enemized): rom.write_int32(0x18020C, 0) # starting time (in frames, sint32) # set up goals for treasure hunt + rom.write_int16(0x180163, world.treasure_hunt_count[player]) 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(0x180194, 1) # Must turn in triforced pieces (instant win not enabled) rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed @@ -1681,7 +1681,7 @@ def hud_format_text(text): return output[:32] -def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite: str, palettes_options, +def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, triforcehud, sprite: str, palettes_options, world=None, player=1, allow_random_on_event=False, reduceflashing=False): local_random = random if not world else world.rom_seeds[player] @@ -1755,6 +1755,10 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr rom.write_byte(0x6FA30, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) rom.write_byte(0x65561, {'red': 0x05, 'blue': 0x0D, 'green': 0x19, 'yellow': 0x09}[color]) + # set triforcehud + triforce_flag = (rom.read_byte(0x180167) & 0x80) | {'normal': 0x00, 'hide_goal': 0x01, 'hide_required': 0x02, 'hide_both': 0x03}[triforcehud] + rom.write_byte(0x180167, triforce_flag) + if z3pr: def buildAndRandomize(option_name, mode): options = {