Fix trock entrances when intensity >= 3
Keysanity menu countdowns Standard rain state Multi-entrance dungeon bosses This dungeon/universl key drops skip pose More rupee candidates to remove for retro
This commit is contained in:
@@ -1499,6 +1499,10 @@ class Portal(object):
|
|||||||
self.deadEnd = False
|
self.deadEnd = False
|
||||||
self.light_world = False
|
self.light_world = False
|
||||||
|
|
||||||
|
def change_boss_exit(self, exit_idx):
|
||||||
|
self.default = False
|
||||||
|
self.boss_exit_idx = exit_idx
|
||||||
|
|
||||||
def change_door(self, new_door):
|
def change_door(self, new_door):
|
||||||
if new_door != self.door:
|
if new_door != self.door:
|
||||||
self.default = False
|
self.default = False
|
||||||
|
|||||||
@@ -369,6 +369,9 @@ def choose_portals(world, player):
|
|||||||
sanc = world.get_portal('Sanctuary', player)
|
sanc = world.get_portal('Sanctuary', player)
|
||||||
sanc.destination = True
|
sanc.destination = True
|
||||||
clean_up_portal_assignment(portal_assignment, dungeon, sanc, master_door_list, outstanding_portals)
|
clean_up_portal_assignment(portal_assignment, dungeon, sanc, master_door_list, outstanding_portals)
|
||||||
|
for target_region, possible_portals in info.required_passage.items():
|
||||||
|
info.required_passage[target_region] = [x for x in possible_portals if x != sanc.name]
|
||||||
|
info.required_passage = {x: y for x, y in info.required_passage.items() if len(y) > 0}
|
||||||
for target_region, possible_portals in info.required_passage.items():
|
for target_region, possible_portals in info.required_passage.items():
|
||||||
candidates = find_portal_candidates(master_door_list, dungeon, need_passage=True, crossed=cross_flag,
|
candidates = find_portal_candidates(master_door_list, dungeon, need_passage=True, crossed=cross_flag,
|
||||||
bk_shuffle=bk_shuffle)
|
bk_shuffle=bk_shuffle)
|
||||||
@@ -596,6 +599,8 @@ def create_dungeon_entrances(world, player):
|
|||||||
filtered_choices = [x for x in choices if any(y not in world.inaccessible_regions[player] for y in originating[key][x].keys())]
|
filtered_choices = [x for x in choices if any(y not in world.inaccessible_regions[player] for y in originating[key][x].keys())]
|
||||||
else:
|
else:
|
||||||
filtered_choices = dest_choices
|
filtered_choices = dest_choices
|
||||||
|
if len(filtered_choices) == 0:
|
||||||
|
raise Exception('No valid destinations')
|
||||||
choice = random.choice(filtered_choices)
|
choice = random.choice(filtered_choices)
|
||||||
r_name = portal.door.entrance.parent_region.name
|
r_name = portal.door.entrance.parent_region.name
|
||||||
split_map[key][choice].append(r_name)
|
split_map[key][choice].append(r_name)
|
||||||
@@ -650,6 +655,8 @@ def within_dungeon(world, player):
|
|||||||
target = portal.door.entrance.parent_region
|
target = portal.door.entrance.parent_region
|
||||||
connect_simple_door(world, 'Sanctuary Mirror Route', target, player)
|
connect_simple_door(world, 'Sanctuary Mirror Route', target, player)
|
||||||
|
|
||||||
|
refine_boss_exits(world, player)
|
||||||
|
|
||||||
|
|
||||||
def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info):
|
def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, builder_info):
|
||||||
dungeon_entrances, split_dungeon_entrances, world, player = builder_info
|
dungeon_entrances, split_dungeon_entrances, world, player = builder_info
|
||||||
@@ -943,6 +950,7 @@ def cross_dungeon(world, player):
|
|||||||
palette_assignment(world, player)
|
palette_assignment(world, player)
|
||||||
|
|
||||||
refine_hints(dungeon_builders)
|
refine_hints(dungeon_builders)
|
||||||
|
refine_boss_exits(world, player)
|
||||||
|
|
||||||
|
|
||||||
def assign_cross_keys(dungeon_builders, world, player):
|
def assign_cross_keys(dungeon_builders, world, player):
|
||||||
@@ -1138,6 +1146,44 @@ def refine_hints(dungeon_builders):
|
|||||||
location.hint_text = dungeon_hints[name]
|
location.hint_text = dungeon_hints[name]
|
||||||
|
|
||||||
|
|
||||||
|
def refine_boss_exits(world, player):
|
||||||
|
for d_name, d_boss in {'Desert Palace': 'Desert Boss',
|
||||||
|
'Skull Woods': 'Skull Boss',
|
||||||
|
'Turtle Rock': 'TR Boss'}.items():
|
||||||
|
possible_portals = []
|
||||||
|
current_boss = None
|
||||||
|
for portal_name in dungeon_portals[d_name]:
|
||||||
|
portal = world.get_portal(portal_name, player)
|
||||||
|
if not portal.destination:
|
||||||
|
possible_portals.append(portal)
|
||||||
|
if portal.boss_exit_idx > -1:
|
||||||
|
current_boss = portal
|
||||||
|
if len(possible_portals) == 1:
|
||||||
|
if possible_portals[0] != current_boss:
|
||||||
|
possible_portals[0].change_boss_exit(current_boss.boss_exit_idx)
|
||||||
|
current_boss.change_boss_exit(-1)
|
||||||
|
else:
|
||||||
|
reachable_portals = []
|
||||||
|
for portal in possible_portals:
|
||||||
|
start_area = portal.door.entrance.parent_region
|
||||||
|
state = ExplorationState(dungeon=d_name)
|
||||||
|
state.visit_region(start_area)
|
||||||
|
state.add_all_doors_check_unattached(start_area, world, player)
|
||||||
|
explore_state_not_inaccessible(state, world, player)
|
||||||
|
if state.visited_at_all(world.get_region(d_boss, player)):
|
||||||
|
reachable_portals.append(portal)
|
||||||
|
if len(reachable_portals) == 0:
|
||||||
|
reachable_portals = possible_portals
|
||||||
|
unreachable = world.inaccessible_regions[player]
|
||||||
|
filtered = [x for x in reachable_portals if x.door.entrance.connected_region.name not in unreachable]
|
||||||
|
if 0 < len(filtered) < len(reachable_portals):
|
||||||
|
reachable_portals = filtered
|
||||||
|
chosen_one = random.choice(reachable_portals) if len(reachable_portals) > 1 else reachable_portals[0]
|
||||||
|
if chosen_one != current_boss:
|
||||||
|
chosen_one.change_boss_exit(current_boss.boss_exit_idx)
|
||||||
|
current_boss.change_boss_exit(-1)
|
||||||
|
|
||||||
|
|
||||||
def convert_to_sectors(region_names, world, player):
|
def convert_to_sectors(region_names, world, player):
|
||||||
region_list = convert_regions(region_names, world, player)
|
region_list = convert_regions(region_names, world, player)
|
||||||
sectors = []
|
sectors = []
|
||||||
@@ -1806,6 +1852,15 @@ def explore_state(state, world, player):
|
|||||||
state.add_all_doors_check_unattached(connect_region, world, player)
|
state.add_all_doors_check_unattached(connect_region, world, player)
|
||||||
|
|
||||||
|
|
||||||
|
def explore_state_not_inaccessible(state, world, player):
|
||||||
|
while len(state.avail_doors) > 0:
|
||||||
|
door = state.next_avail_door().door
|
||||||
|
connect_region = world.get_entrance(door.name, player).connected_region
|
||||||
|
if state.can_traverse(door) and not state.visited(connect_region) and connect_region.type == RegionType.Dungeon:
|
||||||
|
state.visit_region(connect_region)
|
||||||
|
state.add_all_doors_check_unattached(connect_region, world, player)
|
||||||
|
|
||||||
|
|
||||||
def check_if_regions_visited(state, check_paths):
|
def check_if_regions_visited(state, check_paths):
|
||||||
valid = False
|
valid = False
|
||||||
breaking_region = None
|
breaking_region = None
|
||||||
|
|||||||
@@ -1,34 +1,78 @@
|
|||||||
# New Features
|
# New Features
|
||||||
|
|
||||||
* Lobby shuffle added as Intensity level 3
|
## Lobby shuffle added as Intensity level 3
|
||||||
* Can now be found in the spoiler
|
|
||||||
* Palette changes:
|
* Standard notes:
|
||||||
* Certain doors/transition no longer have an effect on the palette choice (dead ends mostly or just bridges)
|
* The sanctuary is vanilla, and will be missing the exit door until Zelda is rescued
|
||||||
* Sanctuary palette back to the adjacent rooms to Sanctuary (sanctuary stays the dungeon color for now)
|
* In entrance shuffle the hyrule castle left and right exit door will be missing until Zelda is rescued. This
|
||||||
* Sewer palette comes back for part of Hyrule Castle for areas "near" the sewer dropdown
|
replaces the rails that used to block those lobby exits
|
||||||
* Known issues:
|
* In non-entrance shuffle, Agahnims tower can be in logic if you have cape and/or Master sword, but you are never
|
||||||
* Palettes aren't perfect
|
required to beat Agahnim 1 until Zelda is rescued.
|
||||||
May add a way to turn off palette "fixing"
|
* Open notes:
|
||||||
* Some ugly colors
|
* The Sanctuary is limited to be in a LW dungeon unless you have ER Crossed or higher enabled
|
||||||
* Invisible floors can be see in many palettes
|
* Mirroring from the Sanctuary to the new "Sanctuary" lobby is now in logic, as is exiting there.
|
||||||
* --keydropshuffle added (coming to the GUI soon). This add 33 new locations to the game where keys are found under pots
|
* In ER crossed or higher, if the Sanctuary is in the Dark World, Link starts as Bunny there until the Moon Pearl
|
||||||
|
is found. Nothing inside that dungeon is in logic until the Moon Pearl is found. (Unless it is a multi-entrance
|
||||||
|
dungeon that you can access from some LW entrance)
|
||||||
|
* Lobby list is found in the spoiler
|
||||||
|
* Exits for Multi-entrance dungeons after beating bosses now makes more sense. Generally you'll exit from a entrance
|
||||||
|
from which the boss can logically be reached. If there are multiple, ones that do not lead to regions only accessible
|
||||||
|
by connector are preferred. The exit is randomly chosen if there's no obvious preference. However, In certain poor
|
||||||
|
cases like Skull Woods in ER, sometimes an exit is chosen not because you can reach the boss from there, but to
|
||||||
|
prevent a potential forced S&Q.
|
||||||
|
* Palette changes:
|
||||||
|
* Certain doors/transition no longer have an effect on the palette choice (dead ends mostly or just bridges)
|
||||||
|
* Sanctuary palette used on the adjacent rooms to Sanctuary (Sanctuary stays the dungeon color for now)
|
||||||
|
* Sewer palette comes back for part of Hyrule Castle for areas "near" the sewer dropdown
|
||||||
|
* There is a setting to keep original palettes (--standardize_palettes original)
|
||||||
|
* Known issues:
|
||||||
|
* Palettes aren't perfect
|
||||||
|
* Some ugly colors
|
||||||
|
* Invisible floors can be see in many palettes
|
||||||
|
|
||||||
|
## Key Drop Shuffle
|
||||||
|
|
||||||
|
--keydropshuffle added. This add 33 new locations to the game where keys are found under pots
|
||||||
and where enemies drop keys. This includes 32 small key location and the ball and chain guard who normally drop the HC
|
and where enemies drop keys. This includes 32 small key location and the ball and chain guard who normally drop the HC
|
||||||
Big Key.
|
Big Key.
|
||||||
* Overall location count updated
|
|
||||||
* Setting mentioned in spoiler
|
* Overall location count updated
|
||||||
* Known issue:
|
* Setting mentioned in spoiler
|
||||||
* GT Big Key count needs to be updated
|
* Minor change: if a key is Universal or for that dungeon, then if will use the old mechanics of picking up the key without
|
||||||
* --mixed_travel setting added
|
an entire pose and should be obtainable with the hookshot or boomerang as before
|
||||||
* Due to Hammerjump, Hovering in PoD Arena, and the Mire Big Key Chest bomb jump two sections of a supertile that are
|
|
||||||
|
## --mixed_travel setting
|
||||||
|
* Due to Hammerjump, Hovering in PoD Arena, and the Mire Big Key Chest bomb jump two sections of a supertile that are
|
||||||
otherwise unconnected logically can be reach using these glitches. To prevent the player from unintentionally
|
otherwise unconnected logically can be reach using these glitches. To prevent the player from unintentionally
|
||||||
* prevent: Rails are added the 3 spots to prevent this tricks. This setting is recommend for those learning
|
* prevent: Rails are added the 3 spots to prevent this tricks. This setting is recommend for those learning
|
||||||
crossed dungeon mode to learn what is dangerous and what is not. No logic seeds ignore this setting.
|
crossed dungeon mode to learn what is dangerous and what is not. No logic seeds ignore this setting.
|
||||||
* allow: The rooms are left alone and it is up to the discretion of the player whether to use these tricks or not.
|
* allow: The rooms are left alone and it is up to the discretion of the player whether to use these tricks or not.
|
||||||
* force: The two disjointed sections are forced to be in the same dungeon but never logically required to complete that game.
|
* force: The two disjointed sections are forced to be in the same dungeon but never logically required to complete that game.
|
||||||
|
|
||||||
|
## Keysanity menu redesign
|
||||||
|
|
||||||
|
Redesign of Keysanity Menu complete for crossed dungeon and moved out of experimental.
|
||||||
|
* First screen about Big Keys and Small Keys
|
||||||
|
* 1st Column: The map is required for information about the Big Key
|
||||||
|
* If you don't have the map, it'll be blank until you obtain the Big Key
|
||||||
|
* If have the map:
|
||||||
|
* 0 indicates there is no Big Key for that dungeon
|
||||||
|
* A red symbol indicates the Ball N Chain guard has the big key for that dungeon (does not apply in
|
||||||
|
--keydropshuffle)
|
||||||
|
* Blank if there a big key but you haven't found it yet
|
||||||
|
* 2nd Column displays the current number of keys for that dungeon. Suppressed in retro (always blank)
|
||||||
|
* 3rd Column only display if you have the map. It shows the number of keys left to collect for that dungeon. If
|
||||||
|
--keydropshuffle is off, this does not count key drops. If on, it does.
|
||||||
|
* (Note: the key columns can display up to 36 using the letters A-Z after 9)
|
||||||
|
* Second screen about Maps / Compass
|
||||||
|
* 1st Column: indicate if you have foudn the map of not for that dungeon
|
||||||
|
* 2nd and 3rd Column: You must have the compass to see these columns. A two-digit display that show you how
|
||||||
|
many chests are left in the dungeon. If -keydropshuffle is off, this does not count key drop. If on, it does.
|
||||||
|
|
||||||
### Experimental features
|
### Experimental features
|
||||||
|
|
||||||
* Redesign of Keysanity Menu for Crossed Dungeon - soon to move out of experimental
|
* Only the random bomb doors and the item counter are currently experimental
|
||||||
|
* Item counter is suppressed in Triforce Hunt
|
||||||
|
|
||||||
#### Temporary debug features
|
#### Temporary debug features
|
||||||
|
|
||||||
@@ -37,12 +81,12 @@ otherwise unconnected logically can be reach using these glitches. To prevent th
|
|||||||
# Bug Fixes
|
# Bug Fixes
|
||||||
|
|
||||||
* 2.0.12u
|
* 2.0.12u
|
||||||
* Option to keep original palettes in crossed dungeon mode
|
|
||||||
* If sanc if in a DW dungeon because of crossed+ ER, then you start in bunny form
|
|
||||||
* Mirroring from sanc to the portal is now in logic
|
|
||||||
* Another fix for animated tiles (fairy fountains)
|
* Another fix for animated tiles (fairy fountains)
|
||||||
* GT Big Key stat fixed on credits
|
* GT Big Key stat fixed on credits
|
||||||
* Todo: Standard logic fixes for lobbies
|
* Any denomination of rupee 20 or below can be removed to make room for Crossed Dungeon's extra dungeon items. This
|
||||||
|
helps retro generate more often.
|
||||||
|
* Fix for TR Lobbies in intensity 3 and ER shuffles that was causing a hardlock
|
||||||
|
* Standard ER logic revised for lobby shuffle and rain state considerations.
|
||||||
* 2.0.11u
|
* 2.0.11u
|
||||||
* Fix output path setting in settings.json
|
* Fix output path setting in settings.json
|
||||||
* Fix trock entrances when intensity <= 2
|
* Fix trock entrances when intensity <= 2
|
||||||
|
|||||||
39
Rom.py
39
Rom.py
@@ -26,7 +26,7 @@ from EntranceShuffle import door_addresses, exit_ids
|
|||||||
|
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = 'f6be3fdaac906a2217e7ee328e27b95b'
|
RANDOMIZERBASEHASH = '87fb1ec80d48487a84eac3a0a9bf9e04'
|
||||||
|
|
||||||
|
|
||||||
class JsonRom(object):
|
class JsonRom(object):
|
||||||
@@ -648,16 +648,17 @@ def patch_rom(world, rom, player, team, enemized):
|
|||||||
for name, layout in world.key_layout[player].items():
|
for name, layout in world.key_layout[player].items():
|
||||||
offset = compass_data[name][4]//2
|
offset = compass_data[name][4]//2
|
||||||
if world.retro[player]:
|
if world.retro[player]:
|
||||||
rom.write_byte(0x13f02a+offset, layout.max_chests + layout.max_drops)
|
rom.write_byte(0x13f030+offset, layout.max_chests + layout.max_drops)
|
||||||
else:
|
else:
|
||||||
rom.write_byte(0x13f01c+offset, layout.max_chests + layout.max_drops) # not currently used
|
rom.write_byte(0x13f020+offset, layout.max_chests + layout.max_drops) # not currently used
|
||||||
rom.write_byte(0x13f02a+offset, layout.max_chests)
|
rom.write_byte(0x13f030+offset, layout.max_chests)
|
||||||
builder = world.dungeon_layouts[player][name]
|
builder = world.dungeon_layouts[player][name]
|
||||||
rom.write_byte(0x13f070+offset, builder.location_cnt % 10)
|
rom.write_byte(0x13f080+offset, builder.location_cnt % 10)
|
||||||
rom.write_byte(0x13f07e+offset, builder.location_cnt // 10)
|
rom.write_byte(0x13f090+offset, builder.location_cnt // 10)
|
||||||
|
rom.write_byte(0x13f0a0+offset, builder.location_cnt)
|
||||||
bk_status = 1 if builder.bk_required else 0
|
bk_status = 1 if builder.bk_required else 0
|
||||||
bk_status = 2 if builder.bk_provided else bk_status
|
bk_status = 2 if builder.bk_provided else bk_status
|
||||||
rom.write_byte(0x13f038+offset*2, bk_status)
|
rom.write_byte(0x13f040+offset*2, bk_status)
|
||||||
if player in world.sanc_portal.keys():
|
if player in world.sanc_portal.keys():
|
||||||
rom.write_byte(0x159a6, world.sanc_portal[player].ent_offset)
|
rom.write_byte(0x159a6, world.sanc_portal[player].ent_offset)
|
||||||
sanc_region = world.sanc_portal[player].door.entrance.parent_region
|
sanc_region = world.sanc_portal[player].door.entrance.parent_region
|
||||||
@@ -1282,7 +1283,21 @@ def patch_rom(world, rom, player, team, enemized):
|
|||||||
rom.write_byte(0x18005F, world.crystals_needed_for_ganon[player])
|
rom.write_byte(0x18005F, world.crystals_needed_for_ganon[player])
|
||||||
|
|
||||||
# block HC upstairs doors in rain state in standard mode
|
# block HC upstairs doors in rain state in standard mode
|
||||||
rom.write_byte(0x18008A, 0x01 if world.mode[player] == "standard" and world.shuffle[player] != 'vanilla' else 0x00)
|
prevent_rain = world.mode[player] == "standard" and world.shuffle[player] != 'vanilla'
|
||||||
|
rom.write_byte(0x18008A, 0x01 if prevent_rain else 0x00)
|
||||||
|
# block sanc door in rain state and the dungeon is not vanilla
|
||||||
|
rom.write_byte(0x13f0fa, 0x01 if world.mode[player] == "standard" and world.doorShuffle[player] != 'vanilla' else 0x00)
|
||||||
|
|
||||||
|
if prevent_rain:
|
||||||
|
portals = [world.get_portal('Hyrule Castle East', player), world.get_portal('Hyrule Castle West', player)]
|
||||||
|
for idx, portal in enumerate(portals):
|
||||||
|
x = idx*2
|
||||||
|
room_idx = portal.door.roomIndex
|
||||||
|
room = world.get_room(room_idx, player)
|
||||||
|
rom.write_byte(0x13f0f0+x, room_idx & 0xff)
|
||||||
|
rom.write_byte(0x13f0f1+x, (room_idx >> 8) & 0xff)
|
||||||
|
rom.write_byte(0x13f0f6+x, room.position(portal.door).value)
|
||||||
|
rom.write_byte(0x13f0f7+x, room.kind(portal.door).value)
|
||||||
|
|
||||||
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.keyshuffle[player] else 0x00)
|
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.keyshuffle[player] else 0x00)
|
||||||
| (0x02 if world.compassshuffle[player] else 0x00)
|
| (0x02 if world.compassshuffle[player] else 0x00)
|
||||||
@@ -1415,9 +1430,11 @@ def patch_rom(world, rom, player, team, enemized):
|
|||||||
|
|
||||||
# fix trock doors for reverse entrances
|
# fix trock doors for reverse entrances
|
||||||
if world.fix_trock_doors[player]:
|
if world.fix_trock_doors[player]:
|
||||||
# do this unconditionally
|
if world.get_door('TR Lazy Eyes SE', player).entranceFlag:
|
||||||
world.get_room(0x23, player).change(0, DoorKind.CaveEntrance)
|
world.get_room(0x23, player).change(0, DoorKind.CaveEntrance)
|
||||||
world.get_room(0xd5, player).change(0, DoorKind.CaveEntrance)
|
if world.get_door('TR Eye Bridge SW', player).entranceFlag:
|
||||||
|
world.get_room(0xd5, player).change(0, DoorKind.CaveEntrance)
|
||||||
|
# do this unconditionally - gets overwritten by RoomData in doorShufflemodes
|
||||||
rom.write_byte(0xFED31, 0x0E) # preopen bombable exit
|
rom.write_byte(0xFED31, 0x0E) # preopen bombable exit
|
||||||
rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit
|
rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit
|
||||||
|
|
||||||
|
|||||||
@@ -263,6 +263,9 @@ class Room(object):
|
|||||||
self.modified = False
|
self.modified = False
|
||||||
self.palette = None
|
self.palette = None
|
||||||
|
|
||||||
|
def position(self, door):
|
||||||
|
return self.doorList[door.doorListPos][0]
|
||||||
|
|
||||||
def kind(self, door):
|
def kind(self, door):
|
||||||
return self.doorList[door.doorListPos][1]
|
return self.doorList[door.doorListPos][1]
|
||||||
|
|
||||||
|
|||||||
11
Rules.py
11
Rules.py
@@ -832,10 +832,13 @@ def standard_rules(world, player):
|
|||||||
set_rule(world.get_entrance('Sanctuary S&Q', player), lambda state: state.can_reach('Sanctuary', 'Region', player))
|
set_rule(world.get_entrance('Sanctuary S&Q', player), lambda state: state.can_reach('Sanctuary', 'Region', player))
|
||||||
# these are because of rails
|
# these are because of rails
|
||||||
if world.shuffle[player] != 'vanilla':
|
if world.shuffle[player] != 'vanilla':
|
||||||
# todo:
|
# where ever these happen to be
|
||||||
set_rule(world.get_entrance('Hyrule Castle Exit (East)', player), lambda state: state.has('Zelda Delivered', player))
|
for portal_name in ['Hyrule Castle East', 'Hyrule Castle West']:
|
||||||
set_rule(world.get_entrance('Hyrule Castle Exit (West)', player), lambda state: state.has('Zelda Delivered', player))
|
entrance = world.get_portal(portal_name, player).door.entrance
|
||||||
set_rule(world.get_entrance('Sanctuary Exit', player), lambda state: state.has('Zelda Delivered', player))
|
set_rule(entrance, lambda state: state.has('Zelda Delivered', player))
|
||||||
|
set_rule(world.get_entrance('Sanctuary Exit', player), lambda state: state.has('Zelda Delivered', player))
|
||||||
|
# zelda should be saved before agahnim is in play
|
||||||
|
set_rule(world.get_location('Agahnim 1', player), lambda state: state.has('Zelda Delivered', player))
|
||||||
|
|
||||||
# too restrictive for crossed?
|
# too restrictive for crossed?
|
||||||
def uncle_item_rule(item):
|
def uncle_item_rule(item):
|
||||||
|
|||||||
@@ -562,23 +562,26 @@ db $01, $02, $03, $04, $05, $06, $0a, $14
|
|||||||
; HC HC EP DP AT SP PD MM SW IP TH TT TR GT
|
; HC HC EP DP AT SP PD MM SW IP TH TT TR GT
|
||||||
org $27f000
|
org $27f000
|
||||||
CompassBossIndicator:
|
CompassBossIndicator:
|
||||||
dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
|
dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
|
||||||
TotalKeys: ;27f01c
|
TotalKeys: ;27f020
|
||||||
db $04, $04, $02, $04, $04, $06, $06, $06, $05, $06, $01, $03, $06, $08
|
db $04, $04, $02, $04, $04, $06, $06, $06, $05, $06, $01, $03, $06, $08, $00, $00
|
||||||
ChestKeys: ;27f02a
|
ChestKeys: ;27f030
|
||||||
db $01, $01, $00, $01, $02, $01, $06, $03, $03, $02, $01, $01, $04, $04
|
db $01, $01, $00, $01, $02, $01, $06, $03, $03, $02, $01, $01, $04, $04, $00, $00
|
||||||
BigKeyStatus: ;27f038 (status 2 indicate BnC guard)
|
BigKeyStatus: ;27f040 (status 2 indicate BnC guard)
|
||||||
dw $0002, $0002, $0001, $0001, $0000, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001
|
dw $0002, $0002, $0001, $0001, $0000, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0001, $0000, $0000
|
||||||
DungeonReminderTable: ;27f054
|
DungeonReminderTable: ;27f060
|
||||||
dw $2D50, $2D50, $2D51, $2D52, $2D54, $2D56, $2D55, $2D5A, $2D57, $2D59, $2D53, $2D58, $2D5B, $2D5C
|
dw $2D50, $2D50, $2D51, $2D52, $2D54, $2D56, $2D55, $2D5A, $2D57, $2D59, $2D53, $2D58, $2D5B, $2D5C, $0000, $0000
|
||||||
TotalLocationsLow: ;27f070
|
TotalLocationsLow: ;27f080
|
||||||
db $08, $08, $06, $06, $02, $00, $04, $08, $08, $08, $06, $08, $02, $07
|
db $08, $08, $06, $06, $02, $00, $04, $08, $08, $08, $06, $08, $02, $07, $00, $00
|
||||||
TotalLocationsHigh: ;27f07e
|
TotalLocationsHigh: ;27f090
|
||||||
db $00, $00, $00, $00, $00, $01, $01, $00, $00, $00, $00, $00, $01, $02
|
db $00, $00, $00, $00, $00, $01, $01, $00, $00, $00, $00, $00, $01, $02, $00, $00
|
||||||
;27F08C
|
org $27f0a0
|
||||||
|
TotalLocations:
|
||||||
|
db $08, $08, $06, $06, $02, $0a, $0e, $08, $08, $08, $06, $08, $0c, $1b, $00, $00
|
||||||
|
; no more room here
|
||||||
|
|
||||||
; Vert 0,6,0 Horz 2,0,8
|
; Vert 0,6,0 Horz 2,0,8
|
||||||
org $27f090
|
org $27f0b0
|
||||||
CoordIndex: ; Horizontal 1st
|
CoordIndex: ; Horizontal 1st
|
||||||
db 2, 0 ; Coordinate Index $20-$23
|
db 2, 0 ; Coordinate Index $20-$23
|
||||||
OppCoordIndex:
|
OppCoordIndex:
|
||||||
@@ -598,7 +601,16 @@ dw $007f, $0077 ; Left/Top camera bounds when at edge or layout frozen
|
|||||||
dw $0007, $000b ; Left/Top camera bounds when not frozen + appropriate low byte $22/$20 (preadj. by #$78/#$6c)
|
dw $0007, $000b ; Left/Top camera bounds when not frozen + appropriate low byte $22/$20 (preadj. by #$78/#$6c)
|
||||||
dw $00ff, $010b ; Right/Bot camera bounds when not frozen + appropriate low byte $20/$22
|
dw $00ff, $010b ; Right/Bot camera bounds when not frozen + appropriate low byte $20/$22
|
||||||
dw $017f, $0187 ; Right/Bot camera bound when at edge or layout frozen
|
dw $017f, $0187 ; Right/Bot camera bound when at edge or layout frozen
|
||||||
;27f0ae next free byte
|
;27f0ce next free byte
|
||||||
|
|
||||||
|
org $27f0f0
|
||||||
|
RemoveRainDoorsRoom:
|
||||||
|
dw $0060, $0062, $ffff ; ffff indicates end of list
|
||||||
|
RainDoorMatch: ; org $27f0f6 and f8 for now
|
||||||
|
dw $0081, $0061 ; not xba'd
|
||||||
|
BlockSanctuaryDoorInRain: ;27f0fa
|
||||||
|
dw $0000
|
||||||
|
|
||||||
|
|
||||||
org $27f100
|
org $27f100
|
||||||
TilesetTable:
|
TilesetTable:
|
||||||
|
|||||||
@@ -156,6 +156,9 @@ JSL RetrieveBunnyState : NOP
|
|||||||
org $02d9ce ; <- Bank02.asm : Dungeon_LoadEntrance 10829 (STA $A0 : STA $048E)
|
org $02d9ce ; <- Bank02.asm : Dungeon_LoadEntrance 10829 (STA $A0 : STA $048E)
|
||||||
JSL CheckDarkWorldSanc : NOP
|
JSL CheckDarkWorldSanc : NOP
|
||||||
|
|
||||||
|
org $01891e ; <- Bank 01.asm : 991 Dungeon_LoadType2Object (LDA $00 : XBA : AND.w #$00FF)
|
||||||
|
JSL RainPrevention : NOP #2
|
||||||
|
|
||||||
; These two, if enabled together, have implications for vanilla BK doors in IP/Hera/Mire
|
; These two, if enabled together, have implications for vanilla BK doors in IP/Hera/Mire
|
||||||
; IPBJ is common enough to consider not doing this. Mire is not a concern for vanilla - maybe glitched modes
|
; IPBJ is common enough to consider not doing this. Mire is not a concern for vanilla - maybe glitched modes
|
||||||
; Hera BK door back can be seen with Pot clipping - likely useful for no logic seeds
|
; Hera BK door back can be seen with Pot clipping - likely useful for no logic seeds
|
||||||
|
|||||||
@@ -95,12 +95,18 @@ DrHudDungeonItemsAdditions:
|
|||||||
+ stx $00
|
+ stx $00
|
||||||
txa : lsr : tax
|
txa : lsr : tax
|
||||||
lda.w #$24f5 : sta $1644, y
|
lda.w #$24f5 : sta $1644, y
|
||||||
lda.l $7ef37c, x : beq +
|
lda.l GenericKeys : bne +
|
||||||
|
lda.l $7ef37c, x : and #$00FF : beq +
|
||||||
jsr ConvertToDisplay2 : sta $1644, y
|
jsr ConvertToDisplay2 : sta $1644, y
|
||||||
+ iny #2 : lda.w #$24f5 : sta $1644, y
|
+ iny #2 : lda.w #$24f5 : sta $1644, y
|
||||||
phx : ldx $00
|
phx : ldx $00
|
||||||
lda $7ef368 : and.l $0098c0, x : beq + ; must have map
|
lda $7ef368 : and.l $0098c0, x : beq + ; must have map
|
||||||
plx : lda.l ChestKeys, x : jsr ConvertToDisplay2 : sta $1644, y ; small key totals
|
plx : sep #$30 : lda.l ChestKeys, x : sta $02
|
||||||
|
lda.l GenericKeys : bne +++
|
||||||
|
lda $02 : !sub $7ef4e0, x : sta $02
|
||||||
|
+++ lda $02
|
||||||
|
rep #$30
|
||||||
|
jsr ConvertToDisplay2 : sta $1644, y ; small key totals
|
||||||
bra .skipStack
|
bra .skipStack
|
||||||
+ plx
|
+ plx
|
||||||
.skipStack iny #2
|
.skipStack iny #2
|
||||||
@@ -131,15 +137,18 @@ DrHudDungeonItemsAdditions:
|
|||||||
+ lda $7ef364 : and.l $0098c0, x : beq + ; must have compass
|
+ lda $7ef364 : and.l $0098c0, x : beq + ; must have compass
|
||||||
phx ; total chest counts
|
phx ; total chest counts
|
||||||
txa : lsr : tax
|
txa : lsr : tax
|
||||||
lda.l TotalLocationsHigh, x : jsr ConvertToDisplay2 : sta $1644, y : iny #2
|
sep #$30
|
||||||
lda.l TotalLocationsLow, x : jsr ConvertToDisplay2 : sta $1644, y
|
lda.l TotalLocations, x : !sub $7EF4BF, x : JSR HudHexToDec2DigitCopy
|
||||||
|
rep #$30
|
||||||
|
lda $06 : jsr ConvertToDisplay2 : sta $1644, y : iny #2
|
||||||
|
lda $07 : jsr ConvertToDisplay2 : sta $1644, y
|
||||||
plx
|
plx
|
||||||
bra .skipBlanks
|
bra .skipBlanks
|
||||||
+ lda.w #$24f5 : sta $1644, y : iny #2 : sta $1644, y
|
+ lda.w #$24f5 : sta $1644, y : iny #2 : sta $1644, y
|
||||||
.skipBlanks iny #2
|
.skipBlanks iny #2
|
||||||
cpx #$001a : beq +
|
cpx #$001a : beq +
|
||||||
lda.w #$24f5 : sta $1644, y ; blank out spot
|
lda.w #$24f5 : sta $1644, y ; blank out spot
|
||||||
+ inx #2 : cpx #$001b : bcc -
|
+ inx #2 : cpx #$001b : !bge ++ : brl -
|
||||||
++
|
++
|
||||||
plp : ply : plx : rtl
|
plp : ply : plx : rtl
|
||||||
}
|
}
|
||||||
@@ -203,4 +212,27 @@ HudHexToDec4DigitCopy:
|
|||||||
DEC : BNE -
|
DEC : BNE -
|
||||||
+
|
+
|
||||||
STY $07 ; Store 1s digit
|
STY $07 ; Store 1s digit
|
||||||
|
RTS
|
||||||
|
|
||||||
|
;================================================================================
|
||||||
|
; 8-bit registers
|
||||||
|
; in: A(b) - Byte to Convert
|
||||||
|
; out: $06 - $07 (high - low)
|
||||||
|
;================================================================================
|
||||||
|
HudHexToDec2DigitCopy: ; modified
|
||||||
|
PHY
|
||||||
|
LDY.b #$00
|
||||||
|
-
|
||||||
|
CMP.b #10 : !BLT +
|
||||||
|
INY
|
||||||
|
SBC.b #10 : BRA -
|
||||||
|
+
|
||||||
|
STY $06 : LDY #$00 ; Store 10s digit and reset Y
|
||||||
|
CMP.b #1 : !BLT +
|
||||||
|
-
|
||||||
|
INY
|
||||||
|
DEC : BNE -
|
||||||
|
+
|
||||||
|
STY $07 ; Store 1s digit
|
||||||
|
PLY
|
||||||
RTS
|
RTS
|
||||||
@@ -127,7 +127,7 @@ KeyGet:
|
|||||||
phx
|
phx
|
||||||
lda $040c : lsr : tax
|
lda $040c : lsr : tax
|
||||||
lda $00 : cmp KeyTable, x : bne +
|
lda $00 : cmp KeyTable, x : bne +
|
||||||
- plx : pla : rtl
|
- JSL.l FullInventoryExternal : jsl CountChestKeyLong : plx : pla : rtl
|
||||||
+ cmp #$af : beq - ; universal key
|
+ cmp #$af : beq - ; universal key
|
||||||
cmp #$24 : beq - ; small key for this dungeon
|
cmp #$24 : beq - ; small key for this dungeon
|
||||||
plx
|
plx
|
||||||
|
|||||||
@@ -121,3 +121,22 @@ RetrieveBunnyState:
|
|||||||
LDA $5F : BEQ +
|
LDA $5F : BEQ +
|
||||||
STA $5D
|
STA $5D
|
||||||
+ RTL
|
+ RTL
|
||||||
|
|
||||||
|
RainPrevention:
|
||||||
|
LDA $00 : XBA : AND #$00FF ; what we wrote over
|
||||||
|
PHA
|
||||||
|
LDA $7EF3C5 : AND #$00FF : CMP #$0002 : !BGE .done ; only in rain states (0 or 1)
|
||||||
|
LDA.l $7EF3C6 : AND #$0004 : BNE .done ; zelda's been rescued
|
||||||
|
LDA.l BlockSanctuaryDoorInRain : BEQ .done ;flagged
|
||||||
|
LDA $A0 : CMP #$0012 : BNE + ;we're in the sanctuary
|
||||||
|
LDA.l $7EF3CC : AND #$00FF : CMP #$0001 : BEQ .done ; zelda is following
|
||||||
|
LDA $00 : CMP #$02A1 : BNE .done
|
||||||
|
PLA : LDA #$0008 : RTL
|
||||||
|
+ LDA.l BlockCastleDoorsInRain : BEQ .done ;flagged
|
||||||
|
LDX #$FFFE
|
||||||
|
- INX #2 : LDA.l RemoveRainDoorsRoom, X : CMP #$FFFF : BEQ .done
|
||||||
|
CMP $A0 : BNE -
|
||||||
|
LDA.l RainDoorMatch, X : CMP $00 : BNE -
|
||||||
|
PLA : LDA #$0008 : RTL
|
||||||
|
.done PLA : RTL
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user