Ocarina of Time 7.0 (#1277)

## What is this fixing or adding?
- Adds the majority of OoTR 7.0 features:
  - Pot shuffle, Freestanding item shuffle, Crate shuffle, Beehive shuffle
  - Key rings mode
  - Dungeon shortcuts to speed up dungeons
  - "Regional" shuffle for dungeon items
  - New options for shop pricing in shopsanity
  - Expanded Ganon's Boss Key shuffle options
  - Pre-planted beans
  - Improved Chest Appearance Matches Contents mode
  - Blue Fire Arrows
  - Bonk self-damage
  - Finer control over MQ dungeons and spawn position randomization
- Several bugfixes as a result of the update:
  - Items recognized by the server and valid starting items are now in a 1-to-1 correspondence. In particular, starting with keys is now supported.
  - Entrance randomization success rate improved. Hopefully it is now at 100%. 

Co-authored-by: Zach Parks <zach@alliware.com>
This commit is contained in:
espeon65536
2022-12-10 21:11:40 -06:00
committed by GitHub
parent 2cdd03f786
commit aee0df5359
110 changed files with 37691 additions and 18648 deletions

View File

@@ -72,7 +72,14 @@ def download_slot_file(room_id, player_id: int):
if name.endswith("info.json"):
fname = name.rsplit("/", 1)[0] + ".zip"
elif slot_data.game == "Ocarina of Time":
fname = f"AP_{app.jinja_env.filters['suuid'](room_id)}_P{slot_data.player_id}_{slot_data.player_name}.apz5"
stream = io.BytesIO(slot_data.data)
if zipfile.is_zipfile(stream):
with zipfile.ZipFile(stream) as zf:
for name in zf.namelist():
if name.endswith(".zpf"):
fname = name.rsplit(".", 1)[0] + ".apz5"
else: # pre-ootr-7.0 support
fname = f"AP_{app.jinja_env.filters['suuid'](room_id)}_P{slot_data.player_id}_{slot_data.player_name}.apz5"
elif slot_data.game == "VVVVVV":
fname = f"AP_{app.jinja_env.filters['suuid'](room_id)}_SP.apv6"
elif slot_data.game == "Zillion":

View File

@@ -9,7 +9,7 @@
border-top-left-radius: 4px;
border-top-right-radius: 4px;
padding: 3px 3px 10px;
width: 448px;
width: 480px;
background-color: rgb(60, 114, 157);
}
@@ -46,7 +46,7 @@
}
#location-table{
width: 448px;
width: 480px;
border-left: 2px solid #000000;
border-right: 2px solid #000000;
border-bottom: 2px solid #000000;
@@ -108,7 +108,7 @@
}
#location-table td:first-child {
width: 272px;
width: 300px;
}
.location-category td:first-child {

View File

@@ -636,43 +636,47 @@ def __renderOoTTracker(multisave: Dict[str, Any], room: Room, locations: Dict[in
# Gather dungeon locations
area_id_ranges = {
"Overworld": (67000, 67280),
"Deku Tree": (67281, 67303),
"Dodongo's Cavern": (67304, 67334),
"Jabu Jabu's Belly": (67335, 67359),
"Bottom of the Well": (67360, 67384),
"Forest Temple": (67385, 67420),
"Fire Temple": (67421, 67457),
"Water Temple": (67458, 67484),
"Shadow Temple": (67485, 67532),
"Spirit Temple": (67533, 67582),
"Ice Cavern": (67583, 67596),
"Gerudo Training Ground": (67597, 67635),
"Thieves' Hideout": (67259, 67263),
"Ganon's Castle": (67636, 67673),
"Overworld": ((67000, 67258), (67264, 67280), (67747, 68024), (68054, 68062)),
"Deku Tree": ((67281, 67303), (68063, 68077)),
"Dodongo's Cavern": ((67304, 67334), (68078, 68160)),
"Jabu Jabu's Belly": ((67335, 67359), (68161, 68188)),
"Bottom of the Well": ((67360, 67384), (68189, 68230)),
"Forest Temple": ((67385, 67420), (68231, 68281)),
"Fire Temple": ((67421, 67457), (68282, 68350)),
"Water Temple": ((67458, 67484), (68351, 68483)),
"Shadow Temple": ((67485, 67532), (68484, 68565)),
"Spirit Temple": ((67533, 67582), (68566, 68625)),
"Ice Cavern": ((67583, 67596), (68626, 68649)),
"Gerudo Training Ground": ((67597, 67635), (68650, 68656)),
"Thieves' Hideout": ((67259, 67263), (68025, 68053)),
"Ganon's Castle": ((67636, 67673), (68657, 68705)),
}
def lookup_and_trim(id, area):
full_name = lookup_any_location_id_to_name[id]
if id == 67673:
return full_name[13:] # Ganons Tower Boss Key Chest
if 'Ganons Tower' in full_name:
return full_name
if area not in ["Overworld", "Thieves' Hideout"]:
# trim dungeon name. leaves an extra space that doesn't display, or trims fully for DC/Jabu/GC
return full_name[len(area):]
return full_name
checked_locations = multisave.get("location_checks", {}).get((team, player), set()).intersection(set(locations[player]))
location_info = {area: {lookup_and_trim(id, area): id in checked_locations for id in range(min_id, max_id+1) if id in locations[player]}
for area, (min_id, max_id) in area_id_ranges.items()}
checks_done = {area: len(list(filter(lambda x: x, location_info[area].values()))) for area in area_id_ranges}
checks_in_area = {area: len([id for id in range(min_id, max_id+1) if id in locations[player]])
for area, (min_id, max_id) in area_id_ranges.items()}
# Remove Thieves' Hideout checks from Overworld, since it's in the middle of the range
checks_in_area["Overworld"] -= checks_in_area["Thieves' Hideout"]
checks_done["Overworld"] -= checks_done["Thieves' Hideout"]
for loc in location_info["Thieves' Hideout"]:
del location_info["Overworld"][loc]
location_info = {}
checks_done = {}
checks_in_area = {}
for area, ranges in area_id_ranges.items():
location_info[area] = {}
checks_done[area] = 0
checks_in_area[area] = 0
for r in ranges:
min_id, max_id = r
for id in range(min_id, max_id+1):
if id in locations[player]:
checked = id in checked_locations
location_info[area][lookup_and_trim(id, area)] = checked
checks_in_area[area] += 1
checks_done[area] += checked
checks_done['Total'] = sum(checks_done.values())
checks_in_area['Total'] = sum(checks_in_area.values())
@@ -683,25 +687,28 @@ def __renderOoTTracker(multisave: Dict[str, Any], room: Room, locations: Dict[in
if "GS" in lookup_and_trim(id, ''):
display_data["token_count"] += 1
oot_y = ''
oot_x = ''
# Gather small and boss key info
small_key_counts = {
"Forest Temple": inventory[66175],
"Fire Temple": inventory[66176],
"Water Temple": inventory[66177],
"Spirit Temple": inventory[66178],
"Shadow Temple": inventory[66179],
"Bottom of the Well": inventory[66180],
"Gerudo Training Ground": inventory[66181],
"Thieves' Hideout": inventory[66182],
"Ganon's Castle": inventory[66183],
"Forest Temple": oot_y if inventory[66203] else inventory[66175],
"Fire Temple": oot_y if inventory[66204] else inventory[66176],
"Water Temple": oot_y if inventory[66205] else inventory[66177],
"Spirit Temple": oot_y if inventory[66206] else inventory[66178],
"Shadow Temple": oot_y if inventory[66207] else inventory[66179],
"Bottom of the Well": oot_y if inventory[66208] else inventory[66180],
"Gerudo Training Ground": oot_y if inventory[66209] else inventory[66181],
"Thieves' Hideout": oot_y if inventory[66210] else inventory[66182],
"Ganon's Castle": oot_y if inventory[66211] else inventory[66183],
}
boss_key_counts = {
"Forest Temple": '' if inventory[66149] else '',
"Fire Temple": '' if inventory[66150] else '',
"Water Temple": '' if inventory[66151] else '',
"Spirit Temple": '' if inventory[66152] else '',
"Shadow Temple": '' if inventory[66153] else '',
"Ganon's Castle": '' if inventory[66154] else '',
"Forest Temple": oot_y if inventory[66149] else oot_x,
"Fire Temple": oot_y if inventory[66150] else oot_x,
"Water Temple": oot_y if inventory[66151] else oot_x,
"Spirit Temple": oot_y if inventory[66152] else oot_x,
"Shadow Temple": oot_y if inventory[66153] else oot_x,
"Ganon's Castle": oot_y if inventory[66154] else oot_x,
}
# Victory condition