Skip to content

Commit

Permalink
OoS: Major updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Dinopony committed Feb 24, 2024
1 parent a4292df commit 79141af
Show file tree
Hide file tree
Showing 13 changed files with 308 additions and 79 deletions.
52 changes: 31 additions & 21 deletions worlds/tloz_oos/Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@

ROM_ADDRS = {
"game_identifier": (0x0134, 9, "ROM"),
"slot_name": (0x0000, 64, "ROM"), # TODO: Inject using patcher and put addr here
"slot_name": (0xFFFC0, 64, "ROM"),
}

RAM_ADDRS = {
"game_state": (0xC2EE, 1, "System Bus"),
"received_item_index": (0xC6A0, 2, "System Bus"),
"received_item": (0xCBFB, 1, "System Bus"),
"location_flags": (0xC600, 0x500, "System Bus"), # TODO: Find and set the real address
"game_clear_byte": (0x0000, 1, "System Bus"), # TODO: Find and set the real address
"location_flags": (0xC600, 0x500, "System Bus"),

"current_map_group": (0xCC49, 1, "System Bus"),
"current_map_id": (0xCC4C, 1, "System Bus"),
}

class OracleOfSeasonsClient(BizHawkClient):
Expand All @@ -32,9 +34,9 @@ class OracleOfSeasonsClient(BizHawkClient):

def __init__(self) -> None:
super().__init__()
self.local_checked_locations = set()
self.item_id_to_name = build_item_id_to_name_dict()
self.location_name_to_id = build_location_name_to_id_dict()
self.local_checked_locations = set()

async def validate_rom(self, ctx: "BizHawkClientContext") -> bool:
try:
Expand All @@ -56,8 +58,8 @@ async def validate_rom(self, ctx: "BizHawkClientContext") -> bool:
return True

async def set_auth(self, ctx: "BizHawkClientContext") -> None:
# slot_name_bytes = (await bizhawk.read(ctx.bizhawk_ctx, [ROM_ADDRS["slot_name"]]))[0]
# ctx.auth = bytes([byte for byte in slot_name_bytes if byte != 0]).decode("utf-8")
slot_name_bytes = (await bizhawk.read(ctx.bizhawk_ctx, [ROM_ADDRS["slot_name"]]))[0]
ctx.auth = bytes([byte for byte in slot_name_bytes if byte != 0]).decode("utf-8")
pass

async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
Expand All @@ -72,7 +74,8 @@ async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
RAM_ADDRS["received_item_index"], # Number of received items
RAM_ADDRS["received_item"], # Received item still pending?
RAM_ADDRS["location_flags"], # Location flags
RAM_ADDRS["game_clear_byte"], # Has goal been completed?
RAM_ADDRS["current_map_group"],
RAM_ADDRS["current_map_id"], # Current map & id to check for goal completion
])
if read_result is None:
return
Expand All @@ -84,7 +87,7 @@ async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
num_received_items = int.from_bytes(read_result[1], "little")
received_item_is_empty = (read_result[2][0] == 0)
flag_bytes = read_result[3]
game_clear = (read_result[4] == 1) # TODO: Might be a different value
game_clear = (read_result[4][0] == 0x07 and read_result[5][0] == 0x90)

# If the game hasn't received all items yet and the received item struct doesn't contain an item, then
# fill it with the next item
Expand All @@ -105,26 +108,33 @@ async def game_watcher(self, ctx: "BizHawkClientContext") -> None:
])

# Read location flags from RAM
local_checked_locations = set()
for name, location in LOCATIONS_DATA.items():
if "local" in location and location["local"] is True:
continue
if "randomized" in location and location["randomized"] is False:
continue
byte_offset = location["flag_byte"] - RAM_ADDRS["location_flags"][0]
bit_mask = location["bit_mask"] if "bit_mask" in location else 0x20
if flag_bytes[byte_offset] & bit_mask == bit_mask:
location_id = self.location_name_to_id[name]
local_checked_locations.add(location_id)

bytes_to_test = location["flag_byte"]
if not hasattr(bytes_to_test, "__len__"):
bytes_to_test = [bytes_to_test]

for byte_addr in bytes_to_test:
byte_offset = byte_addr - RAM_ADDRS["location_flags"][0]
bit_mask = location["bit_mask"] if "bit_mask" in location else 0x20
if flag_bytes[byte_offset] & bit_mask == bit_mask:
location_id = self.location_name_to_id[name]
self.local_checked_locations.add(location_id)
break

for loc in ctx.locations_checked:
self.local_checked_locations.add(loc)

