Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ocarina of Time 7.0 #1277

Merged
merged 63 commits into from
Dec 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
a0fa4e4
Update rom patch and symbols
espeon65536 Nov 21, 2022
a5437c7
Update logic tricks
espeon65536 Nov 21, 2022
021059b
update oot options
espeon65536 Nov 21, 2022
3e4a31f
Update data files
espeon65536 Nov 21, 2022
2ecc5f2
fix some minor oversights
espeon65536 Nov 21, 2022
eb53756
Update location list, item list, and datapackage version
espeon65536 Nov 22, 2022
d1f4823
add new item data
espeon65536 Nov 22, 2022
b6e506d
add DisableType
espeon65536 Nov 22, 2022
9035952
rename to shuffle_hideoutkeys internall
espeon65536 Nov 22, 2022
659a973
update item pool generator
espeon65536 Nov 22, 2022
6f1435c
Add bosses files
espeon65536 Nov 24, 2022
1a51adc
restore generation of default settings
espeon65536 Nov 25, 2022
e30f946
fix file select screen crash
espeon65536 Nov 25, 2022
337bf86
Update license year
espeon65536 Nov 25, 2022
b96eb6c
enable regional shuffle for dungeon items
espeon65536 Nov 25, 2022
131d46c
Update hint distribution options
espeon65536 Nov 25, 2022
f93f50d
Pot/freestanding/etc. checks register in the AP client
espeon65536 Nov 28, 2022
b5a932e
frog song checks registered in client
espeon65536 Nov 28, 2022
c715013
remove one of the magic beans from item_name_to_id
espeon65536 Nov 28, 2022
ea92aee
Merge branch 'main' of https://github.com/ArchipelagoMW/Archipelago i…
espeon65536 Nov 28, 2022
eee3ed6
Fix spawn_positions and shuffle_dungeon_entrances options
espeon65536 Nov 28, 2022
ce15734
fix copied references to world rather than ootworld
espeon65536 Nov 28, 2022
65bcc9c
fix shopsanity prices trying to use built-in random
espeon65536 Nov 28, 2022
4e9f0ee
fix ER generation failures from incorrectly assuming prizes behind ra…
espeon65536 Nov 29, 2022
c972a7a
re-add GV lower stream -> lake hylia to entrance list
espeon65536 Nov 29, 2022
8ec5947
fix inverted bean salesman logic
espeon65536 Nov 30, 2022
33297e0
OoT: convert to APContainer format
espeon65536 Nov 30, 2022
4901e0a
quick hack to fix triforce option rename
espeon65536 Nov 30, 2022
911144b
Get new SaveContext file, tweak save context usage accordingly
espeon65536 Nov 30, 2022
8ac405a
replace give_raw_item with give_item
espeon65536 Nov 30, 2022
1671707
OoT: some client repairs
espeon65536 Nov 30, 2022
f283723
OoT: fixed KF Shop Blue Rupee client tracking
espeon65536 Nov 30, 2022
cc8f22a
fix undefined variable usage
espeon65536 Dec 2, 2022
f5c7f7b
Merge branch 'main' of https://github.com/ArchipelagoMW/Archipelago i…
espeon65536 Dec 3, 2022
e8f7f02
OoT: various small betatest fixes
espeon65536 Dec 3, 2022
9351de4
Repair keyring functionality with custom collect methods
espeon65536 Dec 4, 2022
d6ea574
fix boss shuffle
espeon65536 Dec 4, 2022
dcf3687
Fix dungeon pot/crate shuffle; clarify beehive shuffle docstring
espeon65536 Dec 4, 2022
32ba1d1
fix AP item model in shops
espeon65536 Dec 4, 2022
72206cc
Fix skulltulas never being registered as checks
espeon65536 Dec 4, 2022
6d88e39
update misc hints to 7.0 version
espeon65536 Dec 4, 2022
9f9cbe6
Merge branch 'main' of https://github.com/ArchipelagoMW/Archipelago i…
espeon65536 Dec 4, 2022
a17e4c7
also fix freestanding item shuffle in dungeons
espeon65536 Dec 4, 2022
bdbacb5
strip apostrophes from key_rings_list
espeon65536 Dec 4, 2022
629fdd3
fix junk fill if bridge setting is hearts
espeon65536 Dec 5, 2022
25ebc26
fix item creation call
espeon65536 Dec 5, 2022
53bbca2
Repair misc hints and include the new hints as part of it
espeon65536 Dec 6, 2022
797323d
Fix DC single/double eye switch pot addresses
espeon65536 Dec 6, 2022
f62887c
Add player name to file select screen
espeon65536 Dec 6, 2022
4c1f06a
Update logic and ASM to OoTR 7.1.0
espeon65536 Dec 7, 2022
4c5f749
Fix minor ER hint data bug, set appropriate hint data for boss items
espeon65536 Dec 7, 2022
d8ed953
change function typing to satisfy linter
espeon65536 Dec 8, 2022
7cfb2c3
Remove voice banks from AP repo
espeon65536 Dec 8, 2022
b43724a
Merge branch 'main' of https://github.com/ArchipelagoMW/Archipelago i…
espeon65536 Dec 8, 2022
7cd5390
Merge branch 'main' into oot7.0
ThePhar Dec 8, 2022
b7f5951
wipe temp context after reading so new values can be written by ROM
espeon65536 Dec 9, 2022
0f93d63
remove misleading junk hint referencing woth East Clock Town
espeon65536 Dec 9, 2022
53bd581
Merge branch 'oot7.0' of https://github.com/espeon65536/Archipelago i…
espeon65536 Dec 9, 2022
441ca30
Update OoT tracker with new locations
espeon65536 Dec 9, 2022
2e90b88
Update generic tracker for new locations
espeon65536 Dec 9, 2022
d090477
Fix permanent flag read location for OoT boss items
espeon65536 Dec 9, 2022
5d3f25a
fix region_has_shortcuts
espeon65536 Dec 9, 2022
9306f46
old patch support in webhost downloads
espeon65536 Dec 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions OoTAdjuster.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
import random
import os
import zipfile
from itertools import chain

