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

Various changes to precompleted dungeons #2330

Open
wants to merge 2 commits into
base: Dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 11 additions & 11 deletions Dungeon.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ def shuffle_dungeon_rewards(self) -> str:
return self.world.settings.shuffle_dungeon_rewards

@property
def empty(self) -> bool:
return self.world.empty_dungeons[self.name].empty
def precompleted(self) -> bool:
return self.world.precompleted_dungeons.get(self.name, False)

@property
def keys(self) -> list[Item]:
Expand Down Expand Up @@ -112,28 +112,28 @@ def is_dungeon_item(self, item: Item) -> bool:
return item.name in [dungeon_item.name for dungeon_item in self.all_items]

def get_restricted_dungeon_items(self) -> Iterator[Item]:
if self.shuffle_mapcompass == 'dungeon' or (self.empty and self.shuffle_mapcompass in ['any_dungeon', 'overworld', 'keysanity', 'regional']):
if self.shuffle_mapcompass == 'dungeon' or (self.precompleted and self.shuffle_mapcompass in ('any_dungeon', 'overworld', 'keysanity', 'regional')):
yield from self.dungeon_items
if self.shuffle_smallkeys == 'dungeon' or (self.empty and self.shuffle_smallkeys in ['any_dungeon', 'overworld', 'keysanity', 'regional']):
if self.shuffle_smallkeys == 'dungeon' or (self.precompleted and self.shuffle_smallkeys in ('any_dungeon', 'overworld', 'keysanity', 'regional')):
yield from self.small_keys
if self.shuffle_bosskeys == 'dungeon' or (self.empty and self.shuffle_bosskeys in ['any_dungeon', 'overworld', 'keysanity', 'regional']):
if self.shuffle_bosskeys == 'dungeon' or (self.precompleted and self.shuffle_bosskeys in ('any_dungeon', 'overworld', 'keysanity', 'regional')):
yield from self.boss_key
if self.shuffle_silver_rupees == 'dungeon' or (self.empty and self.shuffle_silver_rupees in ['any_dungeon', 'overworld', 'anywhere', 'regional']):
if self.shuffle_silver_rupees == 'dungeon' or (self.precompleted and self.shuffle_silver_rupees in ('any_dungeon', 'overworld', 'anywhere', 'regional')):
yield from self.silver_rupees
if self.shuffle_dungeon_rewards in ('vanilla', 'dungeon'): # we don't lock rewards inside pre-completed dungeons since they're still useful outside
yield from self.reward