# Send locations
if local_checked_locations != ctx.locations_checked:
self.local_checked_locations = local_checked_locations
if local_checked_locations is not None:
await ctx.send_msgs([{
"cmd": "LocationChecks",
"locations": list(local_checked_locations)
}])
if self.local_checked_locations != ctx.locations_checked:
await ctx.send_msgs([{
"cmd": "LocationChecks",
"locations": list(self.local_checked_locations)
}])

# Send game clear
if not ctx.finished_game and game_clear:
Expand Down
10 changes: 5 additions & 5 deletions worlds/tloz_oos/Logic.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from BaseClasses import MultiWorld
from worlds.tloz_oos.data.logic.dungeons import (make_d0_logic, make_d1_logic, make_d2_logic, make_d3_logic,
make_d4_logic, make_d5_logic, make_d6_logic, make_d7_logic,
make_d8_logic)
from worlds.tloz_oos.data.logic.holodrum import make_holodrum_logic
from worlds.tloz_oos.data.logic.subrosia import make_subrosia_logic
from worlds.tloz_oos.data.logic.DungeonsLogic import (make_d0_logic, make_d1_logic, make_d2_logic, make_d3_logic,
make_d4_logic, make_d5_logic, make_d6_logic, make_d7_logic,
make_d8_logic)
from worlds.tloz_oos.data.logic.OverworldLogic import make_holodrum_logic
from worlds.tloz_oos.data.logic.SubrosiaLogic import make_subrosia_logic


def create_connections(multiworld: MultiWorld, player: int):
Expand Down
20 changes: 13 additions & 7 deletions worlds/tloz_oos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
from .Logic import create_connections
from .Options import *
from .data import LOCATIONS_DATA
from .data.logic.constants import SEED_ITEMS, REGIONS_CONVERSION_TABLE, PORTALS_CONVERSION_TABLE, DUNGEON_NAMES, \
SEASONS, COMPANIONS, ESSENCES, DIRECTIONS
from .data.logic.regions import REGIONS
from .data.Constants import SEED_ITEMS, REGIONS_CONVERSION_TABLE, PORTALS_CONVERSION_TABLE, DUNGEON_NAMES, \
SEASONS, COMPANIONS, ESSENCES, DIRECTIONS, DUNGEON_ITEMS
from .data.Regions import REGIONS
from .Client import OracleOfSeasonsClient # Unused, but required to register with BizHawkClient


