Merge branch 'main' into breaking_changes

# Conflicts:
#	Main.py
#	worlds/alttp/EntranceRandomizer.py
This commit is contained in:
Fabian Dill 2021-03-03 01:54:52 +01:00
commit 0d576eefbb
14 changed files with 425 additions and 21 deletions

View File

@ -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('--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('--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('--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'], parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['double', 'normal', 'half', 'quarter', 'off'],
help='''\ help='''\

22
Main.py
View File

@ -7,6 +7,7 @@ import time
import zlib import zlib
import concurrent.futures import concurrent.futures
import pickle import pickle
from typing import Dict
from BaseClasses import MultiWorld, CollectionState, Region, Item from BaseClasses import MultiWorld, CollectionState, Region, Item
from worlds.alttp import ALttPLocation from worlds.alttp import ALttPLocation
@ -38,6 +39,14 @@ def get_seed(seed=None):
return seed 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): def main(args, seed=None):
if args.outputpath: if args.outputpath:
os.makedirs(args.outputpath, exist_ok=True) 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)) world.er_seeds[player] = str(world.random.randint(0, 2 ** 64))
if "-" in world.shuffle[player]: if "-" in world.shuffle[player]:
shuffle, seed = world.shuffle[player].split("-") shuffle, seed = world.shuffle[player].split("-", 1)
world.shuffle[player] = shuffle 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) 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] palettes_options['link']=args.link_palettes[player]
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[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) palettes_options, world, player, True, reduceflashing=args.reduceflashing[player] if not args.race else True)
mcsb_name = '' mcsb_name = ''

View File

@ -402,8 +402,11 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
ret.accessibility = get_choice('accessibility', weights) ret.accessibility = get_choice('accessibility', weights)
entrance_shuffle = get_choice('entrance_shuffle', weights) entrance_shuffle = get_choice('entrance_shuffle', weights, 'vanilla')
ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else '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') goal = get_choice('goals', weights, 'ganon')
ret.goal = {'ganon': '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.sprite_pool += [key] * int(value)
ret.disablemusic = get_choice('disablemusic', romweights, False) ret.disablemusic = get_choice('disablemusic', romweights, False)
ret.triforcehud = get_choice('triforcehud', romweights, 'hide_goal')
ret.quickswap = get_choice('quickswap', romweights, True) ret.quickswap = get_choice('quickswap', romweights, True)
ret.fastmenu = get_choice('menuspeed', romweights, "normal") ret.fastmenu = get_choice('menuspeed', romweights, "normal")
ret.reduceflashing = get_choice('reduceflashing', romweights, False) ret.reduceflashing = get_choice('reduceflashing', romweights, False)

View File

@ -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)
});

View File