# get a list of items that don't have to be in their proper dungeon
def get_unrestricted_dungeon_items(self) -> Iterator[Item]:
if self.empty:
if self.precompleted:
return
if self.shuffle_mapcompass in ['any_dungeon', 'overworld', 'keysanity', 'regional']:
if self.shuffle_mapcompass in ('any_dungeon', 'overworld', 'keysanity', 'regional'):
yield from self.dungeon_items
if self.shuffle_smallkeys in ['any_dungeon', 'overworld', 'keysanity', 'regional']:
if self.shuffle_smallkeys in ('any_dungeon', 'overworld', 'keysanity', 'regional'):
yield from self.small_keys
if self.shuffle_bosskeys in ['any_dungeon', 'overworld', 'keysanity', 'regional']:
if self.shuffle_bosskeys in ('any_dungeon', 'overworld', 'keysanity', 'regional'):
yield from self.boss_key
if self.shuffle_silver_rupees in ['any_dungeon', 'overworld', 'anywhere', 'regional']:
if self.shuffle_silver_rupees in ('any_dungeon', 'overworld', 'anywhere', 'regional'):
yield from self.silver_rupees
if self.shuffle_dungeon_rewards in ('any_dungeon', 'overworld', 'anywhere', 'regional'):
yield from self.reward
Expand Down
9 changes: 6 additions & 3 deletions Fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,11 @@ def distribute_items_restrictive(worlds: list[World], fill_locations: Optional[l

# If some dungeons are supposed to be empty, fill them with useless items.
if worlds[0].settings.empty_dungeons_mode != 'none':
empty_locations = [location for location in fill_locations
if location.world.empty_dungeons[HintArea.at(location).dungeon_name].empty]
empty_locations = [
location
for location in fill_locations
if location.world.precompleted_dungeons.get(HintArea.at(location).dungeon_name, False)
]
for location in empty_locations:
fill_locations.remove(location)

Expand Down Expand Up @@ -268,7 +271,7 @@ def fill_dungeon_unique_item(worlds: list[World], search: Search, fill_locations
minor_items = [item for item in itempool if not item.majoritem]

if worlds[0].settings.empty_dungeons_mode != 'none':
dungeons = [dungeon for world in worlds for dungeon in world.dungeons if not world.empty_dungeons[dungeon.name].empty]
dungeons = [dungeon for world in worlds for dungeon in world.dungeons if not world.precompleted_dungeons.get(dungeon.name, False)]
else:
dungeons = [dungeon for world in worlds for dungeon in world.dungeons]

Expand Down
113 changes: 57 additions & 56 deletions Hints.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,7 @@ def get_barren_hint(spoiler: Spoiler, world: World, checked: set[str], all_check
areas = list(filter(lambda area:
area not in checked_areas
and str(area) not in world.hint_type_overrides['barren']
and not world.precompleted_dungeons.get(area.dungeon_name, False)
and not (world.barren_dungeon >= world.hint_dist_user['dungeons_barren_limit'] and world.empty_areas[area]['dungeon'])
and any(
location.name not in all_checked
Expand Down Expand Up @@ -766,13 +767,22 @@ def get_barren_hint(spoiler: Spoiler, world: World, checked: set[str], all_check
return GossipText("plundering %s is a foolish choice." % area.text(world.settings.clearer_hints), ['Pink']), None


def is_not_checked(locations: Iterable[Location], checked: set[HintArea | str]) -> bool:
return not any(location.name in checked or HintArea.at(location) in checked for location in locations)
def is_checked(locations: Iterable[Location], checked: set[HintArea | str]) -> bool:
for location in locations:
if location.name in checked:
return True
hint_area = HintArea.at(location)
if hint_area in checked:
return True
if location.world.precompleted_dungeons.get(hint_area.dungeon_name, False):
# don't hint locations in precompleted dungeons
return True
return False


def get_good_item_hint(spoiler: Spoiler, world: World, checked: set[str]) -> HintReturn:
locations = list(filter(lambda location:
is_not_checked([location], checked)
not is_checked([location], checked)
and ((location.item.majoritem
and location.item.name not in unHintableWothItems)
or location.name in world.added_hint_types['item']
Expand Down Expand Up @@ -809,22 +819,20 @@ def get_specific_item_hint(spoiler: Spoiler, world: World, checked: set[str]) ->
if itemname == "Bottle" and world.settings.hint_dist == "bingo":
locations = [
location for location in world.get_filled_locations()
if (is_not_checked([location], checked)
and location.name not in world.hint_exclusions
and location.item.name in bingoBottlesForHints
and not location.locked
and location.name not in world.hint_type_overrides['named-item']
)
if not is_checked([location], checked)
and location.name not in world.hint_exclusions
and location.item.name in bingoBottlesForHints
and not location.locked
and location.name not in world.hint_type_overrides['named-item']
]
else:
locations = [
location for location in world.get_filled_locations()
if (is_not_checked([location], checked)
and location.name not in world.hint_exclusions
and location.item.name == itemname
and not location.locked
and location.name not in world.hint_type_overrides['named-item']
)
if not is_checked([location], checked)
and location.name not in world.hint_exclusions
and location.item.name == itemname
and not location.locked
and location.name not in world.hint_type_overrides['named-item']
]

if len(locations) > 0:
Expand Down Expand Up @@ -885,24 +893,24 @@ def get_specific_item_hint(spoiler: Spoiler, world: World, checked: set[str]) ->
if itemname == "Bottle" and world.settings.hint_dist == "bingo":
locations = [
location for location in named_item_locations
if (is_not_checked([location], checked)
and location.item.world.id == world.id
and location.name not in world.hint_exclusions
and location.item.name in bingoBottlesForHints
and not location.locked
and (itemname, world.id) not in always_locations
and location.name not in world.hint_type_overrides['named-item'])
if not is_checked([location], checked)
and location.item.world.id == world.id
and location.name not in world.hint_exclusions
and location.item.name in bingoBottlesForHints
and not location.locked
and (itemname, world.id) not in always_locations
and location.name not in world.hint_type_overrides['named-item']
]
else:
locations = [
location for location in named_item_locations
if (is_not_checked([location], checked)
and location.item.world.id == world.id
and location.name not in world.hint_exclusions
and location.item.name == itemname
and not location.locked
and (itemname, world.id) not in always_locations
and location.name not in world.hint_type_overrides['named-item'])
if not is_checked([location], checked)
and location.item.world.id == world.id
and location.name not in world.hint_exclusions
and location.item.name == itemname
and not location.locked
and (itemname, world.id) not in always_locations
and location.name not in world.hint_type_overrides['named-item']
]

if len(locations) > 0:
Expand Down Expand Up @@ -939,14 +947,13 @@ def get_specific_item_hint(spoiler: Spoiler, world: World, checked: set[str]) ->

def get_random_location_hint(spoiler: Spoiler, world: World, checked: set[str]) -> HintReturn:
locations = list(filter(lambda location:
is_not_checked([location], checked)
not is_checked([location], checked)
and location.item.type not in ('Drop', 'Event', 'Shop')
and not is_restricted_dungeon_item(location.item)
and not location.locked
and location.name not in world.hint_exclusions
and location.name not in world.hint_type_overrides['item']
and location.item.name not in world.item_hint_type_overrides['item']
and (location.world.settings.empty_dungeons_mode == 'none' or not location.world.empty_dungeons[HintArea.at(location).dungeon_name].empty),
and location.item.name not in world.item_hint_type_overrides['item'],
world.get_filled_locations()))
if not locations:
return None
Expand All @@ -965,25 +972,19 @@ def get_random_location_hint(spoiler: Spoiler, world: World, checked: set[str])


def get_specific_hint(spoiler: Spoiler, world: World, checked: set[str], hint_type: str) -> HintReturn:
def is_valid_hint(hint: Hint) -> bool:
location = world.get_location(hint.name)
if not is_not_checked([world.get_location(hint.name)], checked):
return False
if location.world.settings.empty_dungeons_mode != 'none' and location.world.empty_dungeons[HintArea.at(location).dungeon_name].empty:
return False
return True

hint_group = get_hint_group(hint_type, world)
hint_group = list(filter(is_valid_hint, hint_group))
hint_group = list(filter(lambda hint: not is_checked([world.get_location(hint.name)], checked), hint_group))
if not hint_group:
return None

hint = random.choice(hint_group)

if world.hint_dist_user['upgrade_hints'] in ['on', 'limited']:
if world.hint_dist_user['upgrade_hints'] in ('on', 'limited'):
upgrade_list = get_upgrade_hint_list(world, [hint.name])
upgrade_list = list(filter(lambda upgrade: is_not_checked([world.get_location(location) for location in get_multi(
upgrade.name).locations], checked), upgrade_list))
upgrade_list = list(filter(
lambda upgrade: not is_checked([world.get_location(location) for location in get_multi(upgrade.name).locations], checked),
upgrade_list,
))

if upgrade_list is not None:
multi = None
Expand Down Expand Up @@ -1030,8 +1031,10 @@ def get_dungeon_hint(spoiler: Spoiler, world: World, checked: set[str]) -> HintR

def get_random_multi_hint(spoiler: Spoiler, world: World, checked: set[str], hint_type: str) -> HintReturn:
hint_group = get_hint_group(hint_type, world)
multi_hints = list(filter(lambda hint: is_not_checked([world.get_location(location) for location in get_multi(
hint.name).locations], checked), hint_group))
multi_hints = list(filter(
lambda hint: not is_checked([world.get_location(location) for location in get_multi(hint.name).locations], checked),
hint_group,
))

if not multi_hints:
return None
Expand All @@ -1042,8 +1045,10 @@ def get_random_multi_hint(spoiler: Spoiler, world: World, checked: set[str], hin
multi = get_multi(hint.name)

upgrade_list = get_upgrade_hint_list(world, multi.locations)
upgrade_list = list(filter(lambda upgrade: is_not_checked([world.get_location(location) for location in get_multi(
upgrade.name).locations], checked), upgrade_list))
upgrade_list = list(filter(
lambda upgrade: not is_checked([world.get_location(location) for location in get_multi(upgrade.name).locations], checked),
upgrade_list,
))

if upgrade_list:
for upgrade in upgrade_list:
Expand Down Expand Up @@ -1322,12 +1327,6 @@ def build_gossip_hints(spoiler: Spoiler, worlds: list[World]) -> None:
if item_world.id not in checked_locations:
checked_locations[item_world.id] = set()
checked_locations[item_world.id].add(location.name)
for dungeon_name, info in world.empty_dungeons.items():
if info.empty:
for region in world.regions:
if region.dungeon != None and region.dungeon.name == dungeon_name:
precompleted_locations = list(map(lambda location: location.name, region.locations))
checked_locations[world.id].update(precompleted_locations)

# Build all the hints.
for world in worlds:
Expand Down Expand Up @@ -1485,8 +1484,10 @@ def build_world_gossip_hints(spoiler: Spoiler, world: World, checked_locations:

# Add required location hints, only if hint copies > 0
if hint_dist['always'][1] > 0:
always_locations = list(filter(lambda hint: is_not_checked([world.get_location(hint.name)], checked_always_locations),
get_hint_group('always', world)))
always_locations = list(filter(
lambda hint: not is_checked([world.get_location(hint.name)], checked_always_locations),
get_hint_group('always', world),
))
for hint in always_locations:
location = world.get_location(hint.name)
checked_always_locations.add(hint.name)
Expand Down
8 changes: 5 additions & 3 deletions ItemPool.py
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,7 @@ def get_pool_core(world: World) -> tuple[list[str], dict[str, Item]]:
world.state.collect(ItemFactory(item, world))
item = get_junk_item()[0]
shuffle_item = True
elif shuffle_setting in ('any_dungeon', 'overworld', 'keysanity', 'regional', 'anywhere') and not world.empty_dungeons[dungeon.name].empty:
elif shuffle_setting in ('any_dungeon', 'overworld', 'keysanity', 'regional', 'anywhere') and not world.precompleted_dungeons.get(dungeon.name, False):
shuffle_item = True
elif shuffle_item is None:
dungeon_collection.append(ItemFactory(item, world))
Expand Down Expand Up @@ -926,8 +926,10 @@ def get_pool_core(world: World) -> tuple[list[str], dict[str, Item]]:
world.state.collect(ItemFactory('Small Key (Shadow Temple)', world))
world.state.collect(ItemFactory('Small Key (Shadow Temple)', world))

if (not world.keysanity or (world.empty_dungeons['Fire Temple'].empty and world.settings.shuffle_smallkeys != 'remove'))\
and not world.dungeon_mq['Fire Temple']:
if (
(not world.keysanity or (world.precompleted_dungeons['Fire Temple'] and world.settings.shuffle_smallkeys != 'remove'))
and not world.dungeon_mq['Fire Temple']
):
world.state.collect(ItemFactory('Small Key (Fire Temple)', world))

if world.settings.shuffle_ganon_bosskey == 'on_lacs':
Expand Down
8 changes: 5 additions & 3 deletions Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,16 @@ def build_world_graphs(settings: Settings) -> list[World]:
if world.settings.shuffle_dungeon_rewards in ('vanilla', 'reward'):
world.fill_bosses()

if settings.empty_dungeons_mode == 'rewards':
world.set_empty_dungeon_rewards(settings.empty_dungeons_rewards)

if settings.triforce_hunt:
settings.distribution.configure_triforce_hunt(worlds)

logger.info('Setting Entrances.')
set_entrances(worlds, savewarps_to_connect)

for world in worlds:
if world.settings.empty_dungeons_mode == 'rewards':
world.set_empty_dungeon_rewards(world.settings.empty_dungeons_rewards)

return worlds


Expand Down
4 changes: 2 additions & 2 deletions Patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -2793,8 +2793,8 @@ def configure_dungeon_info(rom: Rom, world: World) -> None:
if location.world.id == world.id and area.is_dungeon:
dungeon_rewards[codes.index(area.dungeon_name)] = boss_reward_index(location.item)

dungeon_is_mq = [1 if world.dungeon_mq.get(c) else 0 for c in codes]
dungeon_precompleted = [1 if world.empty_dungeons[c].empty else 0 for c in codes]
dungeon_is_mq = [int(world.dungeon_mq.get(c, False)) for c in codes]
dungeon_precompleted = [int(world.precompleted_dungeons.get(c, False)) for c in codes]

rom.write_int32(rom.sym('CFG_DUNGEON_INFO_ENABLE'), 2)
rom.write_int32(rom.sym('CFG_DUNGEON_INFO_MQ_ENABLE'), int(mq_enable))
Expand Down
Loading