from BaseClasses import MultiWorld
Expand Down Expand Up @@ -217,13 +218,18 @@ def adjust(args):
# Load up the ROM
rom = Rom(file=args.rom, force_use=True)
delete_zootdec = True
elif os.path.splitext(args.rom)[-1] == '.apz5':
elif os.path.splitext(args.rom)[-1] in ['.apz5', '.zpf']:
# Load vanilla ROM
rom = Rom(file=args.vanilla_rom, force_use=True)
apz5_file = args.rom
base_name = os.path.splitext(apz5_file)[0]
# Patch file
apply_patch_file(rom, args.rom)
apply_patch_file(rom, apz5_file,
sub_file=(os.path.basename(base_name) + '.zpf'
if zipfile.is_zipfile(apz5_file)
else None))
else:
raise Exception("Invalid file extension; requires .n64, .z64, .apz5")
raise Exception("Invalid file extension; requires .n64, .z64, .apz5, .zpf")
# Call patch_cosmetics
try:
patch_cosmetics(ootworld, rom)
Expand Down
46 changes: 32 additions & 14 deletions OoTClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import multiprocessing
import subprocess
import zipfile
from asyncio import StreamReader, StreamWriter

# CommonClient import first to trigger ModuleUpdater
Expand Down Expand Up @@ -50,7 +51,7 @@

oot_loc_name_to_id = network_data_package["games"]["Ocarina of Time"]["location_name_to_id"]

script_version: int = 2
script_version: int = 3

def get_item_value(ap_id):
return ap_id - 66000
Expand Down Expand Up @@ -85,6 +86,9 @@ def __init__(self, server_address, password):
self.n64_status = CONNECTION_INITIAL_STATUS
self.awaiting_rom = False
self.location_table = {}
self.collectible_table = {}
self.collectible_override_flags_address = 0
self.collectible_offsets = {}
self.deathlink_enabled = False
self.deathlink_pending = False
self.deathlink_sent_this_death = False
Expand Down Expand Up @@ -117,6 +121,13 @@ class OoTManager(GameManager):
self.ui = OoTManager(self)
self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI")

def on_package(self, cmd, args):
if cmd == 'Connected':
slot_data = args.get('slot_data', None)
if slot_data:
self.collectible_override_flags_address = slot_data.get('collectible_override_flags', 0)
self.collectible_offsets = slot_data.get('collectible_flag_offsets', {})