@ -75,19 +75,19 @@
"restrict_dungeon_item_on_boss": { "restrict_dungeon_item_on_boss": {
"keyString": "restrict_dungeon_item_on_boss", "keyString": "restrict_dungeon_item_on_boss",
"friendlyName": "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", "inputType": "range",
"subOptions": { "subOptions": {
"on": { "on": {
"keyString": "restrict_dungeon_item_on_boss.on", "keyString": "restrict_dungeon_item_on_boss.on",
"friendlyName": "On", "friendlyName": "On",
"description": "Dungeon bosses will always drop a dungeon item.", "description": "Dungeon bosses will never drop maps, compasses, or keys.",
"defaultValue": 0 "defaultValue": 0
}, },
"off": { "off": {
"keyString": "restrict_dungeon_item_on_boss.off", "keyString": "restrict_dungeon_item_on_boss.off",
"friendlyName": "Off", "friendlyName": "Off",
"description": "Dungeon bosses may not drop a dungeon item.", "description": "Dungeon bosses may drop any item.",
"defaultValue": 0 "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": { "reduceflashing": {
"keyString": "rom.reduceflashing", "keyString": "rom.reduceflashing",
"friendlyName": "Full-Screen Flashing Effects", "friendlyName": "Full-Screen Flashing Effects",

View File

@ -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;
}

View File

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{ player_name }}&apos;s Tracker</title>
<link rel="stylesheet" type="text/css" href="{{ static_autoversion("styles/playerTracker.css") }}"/>
<script type="application/ecmascript" src="{{ static_autoversion("assets/playerTracker.js") }}"></script>
</head>
<body>
<div id="player-tracker-wrapper" data-tracker="{{ room.tracker|suuid }}">
<table id="inventory-table">
<tr>
<td><img src="{{ bow_url }}" class="{{ 'acquired' if bow_acquired }}" /></td>
<td><img src="{{ icons["Blue Boomerang"] }}" class="{{ 'acquired' if 'Blue Boomerang' in acquired_items }}" /></td>
<td><img src="{{ icons["Red Boomerang"] }}" class="{{ 'acquired' if 'Red Boomerang' in acquired_items }}" /></td>
<td><img src="{{ icons["Hookshot"] }}" class="{{ 'acquired' if 'Hookshot' in acquired_items }}" /></td>
<td><img src="{{ icons["Magic Powder"] }}" class="powder-fix {{ 'acquired' if 'Magic Powder' in acquired_items }}" /></td>
</tr>
<tr>
<td><img src="{{ icons["Fire Rod"] }}" class="{{ 'acquired' if "Fire Rod" in acquired_items }}" /></td>
<td><img src="{{ icons["Ice Rod"] }}" class="{{ 'acquired' if "Ice Rod" in acquired_items }}" /></td>
<td><img src="{{ icons["Bombos"] }}" class="{{ 'acquired' if "Bombos" in acquired_items }}" /></td>
<td><img src="{{ icons["Ether"] }}" class="{{ 'acquired' if "Ether" in acquired_items }}" /></td>
<td><img src="{{ icons["Quake"] }}" class="{{ 'acquired' if "Quake" in acquired_items }}" /></td>
</tr>
<tr>
<td><img src="{{ icons["Lamp"] }}" class="{{ 'acquired' if "Lamp" in acquired_items }}" /></td>
<td><img src="{{ icons["Hammer"] }}" class="{{ 'acquired' if "Hammer" in acquired_items }}" /></td>
<td><img src="{{ icons["Flute"] }}" class="{{ 'acquired' if "Flute" in acquired_items }}" /></td>
<td><img src="{{ icons["Bug Catching Net"] }}" class="{{ 'acquired' if "Bug Catching Net" in acquired_items }}" /></td>
<td><img src="{{ icons["Book of Mudora"] }}" class="{{ 'acquired' if "Book of Mudora" in acquired_items }}" /></td>
</tr>
<tr>
<td><img src="{{ icons["Bottle"] }}" class="{{ 'acquired' if "Bottle" in acquired_items }}" /></td>
<td><img src="{{ icons["Cane of Somaria"] }}" class="{{ 'acquired' if "Cane of Somaria" in acquired_items }}" /></td>
<td><img src="{{ icons["Cane of Byrna"] }}" class="{{ 'acquired' if "Cane of Byrna" in acquired_items }}" /></td>
<td><img src="{{ icons["Cape"] }}" class="{{ 'acquired' if "Cape" in acquired_items }}" /></td>
<td><img src="{{ icons["Magic Mirror"] }}" class="{{ 'acquired' if "Magic Mirror" in acquired_items }}" /></td>
</tr>
<tr>
<td><img src="{{ icons["Pegasus Boots"] }}" class="{{ 'acquired' if "Pegasus Boots" in acquired_items }}" /></td>
<td><img src="{{ gloves_url }}" class="{{ 'acquired' if gloves_acquired }}" /></td>
<td><img src="{{ icons["Flippers"] }}" class="{{ 'acquired' if "Flippers" in acquired_items }}" /></td>
<td><img src="{{ icons["Moon Pearl"] }}" class="{{ 'acquired' if "Moon Pearl" in acquired_items }}" /></td>
<td><img src="{{ icons["Mushroom"] }}" class="{{ 'acquired' if "Mushroom" in acquired_items }}" /></td>
</tr>
<tr>
<td><img src="{{ sword_url }}" class="{{ 'acquired' if sword_acquired }}" /></td>
<td><img src="{{ shield_url }}" class="{{ 'acquired' if shield_acquired }}" /></td>
<td><img src="{{ mail_url }}" class="acquired" /></td>
<td><img src="{{ icons["Shovel"] }}" class="{{ 'acquired' if "Shovel" in acquired_items }}" /></td>
<td><img src="{{ icons["Triforce"] }}" class="{{ 'acquired' if "Triforce" in acquired_items }}" /></td>
</tr>
</table>
<table id="location-table">
<tr>
<th></th>
<th class="counter"><img src="{{ icons["Chest"] }}" /></th>
<th class="counter"><img src="{{ icons["Small Key"] }}" /></th>
<th><img src="{{ icons["Big Key"] }}" /></th>
</tr>
{% for area in sp_areas %}
<tr>
<td>{{ area }}</td>
<td class="counter">{{ checks_done[area] }} / {{ checks_in_area[area] }}</td>
<td class="counter">
{{ inventory[small_key_ids[area]] if area in key_locations else 'N/A' }}
</td>
<td>
{{ '✔' if area in big_key_locations and inventory[big_key_ids[area]] else ('N/A' if area not in big_key_locations else '') }}
</td>
</tr>
{% endfor %}
</table>
</div>
</body>
</html>

View File

@ -35,16 +35,16 @@
<th class="center-column"> <th class="center-column">
<img class="alttp-sprite" src="{{ icons[name] }}" alt="{{ name|e }}"> <img class="alttp-sprite" src="{{ icons[name] }}" alt="{{ name|e }}">
</th> </th>
{%- else -%} {%- else -%}
<th class="center-column">{{ name|e }}</th> <th class="center-column">{{ name|e }}</th>
{%- endif -%} {%- endif -%}
{%- endfor -%} {%- endfor -%}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{%- for player, items in players.items() -%} {%- for player, items in players.items() -%}
<tr> <tr>
<td>{{ loop.index }}</td> <td><a href="{{ room.tracker|suuid }}/{{ team + 1 }}/{{ player }}">{{ loop.index }}</a></td>
{%- if (team, loop.index) in video -%} {%- if (team, loop.index) in video -%}
{%- if video[(team, loop.index)][0] == "Twitch" -%} {%- if video[(team, loop.index)][0] == "Twitch" -%}
<td> <td>
@ -120,7 +120,7 @@
<tbody> <tbody>
{%- for player, checks in players.items() -%} {%- for player, checks in players.items() -%}
<tr> <tr>
<td>{{ loop.index }}</td> <td><a href="{{ room.tracker|suuid }}/{{ team + 1 }}/{{ player }}">{{ loop.index }}</a></td>
<td>{{ player_names[(team, loop.index)]|e }}</td> <td>{{ player_names[(team, loop.index)]|e }}</td>
{%- for area in ordered_areas -%} {%- for area in ordered_areas -%}
{%- set checks_done = checks[area] -%} {%- set checks_done = checks[area] -%}

View File

@ -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) app.jinja_env.filters['item_name'] = lambda id: Items.lookup_id_to_name.get(id, id)
icons = { 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": "Progressive Sword":
r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/c/cc/ALttP_Master_Sword_Sprite.png?version=55869db2a20e157cd3b5c8f556097725", r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/c/cc/ALttP_Master_Sword_Sprite.png?version=55869db2a20e157cd3b5c8f556097725",
"Pegasus Boots": "Pegasus Boots":
r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/ed/ALttP_Pegasus_Shoes_Sprite.png?version=405f42f97240c9dcd2b71ffc4bebc7f9", r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/ed/ALttP_Pegasus_Shoes_Sprite.png?version=405f42f97240c9dcd2b71ffc4bebc7f9",
"Progressive Glove": "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": "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": "Moon Pearl":
r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/6/63/ALttP_Moon_Pearl_Sprite.png?version=d601542d5abcc3e006ee163254bea77e", r"https://gamepedia.cursecdn.com/zelda_gamepedia_en/6/63/ALttP_Moon_Pearl_Sprite.png?version=d601542d5abcc3e006ee163254bea77e",
"Progressive Bow": "Progressive Bow":
@ -246,6 +260,14 @@ def attribute_item(inventory, team, recipient, item):
inventory[team][recipient][target_item] += 1 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() @app.template_filter()
def render_timedelta(delta: datetime.timedelta): def render_timedelta(delta: datetime.timedelta):
hours, minutes = divmod(delta.total_seconds() / 60, 60) hours, minutes = divmod(delta.total_seconds() / 60, 60)
@ -301,6 +323,145 @@ def get_static_room_data(room: Room):
return result return result
@app.route('/tracker/<suuid:tracker>/<int:team>/<int:player>')
@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/<suuid:tracker>') @app.route('/tracker/<suuid:tracker>')
@cache.memoize(timeout=30) # update every 30 seconds @cache.memoize(timeout=30) # update every 30 seconds
def getTracker(tracker: UUID): def getTracker(tracker: UUID):

Binary file not shown.

View File

@ -404,6 +404,11 @@ rom:
quickswap: # Enable switching items by pressing the L+R shoulder buttons quickswap: # Enable switching items by pressing the L+R shoulder buttons
on: 50 on: 50
off: 0 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. reduceflashing: # Reduces instances of flashing such as lightning attacks, weather, ether and more.
on: 50 on: 50
off: 0 off: 0

View File

@ -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('--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('--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('--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), parser.add_argument('--mapshuffle', default=defval(False),
help='Maps are no longer restricted to their dungeons, but can be anywhere', 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', 'remote_items', 'progressive', 'dungeon_counters', 'glitch_boots', 'killable_thieves',
'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool', 'dark_room_logic', 'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool', 'dark_room_logic',
'restrict_dungeon_item_on_boss', 'reduceflashing', 'game', '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) value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
if player == 1: if player == 1:
setattr(ret, name, {1: value}) setattr(ret, name, {1: value})

View File

@ -361,7 +361,7 @@ def generate_itempool(world, player: int):
world.clock_mode[player] = clock_mode world.clock_mode[player] = clock_mode
if treasure_hunt_count is not None: 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: if treasure_hunt_icon is not None:
world.treasure_hunt_icon[player] = treasure_hunt_icon world.treasure_hunt_icon[player] = treasure_hunt_icon

View File

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'a0a9511a2a59e5e8009b38718f8da1bf' RANDOMIZERBASEHASH = 'b2201c6fa50ba7a6ac42d73f37d75493'
import io import io
import json import json
@ -1153,8 +1153,8 @@ def patch_rom(world, rom, player, team, enemized):
rom.write_int32(0x18020C, 0) # starting time (in frames, sint32) rom.write_int32(0x18020C, 0) # starting time (in frames, sint32)
# set up goals for treasure hunt # 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_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_byte(0x180194, 1) # Must turn in triforced pieces (instant win not enabled)
rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed
@ -1681,7 +1681,7 @@ def hud_format_text(text):
return output[:32] 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): world=None, player=1, allow_random_on_event=False, reduceflashing=False):
local_random = random if not world else world.rom_seeds[player] 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(0x6FA30, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x65561, {'red': 0x05, 'blue': 0x0D, 'green': 0x19, 'yellow': 0x09}[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: if z3pr:
def buildAndRandomize(option_name, mode): def buildAndRandomize(option_name, mode):
options = { options = {