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
+
+
+
+
+
+
+
+
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 @@
|
- {%- 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 = {