def get_payload(ctx: OoTContext):
if ctx.deathlink_enabled and ctx.deathlink_pending:
Expand All @@ -125,11 +136,14 @@ def get_payload(ctx: OoTContext):
else:
trigger_death = False

return json.dumps({
payload = json.dumps({
"items": [get_item_value(item.item) for item in ctx.items_received],
"playerNames": [name for (i, name) in ctx.player_names.items() if i != 0],
"triggerDeath": trigger_death
"triggerDeath": trigger_death,
"collectibleOverrides": ctx.collectible_override_flags_address,
"collectibleOffsets": ctx.collectible_offsets
})
return payload


async def parse_payload(payload: dict, ctx: OoTContext, force: bool):
Expand All @@ -141,6 +155,7 @@ async def parse_payload(payload: dict, ctx: OoTContext, force: bool):
ctx.deathlink_client_override = False
ctx.finished_game = False
ctx.location_table = {}
ctx.collectible_table = {}
ctx.deathlink_pending = False
ctx.deathlink_sent_this_death = False
ctx.auth = payload['playerName']
Expand All @@ -161,11 +176,17 @@ async def parse_payload(payload: dict, ctx: OoTContext, force: bool):
ctx.finished_game = True

# Locations handling
if ctx.location_table != payload['locations']:
ctx.location_table = payload['locations']
locations = payload['locations']
collectibles = payload['collectibles']

if ctx.location_table != locations or ctx.collectible_table != collectibles:
ctx.location_table = locations
ctx.collectible_table = collectibles
locs1 = [oot_loc_name_to_id[loc] for loc, b in ctx.location_table.items() if b]
locs2 = [int(loc) for loc, b in ctx.collectible_table.items() if b]
await ctx.send_msgs([{
"cmd": "LocationChecks",
"locations": [oot_loc_name_to_id[loc] for loc in ctx.location_table if ctx.location_table[loc]]
"locations": locs1 + locs2
}])

# Deathlink handling
Expand All @@ -191,13 +212,6 @@ async def n64_sync_task(ctx: OoTContext):
try:
await asyncio.wait_for(writer.drain(), timeout=1.5)
try:
# Data will return a dict with up to six fields:
# 1. str: player name (always)
# 2. int: script version (always)
# 3. bool: deathlink active (always)
# 4. dict[str, bool]: checked locations
# 5. bool: whether Link is currently at 0 HP
# 6. bool: whether the game currently registers as complete
data = await asyncio.wait_for(reader.readline(), timeout=10)
data_decoded = json.loads(data.decode())
reported_version = data_decoded.get('scriptVersion', 0)
Expand Down Expand Up @@ -270,12 +284,16 @@ async def run_game(romfile):


async def patch_and_run_game(apz5_file):
apz5_file = os.path.abspath(apz5_file)
base_name = os.path.splitext(apz5_file)[0]
decomp_path = base_name + '-decomp.z64'
comp_path = base_name + '.z64'
# Load vanilla ROM, patch file, compress ROM
rom = Rom(Utils.local_path(Utils.get_options()["oot_options"]["rom_file"]))
apply_patch_file(rom, apz5_file)
apply_patch_file(rom, apz5_file,
sub_file=(os.path.basename(base_name) + '.zpf'
if zipfile.is_zipfile(apz5_file)
else None))
rom.write_to_file(decomp_path)
os.chdir(data_path("Compress"))
compress_rom_file(decomp_path, comp_path)
Expand Down
9 changes: 8 additions & 1 deletion WebHostLib/downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you test this? we currently file.seek(0) in upload.py line 128 as is_zipfile can leave the data stream partially read. I'd be surpised if that doesn't happen here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did test it and it seemed to work fine (the game patched and played properly)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting. well, let's see if it breaks for anyone.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe I should just seek to 0 or make a new stream to be safe though

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":
Expand Down
6 changes: 3 additions & 3 deletions WebHostLib/static/styles/ootTracker.css
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -108,7 +108,7 @@
}

#location-table td:first-child {
width: 272px;
width: 300px;
}

.location-category td:first-child {
Expand Down
91 changes: 49 additions & 42 deletions WebHostLib/tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -683,25 +687,28 @@ def lookup_and_trim(id, area):
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
Expand Down
Loading