Skip to content

Commit

Permalink
Merge pull request #26 from Elagatua/feature/another-dev-merge
Browse files Browse the repository at this point in the history
Fix unhintable dungeon barrens bug
  • Loading branch information
Elagatua authored Oct 6, 2022
2 parents 4b5a65b + 958449a commit 733836f
Show file tree
Hide file tree
Showing 16 changed files with 22,679 additions and 22,635 deletions.
1,862 changes: 931 additions & 931 deletions ASM/build/asm_symbols.txt

Large diffs are not rendered by default.

Binary file modified ASM/build/bundle.o
Binary file not shown.
352 changes: 176 additions & 176 deletions ASM/build/c_symbols.txt

Large diffs are not rendered by default.

9 changes: 3 additions & 6 deletions ASM/c/file_select.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ hash_symbol_t hash_symbols[32] = {
{ 1, 19 }, // Big Magic
};


uint32_t cfg_file_select_hash = 0;
extern uint8_t CFG_FILE_SELECT_HASH[5];

void draw_file_select_hash(uint32_t fade_out_alpha, z64_menudata_t* menu_data) {
z64_disp_buf_t *db = &(z64_ctxt.gfx->poly_opa);
Expand All @@ -73,10 +72,8 @@ void draw_file_select_hash(uint32_t fade_out_alpha, z64_menudata_t* menu_data) {
gDPSetCombineMode(db->p++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
gDPSetPrimColor(db->p++, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF);

int hash = cfg_file_select_hash;
for (int i = 0; i < icon_count; i++) {
int sym_index = hash & 0x1F;
hash >>= 5;
uint8_t sym_index = CFG_FILE_SELECT_HASH[i];
hash_symbol_t *sym_desc = &(hash_symbols[sym_index]);
sprite_t *sym_sprite = hash_sprites[sym_desc->sprite_index];

Expand All @@ -85,7 +82,7 @@ void draw_file_select_hash(uint32_t fade_out_alpha, z64_menudata_t* menu_data) {

left += icon_size + padding;
}

draw_file_message(db, menu_data);
draw_file_icons(db, menu_data);

Expand Down
2 changes: 1 addition & 1 deletion ASM/src/config.asm
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ PLANDOMIZER_USED:
; Any changes made here should be documented in Notes/auto-tracker-ctx.md
AUTO_TRACKER_CONTEXT:
AUTO_TRACKER_VERSION:
.word 3 ; Increment this if the auto-tracker context layout changes
.word 4 ; Increment this if the auto-tracker context layout changes

CFG_DUNGEON_INFO_ENABLE:
.word 0
Expand Down
7 changes: 6 additions & 1 deletion ASM/src/coop_state.asm
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
COOP_CONTEXT:

COOP_VERSION:
.word 3 ; Increment this if layout of co-op state changes
.word 4 ; Increment this if layout of co-op state changes

PLAYER_ID:
.byte 0x00 ; Written by frontend
Expand All @@ -27,3 +27,8 @@ OUTGOING_PLAYER:
.area 8*256, 0xDF
PLAYER_NAMES:
.endarea

.area 5, 0x00
CFG_FILE_SELECT_HASH:
.endarea
.align 4
16 changes: 13 additions & 3 deletions EntranceShuffle.py
Original file line number Diff line number Diff line change
Expand Up @@ -822,9 +822,19 @@ def validate_world(world, worlds, entrance_placed, locations_to_ensure_reachable

if locations_to_ensure_reachable:
max_search = Search.max_explore([w.state for w in worlds], itempool)
# If ALR is enabled, ensure all locations we want to keep reachable are indeed still reachable
# Otherwise, just continue if the game is still beatable
if not (world.check_beatable_only and max_search.can_beat_game(False)):
if world.check_beatable_only:
if worlds[0].settings.reachable_locations == 'goals':
# If this entrance is required for a goal, it must be placed somewhere reachable.
# We also need to check to make sure the game is beatable, since custom goals might not imply that.
predicate = lambda state: state.won() and state.has_all_item_goals()
else:
# If the game is not beatable without this entrance, it must be placed somewhere reachable.
predicate = State.won
perform_access_check = not max_search.can_beat_game(scan_for_items=False, predicate=predicate)
else:
# All entrances must be placed somewhere reachable.
perform_access_check = True
if perform_access_check:
max_search.visit_locations(locations_to_ensure_reachable)
for location in locations_to_ensure_reachable:
if not max_search.visited(location):
Expand Down
13 changes: 9 additions & 4 deletions Goals.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def __init__(self, world, name, hint_text, color, items=None, locations=None, lo
self.weight = 0
self.category = None
self._item_cache = {}

def copy(self):
new_goal = Goal(self.world, self.name, self.hint_text, self.color, self.items, self.locations, self.lock_locations, self.lock_entrances, self.required_locations, True)
return new_goal
Expand Down Expand Up @@ -157,7 +157,7 @@ def update_goal_items(spoiler):
# required_locations[category.name][goal.name][world_id] = [...]
required_locations = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
priority_locations = {(world.id): {} for world in worlds}

# rebuild hint exclusion list
for world in worlds:
hintExclusions(world, clear_cache=True)
Expand Down Expand Up @@ -213,7 +213,7 @@ def update_goal_items(spoiler):
required_locations.update(identified_locations)
woth_locations = list(required_locations['way of the hero'])
del required_locations['way of the hero']

# Update WOTH items
woth_locations_dict = {}
for world in worlds:
Expand Down Expand Up @@ -260,7 +260,7 @@ def update_goal_items(spoiler):
goal_locations[location[0]].append(goal_world)
goal.required_locations = [(location, 1, 1, world_ids) for location, world_ids in goal_locations.items()]
# Copy of goal categories for the spoiler log to reference
# since the hint algorithm mutates the world copy
# since the hint algorithm mutates the world copy
for world in worlds:
spoiler.goal_categories[world.id] = {cat_name: category.copy() for cat_name, category in world.goal_categories.items()}
spoiler.goal_locations = required_locations_dict
Expand Down Expand Up @@ -293,6 +293,7 @@ def search_goals(categories, reachable_goals, search, priority_locations, all_lo
world_ids = [state.world.id for state in search.state_list]
if search_woth:
required_locations['way of the hero'] = []
remaining_locations = all_locations[:]
for location in search.iter_reachable_locations(all_locations):
# Try to remove items one at a time and see if the goal is still reachable
if location in item_locations:
Expand Down Expand Up @@ -334,7 +335,11 @@ def search_goals(categories, reachable_goals, search, priority_locations, all_lo
required_locations['way of the hero'].append(location)
location.item = old_item
maybe_set_misc_item_hints(location)
remaining_locations.remove(location)
search.state_list[location.item.world.id].collect(location.item)
for location in remaining_locations:
# finally, collect unreachable locations for misc. item hints
maybe_set_misc_item_hints(location)
return required_locations


Expand Down
3 changes: 3 additions & 0 deletions HintList.py
Original file line number Diff line number Diff line change
Expand Up @@ -1594,6 +1594,9 @@ def tokens_required_by_settings(world):
'1233': ("When the moon hits Termina like a big pizza pie that's game over.", None, 'junk'), # ref: That's Amore by Dean Martin + Majora's Mask
'1234': ("Ganondorf doesn't specialize in hiding items, nor in keeping secrets for that matter.", None, 'junk'),
'1235': ("While you're wasting time reading this hint, the others are playing the seed.", None, 'junk'),
'1236': ("Have you ever tried hammering the ground or wall in a room with Torch Slugs, Flare Dancers, Tektites, Walltulas, Scrubs or Deku Babas?", None, 'junk'),
'1237': ("Did you know that there's a 1/201 chance per Rupee that the Zora from the diving minigame tosses a 500 Rupee?^Keep winning and the odds go up!", None, 'junk'),
'1238': ("J = 0;&while J < 10;& Press \u009F;& J++;^ Press \u009F;& J++;^ Press \u009F;& J++;^ Press \u009F;& J++;^ Press \u009F;& J++;^ Press \u009F;& J++;^ Press \u009F;& J++;^ Press \u009F;& J++;^ Press \u009F;& J++;^ Press \u009F;^break;", None, 'junk'), # \u009F = A button

'Deku Tree': ("an ancient tree", "the Deku Tree", 'dungeonName'),
'Dodongos Cavern': ("an immense cavern", "Dodongo's Cavern", 'dungeonName'),
Expand Down
2 changes: 1 addition & 1 deletion Hints.py
Original file line number Diff line number Diff line change
Expand Up @@ -1276,7 +1276,7 @@ def buildGossipHints(spoiler, worlds):
# Add misc. item hint locations to "checked" locations if the respective hint is reachable without the hinted item.
for world in worlds:
for location in world.hinted_dungeon_reward_locations.values():
if 'altar' in world.settings.misc_hints and not world.settings.enhance_map_compass and can_reach_hint(worlds, world.get_location('ToT Child Altar Hint' if location.item.info.stone else 'ToT Adult Altar Hint'), location):
if 'altar' in world.settings.misc_hints and not world.settings.enhance_map_compass and can_reach_hint(worlds, world.get_location('ToT Child Altar Hint' if location.item.info.stone else 'ToT Adult Altar Hint'), location) and not world.settings.triforce_blitz:
item_world = location.world
if item_world.id not in checkedLocations:
checkedLocations[item_world.id] = set()
Expand Down
31 changes: 29 additions & 2 deletions Notes/auto-tracker-ctx.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
The **auto-tracker context** is a region of memory where a portion of the randomizer's configuration variables are stored in a versioned layout defined below. Its intent is to give some symbols added by the randomizer well-defined addresses so that auto-trackers can read them reliably without needing the symbols.json for the specific randomizer version that was used to generate the seed.

The starting address of the auto-tracker context is listed at address `0x8040_000c` (randomizer context + `0xc`). On versions of the randomizer before this feature was added, the starting address is given as zero. At that address, the following data can be found:
The starting address of the auto-tracker context is listed at address `0x8040_000c` (randomizer context + `0xc`). On versions of the randomizer before this feature was added, the starting address is given as zero. At that address, a 4-byte integer can be found. This is the version number of the auto-tracker context and defines the layout of the remainder of the context according to the sections below. The current version is 4.

# Versions 1–2

|Offset|Name|Min version|Size|Description|
|--:|---|--:|--:|---|
|`0x00`|`AUTO_TRACKER_VERSION`|1|`0x04`|Defines which entries in this table are available. Future versions may also change the layout of this table in an incompatible manner. The current version is 3.|
|`0x04`|`CFG_DUNGEON_INFO_ENABLE`|1|`0x04`|Defines how information about dungeons will be displayed on the inventory screen on the pause menu. `0` = disabled, `1` = a single menu shown when holding A, `2` = separate menus shown when holding D-pad buttons.|
|`0x08`|`CFG_DUNGEON_INFO_MQ_ENABLE`|1|`0x04`|`1` if the dungeon info in the pause menu should include info about which dungeons are in Master Quest mode.|
|`0x0c`|`CFG_DUNGEON_INFO_MQ_NEED_MAP`|1|`0x04`|`1` if the Master Quest info should only be displayed for dungeons whose maps have been obtained or which don't have maps.|
Expand All @@ -21,3 +22,29 @@ The starting address of the auto-tracker context is listed at address `0x8040_00
|`0x46`|`TRIFORCE_PIECES_REQUIRED`|2|`0x02`|In Triforce hunt, the total number of Triforce pieces (across all worlds) required to win the game.|
|`0x48`|`SPECIAL_DEAL_COUNTS`|2|`0x08`|A byte representing the number of special deal slots in each shop, in the following order: KF Shop, Market Bazaar, Market Potion Shop, Market Bombchu Shop, Kak Bazaar, Kak Potion Shop, GC Shop, ZD Shop.|
|`0x50`|`CFG_DUNGEON_REWARD_AREAS`|3|`0xcf`|For each dungeon reward in the order of Emerald, Ruby, Sapphire, Light, Forest, Fire, Water, Shadow, and Spirit, a null-terminated `0x16`-byte (`0x17` including the null terminator) ASCII string containing the hint area of that reward, padded with spaces on the right.|

# Version 3

Special care needs to be taken when working with version 3 of the auto-tracker context, since a breaking change was made in version 6.2.181 of the randomizer without an accompanying change to the auto-tracker context version. Versions 6.2.180 of the randomizer and earlier use the same layout as version 2 described above (with the addition of the `CFG_DUNGEON_REWARD_AREAS` field), while versions 6.2.181 and later use the same layout as version 4 described below. The version number at addresses 0x35–0x37 of the ROM header may be used to check for the randomizer version, but this may not be accurate for forks of the randomizer if they merged the change early.

# Version 4

|Offset|Name|Min version|Size|Description|
|--:|---|--:|--:|---|
|`0x04`|`CFG_DUNGEON_INFO_ENABLE`|4|`0x04`|Defines how information about dungeons will be displayed on the inventory screen on the pause menu. `0` = disabled, `1` = a single menu shown when holding A, `2` = separate menus shown when holding D-pad buttons in addition to the summary menu on A.|
|`0x08`|`CFG_DUNGEON_INFO_MQ_ENABLE`|1|`0x04`|`1` if the dungeon info in the pause menu should include info about which dungeons are in Master Quest mode.|
|`0x0c`|`CFG_DUNGEON_INFO_MQ_NEED_MAP`|1|`0x04`|`1` if the Master Quest info should only be displayed for dungeons whose maps have been obtained or which don't have maps.|
|`0x10`|`CFG_DUNGEON_INFO_REWARD_ENABLE`|1|`0x04`|`1` if the dungeon info in the pause menu should include info about which medallions and stones are in which dungeon.|
|`0x14`|`CFG_DUNGEON_INFO_REWARD_NEED_COMPASS`|1|`0x04`|Defines which item is required to display the location of a dungeon reward in the pause menu. `0` = no item required, `1` = the compass of the dungeon in which the reward is found, `2` = the compass of the reward's vanilla dungeon. For example, if the Kokiri Emerald is in the Fire Temple, `1` means the Fire Temple compass is required and `2` means the Deku Tree compass is required.|
|`0x18`|`CFG_DUNGEON_INFO_REWARD_NEED_ALTAR`|1|`0x04`|`1` if the reward info should only be displayed for rewards whose Temple of Time altar text boxes have been read.|
|`0x1c`|`CFG_DUNGEON_INFO_REWARD_SUMMARY_ENABLE`|4|`0x04`|`1` if the summary menu shown when holding A should display dungeon reward locations.|
|`0x20`|`CFG_DUNGEON_REWARDS`|4|`0x0e`|A byte representing the medallion or stone for each dungeon. Dungeons without rewards are listed as `0xff`, and one reward is chosen arbitrarily if the dungeon has multiple rewards. For more complete data, use `CFG_DUNGEON_REWARD_AREAS` instead.|
|`0x2e`|`CFG_DUNGEON_IS_MQ`|4|`0x0e`|A byte set to `1` for each dungeon in Master Quest mode.|
|`0x3c`|`RAINBOW_BRIDGE_CONDITION`|4|`0x04`|The condition for spawning the rainbow bridge. `0` = open, `1` = medallions, `2` = dungeon rewards, `3` = stones, `4` = vanilla, `5` = tokens, `6` = hearts.|
|`0x40`|`LACS_CONDITION`|4|`0x04`|The condition for triggering the light arrow cutscene. `0` = vanilla, `1` = medallions, `2` = dungeons, `3` = stones, `4` = tokens, `5` = hearts.|
|`0x44`|`RAINBOW_BRIDGE_COUNT`|4|`0x02`|The number of items (of the kind defined in `RAINBOW_BRIDGE_CONDITION`) required to spawn the rainbow bridge.|
|`0x46`|`LACS_CONDITION_COUNT`|4|`0x02`|The number of items (of the kind defined in `LACS_CONDITION`) required to trigger the light arrow cutscene.|
|`0x48`|`TRIFORCE_HUNT_ENABLED`|4|`0x02`|`1` if Triforce hunt is enabled.|
|`0x4a`|`TRIFORCE_PIECES_REQUIRED`|4|`0x02`|In Triforce hunt, the total number of Triforce pieces (across all worlds) required to win the game.|
|`0x4c`|`SPECIAL_DEAL_COUNTS`|4|`0x08`|A byte representing the number of special deal slots in each shop, in the following order: KF Shop, Market Bazaar, Market Potion Shop, Market Bombchu Shop, Kak Bazaar, Kak Potion Shop, GC Shop, ZD Shop.|
|`0x54`|`CFG_DUNGEON_REWARD_AREAS`|4|`0xcf`|For each dungeon reward in the order of Emerald, Ruby, Sapphire, Light, Forest, Fire, Water, Shadow, and Spirit, a null-terminated `0x16`-byte (`0x17` including the null terminator) ASCII string containing the hint area of that reward, padded with spaces on the right.|
Loading

0 comments on commit 733836f

Please sign in to comment.