class OracleOfSeasonsWeb(WebWorld):
theme = "grass"
tutorials = [Tutorial(
Expand Down Expand Up @@ -200,14 +201,16 @@ def create_items(self):

item_name = loc_data['vanilla_item']
if item_name == "Ricky's Gloves": # Ricky's gloves are useless in current logic
item_name = "Gasha Seed"
item_name = "Progressive Sword"
elif item_name == "Rod of Seasons": # No lone rod of seasons supported for now
item_name = "Fool's Ore" if self.options.fools_ore != "excluded" else "Gasha Seed"
elif item_name == "Flute":
item_name = str(self.options.animal_companion.value) + "'s Flute"

if "Ring" in item_name:
ring_count += 1
elif any([(string in item_name) for string in DUNGEON_ITEMS]):
self.pre_fill_items.append(self.create_item(item_name))
else:
self.multiworld.itempool.append(self.create_item(item_name))

Expand Down Expand Up @@ -293,11 +296,12 @@ def generate_output(self, output_directory: str):
yamlObj = {
"settings": {
"companion": self.options.animal_companion.value,
"warp_to_start": self.options.warp_to_start,
"warp_to_start": self.options.warp_to_start.current_key,
"required_essences": self.options.required_essences.value,
"fools_ore_damage": 3 if self.options.fools_ore == "balanced" else 12,
"heart_beep_interval": self.options.heart_beep_interval,
"lost_woods_item_sequence": "winter up winter right winter down summer left"
"heart_beep_interval": self.options.heart_beep_interval.current_key,
"lost_woods_item_sequence": ' '.join(self.lost_woods_item_sequence),
"slot_name": self.multiworld.get_player_name(self.player)
},
"default seasons": {},
"locations": {}
Expand All @@ -317,6 +321,8 @@ def generate_output(self, output_directory: str):
yamlObj["subrosia portals"][PORTALS_CONVERSION_TABLE[portal_holo]] = PORTALS_CONVERSION_TABLE[portal_sub]

for loc in self.multiworld.get_locations(self.player):
if loc.address is None:
continue
item_name = loc.item.name if loc.item.player == loc.player else "Archipelago Item"
yamlObj["locations"][loc.name] = item_name

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,11 @@
"Blowing Wind",
"Seed of Life",
"Changing Seasons",
]
]

DUNGEON_ITEMS = [
"Small Key",
"Boss Key",
"Compass",
"Dungeon Map"
]
34 changes: 12 additions & 22 deletions worlds/tloz_oos/data/Items.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,61 +368,51 @@

"Cuccodex": {
'classification': ItemClassification.progression,
'id': 0x41,
'subid': 0x00
'id': 0x55
},
"Lon Lon Egg": {
'classification': ItemClassification.progression,
'id': 0x41,
'subid': 0x01
'id': 0x56
},
"Ghastly Doll": {
'classification': ItemClassification.progression,
'id': 0x41,
'subid': 0x02
'id': 0x57
},
"Iron Pot": {
'classification': ItemClassification.progression,
'id': 0x41,
'subid': 0x03
'id': 0x35
},
"Lava Soup": {
'classification': ItemClassification.progression,
'id': 0x41,
'subid': 0x04
'id': 0x38
},
"Goron Vase": {
'classification': ItemClassification.progression,
'id': 0x41,
'subid': 0x05
'id': 0x39
},
"Fish": {
'classification': ItemClassification.progression,
'id': 0x41,
'subid': 0x06
'id': 0x3a
},
"Megaphone": {
'classification': ItemClassification.progression,
'id': 0x41,
'subid': 0x07
'id': 0x3b
},
"Mushroom": {
'classification': ItemClassification.progression,
'id': 0x41,
'subid': 0x08
'id': 0x3c
},
"Wooden Bird": {
'classification': ItemClassification.progression,
'id': 0x41,
'subid': 0x09
'id': 0x3d
},
"Engine Grease": {
'classification': ItemClassification.progression,
'id': 0x41
'id': 0x3e
},
"Phonograph": {
'classification': ItemClassification.progression,
'id': 0x41,
'id': 0x3f
},

"Gnarled Key": {
Expand Down
17 changes: 12 additions & 5 deletions worlds/tloz_oos/data/Locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"maku tree": {
"region_id": "maku tree",
"vanilla_item": "Gnarled Key",
"flag_byte": 0xC80B # TODO: Might require more map flags (for different essence counts)
"flag_byte": [0xC80B, 0xC80C, 0xC82B, 0xC82C, 0xC82D, 0xC85B, 0xC85C, 0xC85D, 0xC87B]
# Maku Tree has several rooms depending on the amount of essences owned
},
"horon village SW chest": {
"region_id": "horon village SW chest",
Expand Down Expand Up @@ -202,7 +203,8 @@
"shop, 150 rupees": {
"region_id": "shop, 150 rupees",
"vanilla_item": "Flute",
"local": True # TODO
"flag_byte": 0xC693,
"bit_mask": 0x80
},
"member's shop 1": {
"region_id": "member's shop",
Expand Down Expand Up @@ -256,13 +258,18 @@
"subrosia seaside": {
"region_id": "subrosia seaside",
"vanilla_item": "Star Ore",
"flag_byte": 0xC865 # or C866, C875, C876
"flag_byte": [0xC865, 0xC866, 0xC875, 0xC876]
},
"subrosian wilds chest": {
"region_id": "subrosian wilds chest",
"vanilla_item": "Blue Ore",
"flag_byte": 0xC841
},
"subrosian wilds digging spot": {
"region_id": "subrosian wilds digging spot",
"vanilla_item": "Rupees (100)",
"flag_byte": 0xC840
},
"subrosia village chest": {
"region_id": "subrosia village chest",
"vanilla_item": "Red Ore",
Expand Down Expand Up @@ -1010,8 +1017,8 @@
"vanilla_item": "Wooden Bird",
"flag_byte": 0xC89C
},
"tick-tock trade": {
"region_id": "tick-tock trade",
"tick tock trade": {
"region_id": "tick tock trade",
"vanilla_item": "Engine Grease",
"flag_byte": 0xC883
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"maple trade",
"subrosian chef trade",
"biggoron trade",
"tick-tock trade",
"tick tock trade",
"ingo trade",
"guru-guru trade",
"mayor's gift",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from worlds.tloz_oos.data.logic.predicates import *
from worlds.tloz_oos.data.logic.LogicPredicates import *


def make_d0_logic(player: int):
Expand Down Expand Up @@ -176,7 +176,7 @@ def make_d4_logic(player: int):
])],
["d4 north of entrance", "d4 pot puzzle", False, lambda state: all([
oos_has_bombs(state, player),
oos_can_break_pot(state, player)
oos_has_bracelet(state, player)
])],
["d4 north of entrance", "d4 maze chest", False, lambda state: any([
oos_can_trigger_lever_from_minecart(state, player),
Expand Down
Loading

0 comments on commit 79141af

Please sign in to comment.