Added support for various avianart modes

Uncle boots hints
Some key logic re-working unsure if safe
This commit is contained in:
aerinon
2022-11-28 15:05:39 -07:00
parent fa75d2b4e9
commit 1134eb23e7
21 changed files with 2198 additions and 66 deletions

View File

@@ -514,6 +514,7 @@ class CollectionState(object):
self.opened_doors = {player: set() for player in range(1, parent.players + 1)} self.opened_doors = {player: set() for player in range(1, parent.players + 1)}
self.dungeons_to_check = {player: defaultdict(dict) for player in range(1, parent.players + 1)} self.dungeons_to_check = {player: defaultdict(dict) for player in range(1, parent.players + 1)}
self.dungeon_limits = None self.dungeon_limits = None
self.placing_item = None
# self.trace = None # self.trace = None
def update_reachable_regions(self, player): def update_reachable_regions(self, player):
@@ -787,7 +788,8 @@ class CollectionState(object):
door_candidates.append(door.name) door_candidates.append(door.name)
return door_candidates return door_candidates
door_candidates, skip = [], set() door_candidates, skip = [], set()
if state.world.accessibility[player] != 'locations' and remaining_keys == 0 and dungeon_name != 'Universal': if (state.world.accessibility[player] != 'locations' and remaining_keys == 0 and dungeon_name != 'Universal'
and state.placing_item and state.placing_item.name == small_key_name):
key_logic = state.world.key_logic[player][dungeon_name] key_logic = state.world.key_logic[player][dungeon_name]
for door, paired in key_logic.sm_doors.items(): for door, paired in key_logic.sm_doors.items():
if door.name in key_logic.door_rules: if door.name in key_logic.door_rules:
@@ -830,6 +832,7 @@ class CollectionState(object):
player: defaultdict(dict, {name: copy.copy(checklist) player: defaultdict(dict, {name: copy.copy(checklist)
for name, checklist in self.dungeons_to_check[player].items()}) for name, checklist in self.dungeons_to_check[player].items()})
for player in range(1, self.world.players + 1)} for player in range(1, self.world.players + 1)}
ret.placing_item = self.placing_item
return ret return ret
def apply_dungeon_exploration(self, rrp, player, dungeon_name, checklist): def apply_dungeon_exploration(self, rrp, player, dungeon_name, checklist):

3
CLI.py
View File

@@ -124,7 +124,7 @@ def parse_cli(argv, no_defaults=False):
playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True) playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True)
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
'flute_mode', 'bow_mode', 'take_any', 'flute_mode', 'bow_mode', 'take_any', 'boots_hint',
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'usestartinventory', 'bombbag', 'overworld_map', 'restrict_boss_items', 'usestartinventory', 'bombbag', 'overworld_map', 'restrict_boss_items',
@@ -161,6 +161,7 @@ def parse_settings():
"retro": False, "retro": False,
"bombbag": False, "bombbag": False,
"mode": "open", "mode": "open",
"boots_hint": False,
"logic": "noglitches", "logic": "noglitches",
"goal": "ganon", "goal": "ganon",
"crystals_gt": "7", "crystals_gt": "7",

19
Fill.py
View File

@@ -24,7 +24,6 @@ def promote_dungeon_items(world):
item.advancement = True item.advancement = True
elif item.map or item.compass: elif item.map or item.compass:
item.priority = True item.priority = True
dungeon_tracking(world)
def dungeon_tracking(world): def dungeon_tracking(world):
@@ -35,7 +34,6 @@ def dungeon_tracking(world):
def fill_dungeons_restrictive(world, shuffled_locations): def fill_dungeons_restrictive(world, shuffled_locations):
dungeon_tracking(world)
# with shuffled dungeon items they are distributed as part of the normal item pool # with shuffled dungeon items they are distributed as part of the normal item pool
for item in world.get_items(): for item in world.get_items():
@@ -73,11 +71,13 @@ def fill_dungeons_restrictive(world, shuffled_locations):
def fill_restrictive(world, base_state, locations, itempool, key_pool=None, single_player_placement=False, def fill_restrictive(world, base_state, locations, itempool, key_pool=None, single_player_placement=False,
vanilla=False): vanilla=False):
def sweep_from_pool(): def sweep_from_pool(placing_item=None):
new_state = base_state.copy() new_state = base_state.copy()
for item in itempool: for item in itempool:
new_state.collect(item, True) new_state.collect(item, True)
new_state.placing_item = placing_item
new_state.sweep_for_events() new_state.sweep_for_events()
new_state.placing_item = None
return new_state return new_state
unplaced_items = [] unplaced_items = []
@@ -94,7 +94,7 @@ def fill_restrictive(world, base_state, locations, itempool, key_pool=None, sing
while any(player_items.values()) and locations: while any(player_items.values()) and locations:
items_to_place = [[itempool.remove(items[-1]), items.pop()][-1] for items in player_items.values() if items] items_to_place = [[itempool.remove(items[-1]), items.pop()][-1] for items in player_items.values() if items]
maximum_exploration_state = sweep_from_pool() maximum_exploration_state = sweep_from_pool(placing_item=items_to_place[0])
has_beaten_game = world.has_beaten_game(maximum_exploration_state) has_beaten_game = world.has_beaten_game(maximum_exploration_state)
for item_to_place in items_to_place: for item_to_place in items_to_place:
@@ -522,10 +522,17 @@ def fast_fill_helper(world, item_pool, fill_locations):
def fast_fill(world, item_pool, fill_locations): def fast_fill(world, item_pool, fill_locations):
while item_pool and fill_locations: config = world.item_pool_config
fast_pool = [x for x in item_pool if (x.name, x.player) not in config.restricted]
filtered_pool = [x for x in item_pool if (x.name, x.player) in config.restricted]
filtered_fill(world, filtered_pool, fill_locations)
while fast_pool and fill_locations:
spot_to_fill = fill_locations.pop() spot_to_fill = fill_locations.pop()
item_to_place = item_pool.pop() item_to_place = fast_pool.pop()
world.push_item(spot_to_fill, item_to_place, False) world.push_item(spot_to_fill, item_to_place, False)
item_pool.clear()
item_pool.extend(filtered_pool)
item_pool.extend(fast_pool)
def fast_fill_pot_for_multiworld(world, item_pool, fill_locations): def fast_fill_pot_for_multiworld(world, item_pool, fill_locations):

View File

@@ -6,7 +6,7 @@ import RaceRandom as random
from BaseClasses import Region, RegionType, Shop, ShopType, Location, CollectionState, PotItem from BaseClasses import Region, RegionType, Shop, ShopType, Location, CollectionState, PotItem
from EntranceShuffle import connect_entrance from EntranceShuffle import connect_entrance
from Regions import shop_to_location_table, retro_shops, shop_table_by_location, valid_pot_location from Regions import shop_to_location_table, retro_shops, shop_table_by_location, valid_pot_location
from Fill import FillError, fill_restrictive, get_dungeon_item_pool, is_dungeon_item from Fill import FillError, fill_restrictive, get_dungeon_item_pool, track_dungeon_items, track_outside_keys
from PotShuffle import vanilla_pots from PotShuffle import vanilla_pots
from Items import ItemFactory from Items import ItemFactory
@@ -391,8 +391,16 @@ def generate_itempool(world, player):
custom_medallions = medal_map[player] custom_medallions = medal_map[player]
if 'Misery Mire' in custom_medallions: if 'Misery Mire' in custom_medallions:
mm_medallion = custom_medallions['Misery Mire'] mm_medallion = custom_medallions['Misery Mire']
if isinstance(mm_medallion, dict):
mm_medallion = random.choices(list(mm_medallion.keys()), list(mm_medallion.values()), k=1)[0]
if mm_medallion == 'Random':
mm_medallion = None
if 'Turtle Rock' in custom_medallions: if 'Turtle Rock' in custom_medallions:
tr_medallion = custom_medallions['Turtle Rock'] tr_medallion = custom_medallions['Turtle Rock']
if isinstance(tr_medallion, dict):
tr_medallion = random.choices(list(tr_medallion.keys()), list(tr_medallion.values()), k=1)[0]
if tr_medallion == 'Random':
tr_medallion = None
if not mm_medallion: if not mm_medallion:
mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
if not tr_medallion: if not tr_medallion:
@@ -1261,31 +1269,86 @@ def fill_specific_items(world):
for player, placement_list in placements.items(): for player, placement_list in placements.items():
for location, item in placement_list.items(): for location, item in placement_list.items():
loc = world.get_location(location, player) loc = world.get_location(location, player)
item_parts = item.split('#') item_to_place, event_flag = get_item_and_event_flag(item, world, player,
item_player = player if len(item_parts) < 2 else int(item_parts[1]) dungeon_pool, prize_set, prize_pool)
item_name = item_parts[0] if item_to_place:
event_flag = False world.push_item(loc, item_to_place, False)
if is_dungeon_item(item_name, world, item_player): track_outside_keys(item_to_place, loc, world)
item_to_place = next(x for x in dungeon_pool track_dungeon_items(item_to_place, loc, world)
if x.name == item_name and x.player == item_player) loc.event = event_flag or item_to_place.advancement
dungeon_pool.remove(item_to_place) advanced_placements = world.customizer.get_advanced_placements()
event_flag = True if advanced_placements:
elif item_name in prize_set: for player, placement_list in advanced_placements.items():
item_player = player # prizes must be for that player for placement in placement_list:
item_to_place = ItemFactory(item_name, item_player) if placement['type'] == 'LocationGroup':
prize_pool.remove(item_name) item = placement['item']
event_flag = True item_to_place, event_flag = get_item_and_event_flag(item, world, player,
else: dungeon_pool, prize_set, prize_pool)
item_to_place = next((x for x in world.itempool if not item_to_place:
if x.name == item_name and x.player == item_player), None) continue
if item_to_place is None: locations = placement['locations']
item_to_place = ItemFactory(item_name, player) handled = False
else: while not handled:
world.itempool.remove(item_to_place) if isinstance(locations, dict):
world.push_item(loc, item_to_place, False) chosen_loc = random.choices(list(locations.keys()), list(locations.values()), k=1)[0]
# track_outside_keys(item_to_place, spot_to_fill, world) else: # if isinstance(locations, list):
# track_dungeon_items(item_to_place, spot_to_fill, world) chosen_loc = random.choice(locations)
loc.event = event_flag or item_to_place.advancement if chosen_loc == 'Random':
if is_dungeon_item(item_to_place.name, world, item_to_place.player):
dungeon_pool.append(item_to_place)
elif item_to_place.name in prize_set:
prize_pool.append(item_to_place.name)
else:
world.itempool.append(item_to_place)
else:
loc = world.get_location(chosen_loc, player)
if loc.item:
continue
world.push_item(loc, item_to_place, False)
track_outside_keys(item_to_place, loc, world)
track_dungeon_items(item_to_place, loc, world)
loc.event = (event_flag or item_to_place.advancement
or item_to_place.bigkey or item_to_place.smallkey)
handled = True
elif placement['type'] == 'NotLocationGroup':
item = placement['item']
item_parts = item.split('#')
item_player = player if len(item_parts) < 2 else int(item_parts[1])
item_name = item_parts[0]
world.item_pool_config.restricted[(item_name, item_player)] = placement['locations']
elif placement['type'] == 'PreferredLocationGroup':
item = placement['item']
item_parts = item.split('#')
item_player = player if len(item_parts) < 2 else int(item_parts[1])
item_name = item_parts[0]
world.item_pool_config.preferred[(item_name, item_player)] = placement['locations']
def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_pool):
item_parts = item.split('#')
item_player = player if len(item_parts) < 2 else int(item_parts[1])
item_name = item_parts[0]
event_flag = False
if is_dungeon_item(item_name, world, item_player):
item_to_place = next(x for x in dungeon_pool
if x.name == item_name and x.player == item_player)
dungeon_pool.remove(item_to_place)
event_flag = True
elif item_name in prize_set:
item_player = player # prizes must be for that player
item_to_place = ItemFactory(item_name, item_player)
prize_pool.remove(item_name)
event_flag = True
else:
matcher = lambda x: x.name == item_name and x.player == item_player
if item_name == 'Bottle':
matcher = lambda x: x.name.startswith(item_name) and x.player == item_player
item_to_place = next((x for x in world.itempool if matcher(x)), None)
if item_to_place is None:
return None, event_flag
else:
world.itempool.remove(item_to_place)
return item_to_place, event_flag
def is_dungeon_item(item, world, player): def is_dungeon_item(item, world, player):

View File

@@ -83,7 +83,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Arrows (5)': (False, False, None, 0x5A, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'), 'Arrows (5)': (False, False, None, 0x5A, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'),
'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'), 'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'),
'Big Magic': (False, False, None, 0x5A, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), 'Big Magic': (False, False, None, 0x5A, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'),
'Chicken': (False, False, None, 0x5A, 999, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), 'Chicken': (False, False, None, 0x5A, 5, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'),
'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'), 'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'),
'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'), 'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'),
'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), 'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'),

View File

@@ -301,10 +301,10 @@ def analyze_dungeon(key_layout, world, player):
key_logic.bk_restricted.update(filter_big_chest(key_counter.free_locations)) key_logic.bk_restricted.update(filter_big_chest(key_counter.free_locations))
# note to self: this is due to the enough_small_locations function in validate_key_layout_sub_loop # note to self: this is due to the enough_small_locations function in validate_key_layout_sub_loop
# I don't like this exception here or there # I don't like this exception here or there
elif available < possible_smalls and avail_bigs and non_big_locs > 0: # elif available < possible_smalls and avail_bigs and non_big_locs > 0:
max_ctr = find_max_counter(key_layout) # max_ctr = find_max_counter(key_layout)
bk_lockdown = [x for x in max_ctr.free_locations if x not in key_counter.free_locations] # bk_lockdown = [x for x in max_ctr.free_locations if x not in key_counter.free_locations]
key_logic.bk_restricted.update(filter_big_chest(bk_lockdown)) # key_logic.bk_restricted.update(filter_big_chest(bk_lockdown))
# try to relax the rules here? - smallest requirement that doesn't force a softlock # try to relax the rules here? - smallest requirement that doesn't force a softlock
child_queue = deque() child_queue = deque()
for child in key_counter.child_doors.keys(): for child in key_counter.child_doors.keys():
@@ -1489,7 +1489,7 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
# todo: allow more key shuffles - refine placement rules # todo: allow more key shuffles - refine placement rules
# if (not smalls_avail or available_small_locations == 0) and (state.big_key_opened or num_bigs == 0 or available_big_locations == 0): # if (not smalls_avail or available_small_locations == 0) and (state.big_key_opened or num_bigs == 0 or available_big_locations == 0):
found_forced_bk = state.found_forced_bk() found_forced_bk = state.found_forced_bk()
smalls_done = not smalls_avail or not enough_small_locations(state, available_small_locations) smalls_done = not smalls_avail # or not enough_small_locations(state, available_small_locations)
bk_done = state.big_key_opened or num_bigs == 0 or (available_big_locations == 0 and not found_forced_bk) bk_done = state.big_key_opened or num_bigs == 0 or (available_big_locations == 0 and not found_forced_bk)
# prize door should not be opened if the boss is reachable - but not reached yet # prize door should not be opened if the boss is reachable - but not reached yet
allow_for_prize_lock = (key_layout.prize_can_lock and allow_for_prize_lock = (key_layout.prize_can_lock and

18
Main.py
View File

@@ -24,6 +24,7 @@ from RoomData import create_rooms
from Rules import set_rules from Rules import set_rules
from Dungeons import create_dungeons from Dungeons import create_dungeons
from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive, ensure_good_pots from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive, ensure_good_pots
from Fill import dungeon_tracking
from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items
from Utils import output_path, parse_player_names from Utils import output_path, parse_player_names
@@ -69,9 +70,8 @@ def main(args, seed=None, fish=None):
if args.customizer: if args.customizer:
customized = CustomSettings() customized = CustomSettings()
customized.load_yaml(args.customizer) customized.load_yaml(args.customizer)
seed = customized.determine_seed() seed = customized.determine_seed(seed)
if seed: seeded = True
seeded = True
customized.adjust_args(args) customized.adjust_args(args)
world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords,
args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm,
@@ -89,6 +89,7 @@ def main(args, seed=None, fish=None):
if args.securerandom: if args.securerandom:
world.seed = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(9)) world.seed = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(9))
world.boots_hint = args.boots_hint.copy()
world.remote_items = args.remote_items.copy() world.remote_items = args.remote_items.copy()
world.mapshuffle = args.mapshuffle.copy() world.mapshuffle = args.mapshuffle.copy()
world.compassshuffle = args.compassshuffle.copy() world.compassshuffle = args.compassshuffle.copy()
@@ -195,6 +196,16 @@ def main(args, seed=None, fish=None):
item = ItemFactory(inv_item.strip(), p) item = ItemFactory(inv_item.strip(), p)
if item: if item:
world.push_precollected(item) world.push_precollected(item)
if item.dungeon:
d = world.get_dungeon(item.dungeon, item.player)
match = next((i for i in d.all_items if i.name == item.name), None)
if match:
if match.map or match.compass:
d.dungeon_items.remove(match)
elif match.smallkey:
d.small_keys.remove(match)
elif match.bigkey:
d.big_key.remove(match)
if args.print_custom_yaml: if args.print_custom_yaml:
world.settings.record_info(world) world.settings.record_info(world)
@@ -258,6 +269,7 @@ def main(args, seed=None, fish=None):
massage_item_pool(world) massage_item_pool(world)
if args.print_custom_yaml: if args.print_custom_yaml:
world.settings.record_item_pool(world) world.settings.record_item_pool(world)
dungeon_tracking(world)
fill_specific_items(world) fill_specific_items(world)
logger.info(world.fish.translate("cli", "cli", "placing.dungeon.prizes")) logger.info(world.fish.translate("cli", "cli", "placing.dungeon.prizes"))

60
Rom.py
View File

@@ -1084,9 +1084,14 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x178000 + i, random.randint(0, 255)) rom.write_byte(0x178000 + i, random.randint(0, 255))
# shuffle prize packs # shuffle prize packs
prizes = [0xD8, 0xD8, 0xD8, 0xD8, 0xD9, 0xD8, 0xD8, 0xD9, 0xDA, 0xD9, 0xDA, 0xDB, 0xDA, 0xD9, 0xDA, 0xDA, 0xE0, 0xDF, 0xDF, 0xDA, 0xE0, 0xDF, 0xD8, 0xDF, prizes = [0xD8, 0xD8, 0xD8, 0xD8, 0xD9, 0xD8, 0xD8, 0xD9,
0xDC, 0xDC, 0xDC, 0xDD, 0xDC, 0xDC, 0xDE, 0xDC, 0xE1, 0xD8, 0xE1, 0xE2, 0xE1, 0xD8, 0xE1, 0xE2, 0xDF, 0xD9, 0xD8, 0xE1, 0xDF, 0xDC, 0xD9, 0xD8, 0xDA, 0xD9, 0xDA, 0xDB, 0xDA, 0xD9, 0xDA, 0xDA,
0xD8, 0xE3, 0xE0, 0xDB, 0xDE, 0xD8, 0xDB, 0xE2, 0xD9, 0xDA, 0xDB, 0xD9, 0xDB, 0xD9, 0xDB] 0xE0, 0xDF, 0xDF, 0xDA, 0xE0, 0xDF, 0xD8, 0xDF,
0xDC, 0xDC, 0xDC, 0xDD, 0xDC, 0xDC, 0xDE, 0xDC,
0xE1, 0xD8, 0xE1, 0xE2, 0xE1, 0xD8, 0xE1, 0xE2,
0xDF, 0xD9, 0xD8, 0xE1, 0xDF, 0xDC, 0xD9, 0xD8,
0xD8, 0xE3, 0xE0, 0xDB, 0xDE, 0xD8, 0xDB, 0xE2,
0xD9, 0xDA, 0xDB, 0xD9, 0xDB, 0xD9, 0xDB]
dig_prizes = [0xB2, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, dig_prizes = [0xB2, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
@@ -1099,13 +1104,41 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
return [l[i:i+n] for i in range(0, len(l), n)] return [l[i:i+n] for i in range(0, len(l), n)]
# randomize last 7 slots # randomize last 7 slots
prizes [-7:] = random.sample(prizes, 7) possible_prizes = {
'Small Heart': 0xD8, 'Fairy': 0xE3,
'Rupee (1)': 0xD9, 'Rupees (5)': 0xDA, 'Rupees (20)': 0xDB,
'Big Magic': 0xE0, 'Small Magic': 0xDF,
'Single Bomb': 0xDC, 'Bombs (4)': 0xDD,
'Bombs (8)': 0xDE, 'Arrows (5)': 0xE1, 'Arrows (10)': 0xE2
} #weights, if desired 13, 1, 9, 7, 6, 3, 6, 7, 1, 2, 5, 3
uniform_prizes = list(possible_prizes.values())
prizes[-7:] = random.sample(prizes, 7)
#shuffle order of 7 main packs #shuffle order of 7 main packs
packs = chunk(prizes[:56], 8) packs = chunk(prizes[:56], 8)
random.shuffle(packs) random.shuffle(packs)
prizes[:56] = [drop for pack in packs for drop in pack] prizes[:56] = [drop for pack in packs for drop in pack]
if world.customizer:
drops = world.customizer.get_drops()
if drops:
for player, drop_config in drops.items():
for pack_num in range(1, 8):
if f'Pack {pack_num}' in drop_config:
for prize, idx in enumerate(drop_config[f'Pack {pack_num}']):
chosen = random.choice(uniform_prizes) if prize == 'Random' else possible_prizes[prize]
prizes[(pack_num-1)*8 + idx] = chosen
for tree_pull_tier in range(1, 4):
if f'Tree Pull Tier {tree_pull_tier}' in drop_config:
prize = drop_config[f'Tree Pull Tier {tree_pull_tier}']
chosen = random.choice(uniform_prizes) if prize == 'Random' else possible_prizes[prize]
prizes[63-tree_pull_tier] = chosen # (62 through 60 in reverse)
for key, pos in {'Crab Normal': 59, 'Crab Special': 58, 'Stun Prize': 57, 'Fish': 56}.items():
if key in drop_config:
prize = drop_config[key]
chosen = random.choice(uniform_prizes) if prize == 'Random' else possible_prizes[prize]
prizes[pos] = chosen
if world.difficulty_adjustments[player] in ['hard', 'expert']: if world.difficulty_adjustments[player] in ['hard', 'expert']:
prize_replacements = {0xE0: 0xDF, # Fairy -> heart prize_replacements = {0xE0: 0xDF, # Fairy -> heart
0xE3: 0xD8} # Big magic -> small magic 0xE3: 0xD8} # Big magic -> small magic
@@ -2290,7 +2323,24 @@ def write_strings(rom, world, player, team):
if world.goal[player] in ['dungeons']: if world.goal[player] in ['dungeons']:
tt['sign_ganon'] = 'You need to complete all the dungeons.' tt['sign_ganon'] = 'You need to complete all the dungeons.'
tt['uncle_leaving_text'] = Uncle_texts[random.randint(0, len(Uncle_texts) - 1)] if world.boots_hint[player]:
starting_boots = next((i for i in world.precollected_items if i.player == player
and i.name == 'Pegasus Boots'), None)
if starting_boots:
uncle_text = 'Lonk! Boots\nare on\nyour feet.'
else:
boots_location = next((l for l in world.get_locations()
if l.player == player and l.item and l.item.name == 'Pegasus Boots'), None)
if boots_location:
district = next((d for k, d in world.districts[player].items()
if boots_location.name in d.locations), 'Zebes')
uncle_text = f'Lonk! Boots\nare in {district.name}'
else:
uncle_text = "I couldn't\nfind the Boots\ntoday.\nRIP me."
tt['uncle_leaving_text'] = uncle_text
else:
tt['uncle_leaving_text'] = Uncle_texts[random.randint(0, len(Uncle_texts) - 1)]
tt['end_triforce'] = "{NOBORDER}\n" + Triforce_texts[random.randint(0, len(Triforce_texts) - 1)] tt['end_triforce'] = "{NOBORDER}\n" + Triforce_texts[random.randint(0, len(Triforce_texts) - 1)]
tt['bomb_shop_big_bomb'] = BombShop2_texts[random.randint(0, len(BombShop2_texts) - 1)] tt['bomb_shop_big_bomb'] = BombShop2_texts[random.randint(0, len(BombShop2_texts) - 1)]

View File

@@ -2091,8 +2091,8 @@ def eval_small_key_door_main(state, door_name, dungeon, player):
if ruleType == KeyRuleType.WorstCase: if ruleType == KeyRuleType.WorstCase:
door_openable |= state.has_sm_key(key_logic.small_key_name, player, number) door_openable |= state.has_sm_key(key_logic.small_key_name, player, number)
elif ruleType == KeyRuleType.AllowSmall: elif ruleType == KeyRuleType.AllowSmall:
if (door_rule.small_location.item and door_rule.small_location.item.name == key_logic.small_key_name small_loc_item = door_rule.small_location.item
and door_rule.small_location.item.player == player): if small_loc_item and small_loc_item.name == key_logic.small_key_name and small_loc_item.player == player:
door_openable |= state.has_sm_key(key_logic.small_key_name, player, number) door_openable |= state.has_sm_key(key_logic.small_key_name, player, number)
elif isinstance(ruleType, tuple): elif isinstance(ruleType, tuple):
lock, lock_item = ruleType lock, lock_item = ruleType

View File

@@ -1716,7 +1716,7 @@ class TextTable(object):
text['game_shooting_choice'] = CompressedTextMapper.convert("20 rupees.\n5 arrows.\nWin rupees!\nWant to play?\n ≥ Yes\n No\n{CHOICE}") text['game_shooting_choice'] = CompressedTextMapper.convert("20 rupees.\n5 arrows.\nWin rupees!\nWant to play?\n ≥ Yes\n No\n{CHOICE}")
text['game_shooting_yes'] = CompressedTextMapper.convert("Let's do this!") text['game_shooting_yes'] = CompressedTextMapper.convert("Let's do this!")
text['game_shooting_no'] = CompressedTextMapper.convert("Where are you going? Straight up!") text['game_shooting_no'] = CompressedTextMapper.convert("Where are you going? Straight up!")
text['game_shooting_continue'] = CompressedTextMapper.convert("Keep playing?\nyes\n no\n{CHOICE}") text['game_shooting_continue'] = CompressedTextMapper.convert("Keep playing?\nYes\n No\n{CHOICE}")
text['pond_of_wishing'] = CompressedTextMapper.convert("-Wishing Pond-\n\n On Vacation") text['pond_of_wishing'] = CompressedTextMapper.convert("-Wishing Pond-\n\n On Vacation")
text['pond_item_select'] = CompressedTextMapper.convert("Pick something\nto throw in.\n{ITEMSELECT}") text['pond_item_select'] = CompressedTextMapper.convert("Pick something\nto throw in.\n{ITEMSELECT}")
text['pond_item_test'] = CompressedTextMapper.convert("You toss this?\n ≥ Yup\n Wrong\n{CHOICE}") text['pond_item_test'] = CompressedTextMapper.convert("You toss this?\n ≥ Yup\n Wrong\n{CHOICE}")

View File

@@ -38,7 +38,7 @@ in each player section you can set up default settings for that player or you ca
door_shuffle: basic door_shuffle: basic
``` ```
Player 1's settings will be determined by rolling the mystery weights and player 2's setting will be default except for those two specified in his section. Each settings should be consistent with the CLI arguments. Player 1's settings will be determined by rolling the mystery weights and player 2's setting will be default except for those two specified in his section. Each setting should be consistent with the CLI arguments. Simple weighted settings are supported here. If you need sub-weights though, use a separate yaml file.
Start inventory is not supported here. It has a separate section. Start inventory is not supported here. It has a separate section.
@@ -71,7 +71,21 @@ You may list each location for a player and the item you wish to place there. A
Example: Example:
`Pegasus Boots#3` means the boots for player 3. `Pegasus Boots#3` means the boots for player 3.
### advanced_placements
This must be defined by player. Each player number should be listed with the appropriate section. Each section is a list of placement rules. Each placement rule has a specific type.
Supported Types: PlacementGroup, NotPlacmentGroup
#### PlacementGroup
You may define an item, and a list of locations. The locations may be weighted if desired. The item will be placed at one of the listed locations - this currently ignores logic. The item will be placed there. The special location 'Random' indicates that the item should be placed randomly, without any other consideration. This may be repeated for placement of multiple items like multiple bows or swords.
#### NotPlacementGroup
You may define an item and a list of locations that an item should not be placed at. This will apply to all items of that type. The logic is considered for this. If it is otherwise impossible, the item will be considered for the listed locations. This is important for small key layouts mostly, but it will try other locations first.
### entrances ### entrances
@@ -144,7 +158,7 @@ Misery Mire: Ether
Turtle Rock: Quake Turtle Rock: Quake
``` ```
Leave blank or omit if you wish it to be random. Leave blank or omit if you wish it to be random. Alternatively, a weighted dictionary is supported and a 'Random' option
### bosses ### bosses
@@ -173,4 +187,27 @@ To start with multiple copies of progressive items, list them more than once.
This conflicts with the mystery yaml, if specified. These start inventory items will be added after those are added. This conflicts with the mystery yaml, if specified. These start inventory items will be added after those are added.
### drops
This must be defined by player. You may have prize packs, tree pulls, crab drops, stun prizes, and the fish prize defined using the following keys:
```
drops:
1:
Pack 1
- Small Heart
- Bombs (4)
- Random
- etc
Pack 2: (list)
...
Pack 7: (list)
Tree Pull Tier 1: Single Bomb
Tree Pull Tier 2: Arrows (10)
Tree Pull Tier 3: Fairy
Crab Normal: Rupees (20)
Crab Special: Small Magic
Stun Prize: Bombs (8)
Fish: Big Magic
```
Prize packs expect a list of eight items each (anything not specified will be whatever randomization would have normally occurred). The special drops expect a single item. Packs 1 through 7 are supported. Prize pack 0 is not customizable.

1036
docs/antivanilla.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
meta:
algorithm: balanced
players: 1
settings:
1:
mode: inverted
mapshuffle: True
keyshuffle: wild
placements:
1:
Link's Uncle: Progressive Sword
Blacksmith: Progressive Sword
Master Sword Pedestal: Progressive Sword
Pyramid Fairy - Left: Progressive Sword
Desert Palace - Boss: Compass (Desert Palace)
Eastern Palace - Boss: Compass (Eastern Palace)
Tower of Hera - Boss: Compass (Tower of Hera)
Swamp Palace - Boss: Compass (Swamp Palace)
Thieves' Town - Boss: Compass (Thieves Town)
Skull Woods - Boss: Compass (Skull Woods)
Ice Palace - Boss: Compass (Ice Palace)
Misery Mire - Boss: Compass (Misery Mire)
Turtle Rock - Boss: Compass (Turtle Rock)
Palace of Darkness - Boss: Compass (Palace of Darkness)
Eastern Palace - Big Key Chest: Big Key (Eastern Palace)
Desert Palace - Big Key Chest: Big Key (Desert Palace)
Tower of Hera - Big Key Chest: Big Key (Tower of Hera)
Palace of Darkness - Big Key Chest: Big Key (Palace of Darkness)
Thieves' Town - Big Key Chest: Big Key (Thieves Town)
Skull Woods - Big Key Chest: Big Key (Skull Woods)
Swamp Palace - Big Key Chest: Big Key (Swamp Palace)
Ice Palace - Big Key Chest: Big Key (Ice Palace)
Misery Mire - Big Key Chest: Big Key (Misery Mire)
Turtle Rock - Big Key Chest: Big Key (Turtle Rock)
Ganons Tower - Big Key Chest: Big Key (Ganons Tower)
start_inventory:
1:
- Ocarina (Activated)

View File

@@ -0,0 +1,73 @@
meta:
algorithm: balanced
players: 1
settings:
1:
mode: inverted
goal: dungeons
shuffleenemies: shuffled
shufflebosses: full
item_pool:
1:
Progressive Bow: 8
Blue Boomerang: 4
Red Boomerang: 4
Hookshot: 4
Magic Powder: 4
Mushroom: 4
Ice Rod: 4
Fire Rod: 4
Bombos: 4
Ether: 4
Quake: 4
Lamp: 4
Hammer: 4
Ocarina: 4
Shovel: 4
Bug Catching Net: 4
Book of Mudora: 4
Bottle (Random): 16
Cane of Byrna: 4
Cane of Somaria: 4
Cape: 4
Magic Mirror: 4
Flippers: 4
Moon Pearl: 4
Pegasus Boots: 4
Progressive Glove: 8
Progressive Armor: 8
Progressive Shield: 12
Progressive Sword: 16
Magic Upgrade (1/2): 4
start_inventory:
1:
- Boss Heart Container
- Boss Heart Container
- Boss Heart Container
- Pegasus Boots
- Rupees (5)
- Rupees (5)
- Rupees (5)
- Rupee (1)
- Rupee (1)
- Rupee (1)
- Rupee (1)
- Compass (Desert Palace)
- Compass (Eastern Palace)
- Compass (Tower of Hera)
- Compass (Swamp Palace)
- Compass (Thieves Town)
- Compass (Skull Woods)
- Compass (Ice Palace)
- Compass (Misery Mire)
- Compass (Turtle Rock)
- Compass (Palace of Darkness)
- Compass (Ganons Tower)
drops:
1:
Tree Pull Tier 1: Single Bomb
Tree Pull Tier 2: Bombs (4)
Tree Pull Tier 3: Bombs (8)
Crab Normal: Rupees (20)
Crab Special: Big Magic
Stun Prize: Rupees (20)

View File

@@ -0,0 +1,443 @@
meta:
algorithm: balanced
players: 1
settings:
1:
mode:
open: 1
standard: 1
mapshuffle: True
compassshuffle: True
boots_hint:
True: 1
False: 1
advanced_placements:
1:
- item: Green Pendant
locations:
- Eastern Palace - Prize
- Random
type: LocationGroup
- item: Crystal 5
locations:
- Ice Palace - Prize
- Random
type: LocationGroup
- item: Crystal 6
locations:
- Misery Mire - Prize
- Random
type: LocationGroup
- item: Progressive Bow
locations:
- Eastern Palace - Big Chest
- Random
type: LocationGroup
- item: Progressive Bow
locations:
- Pyramid Fairy - Right
- Random
type: LocationGroup
- item: Book of Mudora
locations:
- Library
- Random
type: LocationGroup
- item: Hammer
locations:
- Palace of Darkness - Big Chest
- Random
type: LocationGroup
- item: Hookshot
locations:
- Swamp Palace - Big Chest
- Random
type: LocationGroup
- item: Magic Mirror
locations:
- Old Man
- Random
type: LocationGroup
- item: Ocarina
locations:
- Flute Spot
- Random
type: LocationGroup
- item: Pegasus Boots
locations:
- Sahasrahla
- Random
type: LocationGroup
- item: Cape
locations:
- King's Tomb
- Random
type: LocationGroup
- item: Mushroom
locations:
- Mushroom
- Random
type: LocationGroup
- item: Shovel
locations:
- Stumpy
- Random
type: LocationGroup
- item: Lamp
locations:
Link's House: 1
Secret Passage: 1
Hyrule Castle - Zelda's Chest: 1
Random: 3
type: LocationGroup
- item: Magic Powder
locations:
- Potion Shop
- Random
type: LocationGroup
- item: Moon Pearl
locations:
- Tower of Hera - Big Chest
- Random
type: LocationGroup
- item: Cane of Somaria
locations:
- Misery Mire - Big Chest
- Random
type: LocationGroup
- item: Fire Rod
locations:
- Skull Woods - Big Chest
- Random
type: LocationGroup
- item: Flippers
locations:
- King Zora
- Random
type: LocationGroup
- item: Ice Rod
locations:
- Ice Rod Cave
- Random
type: LocationGroup
- item: Bombos
locations:
- Bombos Tablet
- Random
type: LocationGroup
- item: Ether
locations:
- Ether Tablet
- Random
type: LocationGroup
- item: Quake
locations:
- Catfish
- Random
type: LocationGroup
- item: Bottle
locations:
- Bottle Merchant
- Random
type: LocationGroup
- item: Bottle
locations:
- Kakariko Tavern
- Random
type: LocationGroup
- item: Bottle
locations:
- Purple Chest
- Random
type: LocationGroup
- item: Bottle
locations:
- Hobo
- Random
type: LocationGroup
- item: Progressive Sword
locations:
- Link's Uncle
- Random
type: LocationGroup
- item: Progressive Sword
locations:
- Blacksmith
- Random
type: LocationGroup
- item: Progressive Sword
locations:
- Master Sword Pedestal
- Random
type: LocationGroup
- item: Progressive Sword
locations:
- Pyramid Fairy - Left
- Random
type: LocationGroup
- item: Progressive Glove
locations:
- Desert Palace - Big Chest
- Random
type: LocationGroup
- item: Progressive Glove
locations:
- Thieves' Town - Big Chest
- Random
type: LocationGroup
- item: Progressive Armor
locations:
- Ice Palace - Big Chest
- Random
type: LocationGroup
- item: Progressive Armor
locations:
- Ganons Tower - Big Chest
- Random
type: LocationGroup
- item: Blue Boomerang
locations:
- Hyrule Castle - Boomerang Chest
- Random
type: LocationGroup
- item: Red Boomerang
locations:
- Waterfall Fairy - Left
- Random
type: LocationGroup
- item: Progressive Shield
locations:
- Secret Passage
- Random
type: LocationGroup
- item: Progressive Shield
locations:
- Waterfall Fairy - Right
- Random
type: LocationGroup
- item: Progressive Shield
locations:
- Turtle Rock - Big Chest
- Random
type: LocationGroup
- item: Bug Catching Net
locations:
- Sick Kid
- Random
type: LocationGroup
- item: Cane of Byrna
locations:
- Spike Cave
- Random
type: LocationGroup
- item: Boss Heart Container
locations:
- Desert Palace - Boss
- Random
type: LocationGroup
- item: Boss Heart Container
locations:
- Eastern Palace - Boss
- Random
type: LocationGroup
- item: Boss Heart Container
locations:
- Tower of Hera - Boss
- Random
type: LocationGroup
- item: Boss Heart Container
locations:
- Swamp Palace - Boss
- Random
type: LocationGroup
- item: Boss Heart Container
locations:
- Thieves' Town - Boss
- Random
type: LocationGroup
- item: Boss Heart Container
locations:
- Skull Woods - Boss
- Random
type: LocationGroup
- item: Boss Heart Container
locations:
- Ice Palace - Boss
- Random
type: LocationGroup
- item: Boss Heart Container
locations:
- Misery Mire - Boss
- Random
type: LocationGroup
- item: Boss Heart Container
locations:
- Turtle Rock - Boss
- Random
type: LocationGroup
- item: Boss Heart Container
locations:
- Palace of Darkness - Boss
- Random
type: LocationGroup
- item: Sanctuary Heart Container
locations:
- Sanctuary
- Random
type: LocationGroup
- item: Rupees (300)
locations:
- Mini Moldorm Cave - Generous Guy
- Random
type: LocationGroup
- item: Rupees (300)
locations:
- Sewers - Secret Room - Middle
- Random
type: LocationGroup
- item: Rupees (300)
locations:
- Hype Cave - Generous Guy
- Random
type: LocationGroup
- item: Rupees (300)
locations:
- Brewery
- Random
type: LocationGroup
- item: Rupees (300)
locations:
- C-Shaped House
- Random
type: LocationGroup
- item: Magic Upgrade (1/2)
locations:
- Magic Bat
- Random
type: LocationGroup
- item: Compass (Eastern Palace)
locations:
- Eastern Palace - Compass Chest
- Random
type: LocationGroup
- item: Map (Eastern Palace)
locations:
- Eastern Palace - Map Chest
- Random
type: LocationGroup
- item: Compass (Desert Palace)
locations:
- Desert Palace - Compass Chest
- Random
type: LocationGroup
- item: Map (Desert Palace)
locations:
- Desert Palace - Map Chest
- Random
type: LocationGroup
- item: Compass (Tower of Hera)
locations:
- Tower of Hera - Compass Chest
- Random
type: LocationGroup
- item: Map (Tower of Hera)
locations:
- Tower of Hera - Map Chest
- Random
type: LocationGroup
- item: Map (Escape)
locations:
- Hyrule Castle - Map Chest
- Random
type: LocationGroup
- item: Compass (Palace of Darkness)
locations:
- Palace of Darkness - Compass Chest
- Random
type: LocationGroup
- item: Map (Palace of Darkness)
locations:
- Palace of Darkness - Map Chest
- Random
type: LocationGroup
- item: Compass (Thieves Town)
locations:
- Thieves' Town - Compass Chest
- Random
type: LocationGroup
- item: Map (Thieves Town)
locations:
- Thieves' Town - Map Chest
- Random
type: LocationGroup
- item: Compass (Skull Woods)
locations:
- Skull Woods - Compass Chest
- Random
type: LocationGroup
- item: Map (Skull Woods)
locations:
- Skull Woods - Map Chest
- Random
type: LocationGroup
- item: Compass (Swamp Palace)
locations:
- Swamp Palace - Compass Chest
- Random
type: LocationGroup
- item: Map (Swamp Palace)
locations:
- Swamp Palace - Map Chest
- Random
type: LocationGroup
- item: Compass (Ice Palace)
locations:
- Ice Palace - Compass Chest
- Random
type: LocationGroup
- item: Map (Ice Palace)
locations:
- Ice Palace - Map Chest
- Random
type: LocationGroup
- item: Compass (Misery Mire)
locations:
- Misery Mire - Compass Chest
- Random
type: LocationGroup
- item: Map (Misery Mire)
locations:
- Misery Mire - Map Chest
- Random
type: LocationGroup
- item: Compass (Turtle Rock)
locations:
- Turtle Rock - Compass Chest
- Random
type: LocationGroup
- item: Map (Turtle Rock)
locations:
- Turtle Rock - Roller Room - Left
- Random
type: LocationGroup
- item: Compass (Ganons Tower)
locations:
- Ganons Tower - Compass Room - Top Left
- Random
type: LocationGroup
- item: Map (Ganons Tower)
locations:
- Ganons Tower - Map Chest
- Random
type: LocationGroup
medallions:
1:
Misery Mire:
Ether: 1
Random: 1
Turtle Rock:
Quake: 1
Random: 1

View File

@@ -0,0 +1,21 @@
meta:
algorithm: balanced
players: 1
settings:
1:
goal: triforcehunt
shuffle: crossed
keysanity: True
shopsanity: True
keydropshuffle: True
pottery: lottery
dungeon_counters: 'on'
triforce_goal: 420
triforce_pool: 420
start_inventory:
1:
- Pegasus Boots
- Lamp
- Rupees (300)
- Rupees (100)
- Rupees (20)

View File

@@ -0,0 +1,21 @@
meta:
algorithm: balanced
players: 1
settings:
1:
goal: triforcehunt
shuffle: crossed
keysanity: True
shopsanity: True
keydropshuffle: True
pottery: lottery
dungeon_counters: 'on'
triforce_goal: 213
triforce_pool: 420
start_inventory:
1:
- Pegasus Boots
- Lamp
- Rupees (300)
- Rupees (100)
- Rupees (20)

View File

@@ -0,0 +1,259 @@
meta:
algorithm: balanced
players: 1
settings:
1:
bigkeyshuffle: True
placements:
1:
Potion Shop: Shovel
Flute Spot: Magic Powder
advanced_placements:
1:
- item: Progressive Bow
locations:
- Palace of Darkness - Boss
- Palace of Darkness - Map Chest
- Palace of Darkness - The Arena - Ledge
- Eastern Palace - Boss
- Ganons Tower - Pre-Moldorm Chest
- Ganons Tower - Mini Helmasaur Room - Left
- Ganons Tower - Mini Helmasaur Room - Right
- Ganons Tower - Validation Chest
type: LocationGroup
- item: Fire Rod
locations:
- Ice Palace - Big Chest
- Ice Palace - Boss
- Ice Palace - Big Key Chest
- Ice Palace - Compass Chest
- Ice Palace - Map Chest
- Ice Palace - Spike Room
- Ice Palace - Iced T Room
- Ice Palace - Freezor Chest
- Desert Palace - Boss
- Tower of Hera - Big Key Chest
- Misery Mire - Big Key Chest
- Misery Mire - Compass Chest
type: LocationGroup
- item: Pegasus Boots
locations:
- Hookshot Cave - Top Right
- Hookshot Cave - Top Left
- Hookshot Cave - Bottom Right
- Hookshot Cave - Bottom Left
- Mushroom Spot
- Zora Ledge
- Bombos Tablet
- Ether Tablet
- Floating Island
- Lake Hylia Island
- Maze Race
- Spectacle Rock
- Spectacle Rock Cave
- Pyramid
- Desert Ledge
- Bumper Cave Ledge
type: LocationGroup
- item: Progressive Sword
locations:
- Swamp Palace - Big Chest
- Swamp Palace - Boss
- Swamp Palace - Big Key Chest
- Swamp Palace - Compass Chest
- Swamp Palace - Map Chest
- Swamp Palace - Entrance
- Swamp Palace - Flooded Room - Left
- Swamp Palace - Flooded Room - Right
- Swamp Palace - Waterfall Room
- Swamp Palace - West Chest
type: LocationGroup
- item: Titans Mitts
locations:
- Swamp Palace - Big Chest
- Swamp Palace - Boss
- Swamp Palace - Big Key Chest
- Swamp Palace - Compass Chest
- Swamp Palace - Map Chest
- Swamp Palace - Entrance
- Swamp Palace - Flooded Room - Left
- Swamp Palace - Flooded Room - Right
- Swamp Palace - Waterfall Room
- Swamp Palace - West Chest
type: LocationGroup
- item: Ice Rod
type: LocationGroup
locations:
- Turtle Rock - Big Chest
- Turtle Rock - Boss
- Turtle Rock - Big Key Chest
- Turtle Rock - Compass Chest
- Turtle Rock - Roller Room - Left
- Turtle Rock - Chain Chomps
- Turtle Rock - Crystaroller Room
- Turtle Rock - Roller Room - Right
- Turtle Rock - Eye Bridge - Bottom Left
- Turtle Rock - Eye Bridge - Bottom Right
- Turtle Rock - Eye Bridge - Top Left
- Turtle Rock - Eye Bridge - Top Right
- Mimic Cave
- Link's House
- Link's Uncle
- Secret Passage
- Hyrule Castle - Map Chest
- Sanctuary
- Mushroom Spot
- Lost Woods Hideout
- Blind's Hideout - Left
- Blind's Hideout - Right
- Blind's Hideout - Far Left
- Blind's Hideout - Far Right
- Blind's Hideout - Top
- Kakariko Well - Right
- Kakariko Well - Left
- Kakariko Well - Middle
- Kakariko Well - Top
- Kakariko Well - Bottom
- Chicken House
- Bottle Merchant
- Kakariko Tavern
- Maze Race
- Floodgate Chest
- Sunken Treasure
- Mini Moldorm Cave - Left
- Mini Moldorm Cave - Right
- Mini Moldorm Cave - Generous Guy
- Mini Moldorm Cave - Far Left
- Mini Moldorm Cave - Far Right
- Sahasrahla's Hut - Middle
- Sahasrahla's Hut - Right
- Sahasrahla's Hut - Left
- Ice Rod Cave
- Eastern Palace - Compass Chest
- Eastern Palace - Map Chest
- Eastern Palace - Cannonball Chest
- Aginah's Cave
- item: Big Key (Eastern Palace)
locations:
- Eastern Palace - Cannonball Chest
- Eastern Palace - Big Key Chest
- Eastern Palace - Compass Chest
- Eastern Palace - Map Chest
type: LocationGroup
- item: Big Key (Desert Palace)
locations:
- Desert Palace - Big Key Chest
- Desert Palace - Compass Chest
- Desert Palace - Map Chest
- Desert Palace - Torch
type: LocationGroup
- item: Big Key (Palace of Darkness)
locations:
- Palace of Darkness - Big Key Chest
- Palace of Darkness - Compass Chest
- Palace of Darkness - Map Chest
- Palace of Darkness - Stalfos Basement
- Palace of Darkness - Dark Basement - Right
- Palace of Darkness - Harmless Hellway
- Palace of Darkness - Shooter Room
- Palace of Darkness - The Arena - Bridge
- Palace of Darkness - The Arena - Ledge
- Palace of Darkness - Dark Basement - Left
- Palace of Darkness - Dark Maze - Bottom
- Palace of Darkness - Dark Maze - Top
type: LocationGroup
- item: Big Key (Thieves Town)
locations:
- Thieves' Town - Big Key Chest
- Thieves' Town - Compass Chest
- Thieves' Town - Map Chest
- Thieves' Town - Ambush Chest
type: LocationGroup
- item: Big Key (Misery Mire)
locations:
- Misery Mire - Big Key Chest
- Misery Mire - Compass Chest
- Misery Mire - Map Chest
- Misery Mire - Spike Chest
- Misery Mire - Main Lobby
- Misery Mire - Bridge Chest
type: LocationGroup
- item: Big Key (Turtle Rock)
locations:
- Turtle Rock - Big Key Chest
- Turtle Rock - Compass Chest
- Turtle Rock - Roller Room - Left
- Turtle Rock - Chain Chomps
- Turtle Rock - Roller Room - Right
type: LocationGroup
- item: Big Key (Ganons Tower)
locations:
- Ganons Tower - Big Key Chest
- Ganons Tower - Compass Room - Top Left
- Ganons Tower - Map Chest
- Ganons Tower - Bob's Torch
- Ganons Tower - Tile Room
- Ganons Tower - Firesnake Room
- Ganons Tower - DMs Room - Top Right
- Ganons Tower - DMs Room - Top Left
- Ganons Tower - DMs Room - Bottom Left
- Ganons Tower - DMs Room - Bottom Right
- Ganons Tower - Compass Room - Top Right
- Ganons Tower - Compass Room - Bottom Right
- Ganons Tower - Compass Room - Bottom Left
- Ganons Tower - Hope Room - Left
- Ganons Tower - Hope Room - Right
- Ganons Tower - Randomizer Room - Top Left
- Ganons Tower - Randomizer Room - Top Right
- Ganons Tower - Randomizer Room - Bottom Right
- Ganons Tower - Randomizer Room - Bottom Left
- Ganons Tower - Bob's Chest
- Ganons Tower - Big Key Room - Left
- Ganons Tower - Big Key Room - Right
type: LocationGroup
item_pool:
1:
Arrows (10): 12
Blue Boomerang: 1
Bombos: 1
Bombs (10): 1
Bombs (3): 16
Book of Mudora: 1
Boss Heart Container: 10
Bottle (Random): 4
Bug Catching Net: 1
Cane of Byrna: 1
Cane of Somaria: 1
Cape: 1
Ether: 1
Fire Rod: 1
Flippers: 1
Hammer: 1
Hookshot: 1
Ice Rod: 1
Lamp: 1
Magic Mirror: 1
Magic Powder: 1
Magic Upgrade (1/2): 1
Moon Pearl: 1
Mushroom: 1
Ocarina: 1
Pegasus Boots: 1
Piece of Heart: 24
Progressive Armor: 2
Progressive Bow: 2
Progressive Glove: 2
Progressive Shield: 3
Progressive Sword: 4
Titans Mitts: 1
Quake: 1
Red Boomerang: 1
Rupee (1): 2
Rupees (100): 1
Rupees (20): 27
Rupees (300): 5
Rupees (5): 4
Rupees (50): 7
Sanctuary Heart Container: 1
Shovel: 1
Single Arrow: 1

View File

@@ -34,6 +34,10 @@
"retro" "retro"
] ]
}, },
"boots_hint": {
"action": "store_true",
"type": "bool"
},
"swords": { "swords": {
"choices": [ "choices": [
"random", "random",

View File

@@ -24,15 +24,20 @@ class CustomSettings(object):
head, filename = os.path.split(file) head, filename = os.path.split(file)
self.relative_dir = head self.relative_dir = head
def determine_seed(self): def determine_seed(self, default_seed):
if 'meta' not in self.file_source: if 'meta' in self.file_source:
return None meta = defaultdict(lambda: None, self.file_source['meta'])
meta = defaultdict(lambda: None, self.file_source['meta']) seed = meta['seed']
seed = meta['seed'] if seed:
if seed: random.seed(seed)
random.seed(seed) return seed
return seed if default_seed is None:
return None random.seed(None)
seed = random.randint(0, 999999999)
else:
seed = default_seed
random.seed(seed)
return seed
def determine_players(self): def determine_players(self):
if 'meta' not in self.file_source: if 'meta' not in self.file_source:
@@ -43,7 +48,10 @@ class CustomSettings(object):
def adjust_args(self, args): def adjust_args(self, args):
def get_setting(value, default): def get_setting(value, default):
if value: if value:
return value if isinstance(value, dict):
return random.choices(list(value.keys()), list(value.values()), k=1)[0]
else:
return value
return default return default
if 'meta' in self.file_source: if 'meta' in self.file_source:
meta = defaultdict(lambda: None, self.file_source['meta']) meta = defaultdict(lambda: None, self.file_source['meta'])
@@ -67,6 +75,7 @@ class CustomSettings(object):
args.door_shuffle[p] = get_setting(settings['door_shuffle'], args.door_shuffle[p]) args.door_shuffle[p] = get_setting(settings['door_shuffle'], args.door_shuffle[p])
args.logic[p] = get_setting(settings['logic'], args.logic[p]) args.logic[p] = get_setting(settings['logic'], args.logic[p])
args.mode[p] = get_setting(settings['mode'], args.mode[p]) args.mode[p] = get_setting(settings['mode'], args.mode[p])
args.boots_hint[p] = get_setting(settings['boots_hint'], args.boots_hint[p])
args.swords[p] = get_setting(settings['swords'], args.swords[p]) args.swords[p] = get_setting(settings['swords'], args.swords[p])
args.flute_mode[p] = get_setting(settings['flute_mode'], args.flute_mode[p]) args.flute_mode[p] = get_setting(settings['flute_mode'], args.flute_mode[p])
args.bow_mode[p] = get_setting(settings['bow_mode'], args.bow_mode[p]) args.bow_mode[p] = get_setting(settings['bow_mode'], args.bow_mode[p])
@@ -86,6 +95,14 @@ class CustomSettings(object):
if args.pottery[p] == 'none': if args.pottery[p] == 'none':
args.pottery[p] = 'keys' args.pottery[p] = 'keys'
if args.retro[p] or args.mode[p] == 'retro':
if args.bow_mode[p] == 'progressive':
args.bow_mode[p] = 'retro'
elif args.bow_mode[p] == 'silvers':
args.bow_mode[p] = 'retro_silvers'
args.take_any[p] = 'random' if args.take_any[p] == 'none' else args.take_any[p]
args.keyshuffle[p] = 'universal'
args.mixed_travel[p] = get_setting(settings['mixed_travel'], args.mixed_travel[p]) args.mixed_travel[p] = get_setting(settings['mixed_travel'], args.mixed_travel[p])
args.standardize_palettes[p] = get_setting(settings['standardize_palettes'], args.standardize_palettes[p] = get_setting(settings['standardize_palettes'],
args.standardize_palettes[p]) args.standardize_palettes[p])
@@ -104,7 +121,8 @@ class CustomSettings(object):
if get_setting(settings['keysanity'], args.keysanity): if get_setting(settings['keysanity'], args.keysanity):
args.bigkeyshuffle[p] = True args.bigkeyshuffle[p] = True
args.keyshuffle[p] = True if args.keyshuffle[p] == 'none':
args.keyshuffle[p] = 'wild'
args.mapshuffle[p] = True args.mapshuffle[p] = True
args.compassshuffle[p] = True args.compassshuffle[p] = True
@@ -149,6 +167,11 @@ class CustomSettings(object):
return self.file_source['placements'] return self.file_source['placements']
return None return None
def get_advanced_placements(self):
if 'advanced_placements' in self.file_source:
return self.file_source['advanced_placements']
return None
def get_entrances(self): def get_entrances(self):
if 'entrances' in self.file_source: if 'entrances' in self.file_source:
return self.file_source['entrances'] return self.file_source['entrances']
@@ -174,6 +197,11 @@ class CustomSettings(object):
return self.file_source['medallions'] return self.file_source['medallions']
return None return None
def get_drops(self):
if 'drops' in self.file_source:
return self.file_source['drops']
return None
def create_from_world(self, world): def create_from_world(self, world):
self.player_range = range(1, world.players + 1) self.player_range = range(1, world.players + 1)
settings_dict, meta_dict = {}, {} settings_dict, meta_dict = {}, {}

View File

@@ -18,6 +18,8 @@ class ItemPoolConfig(object):
self.item_pool = None self.item_pool = None
self.placeholders = None self.placeholders = None
self.reserved_locations = defaultdict(set) self.reserved_locations = defaultdict(set)
self.restricted = {}
self.preferred = {}
self.recorded_choices = [] self.recorded_choices = []
@@ -380,8 +382,10 @@ def vanilla_fallback(item_to_place, locations, world):
def filter_locations(item_to_place, locations, world, vanilla_skip=False, potion=False): def filter_locations(item_to_place, locations, world, vanilla_skip=False, potion=False):
config = world.item_pool_config
item_name = 'Bottle' if item_to_place.name.startswith('Bottle') else item_to_place.name
if world.algorithm == 'vanilla_fill': if world.algorithm == 'vanilla_fill':
config, filtered = world.item_pool_config, [] filtered = []
item_name = 'Bottle' if item_to_place.name.startswith('Bottle') else item_to_place.name item_name = 'Bottle' if item_to_place.name.startswith('Bottle') else item_to_place.name
if item_name in config.static_placement[item_to_place.player]: if item_name in config.static_placement[item_to_place.player]:
restricted = config.static_placement[item_to_place.player][item_name] restricted = config.static_placement[item_to_place.player][item_name]
@@ -418,6 +422,12 @@ def filter_locations(item_to_place, locations, world, vanilla_skip=False, potion
if len(filtered) == 0: if len(filtered) == 0:
raise RuntimeError('Can\'t sell potion of a certain type due to district restriction') raise RuntimeError('Can\'t sell potion of a certain type due to district restriction')
return filtered return filtered
if (item_name, item_to_place.player) in config.restricted:
locs = config.restricted[(item_name, item_to_place.player)]
return sorted(locations, key=lambda l: 1 if l.name in locs else 0)
if (item_name, item_to_place.player) in config.preferred:
locs = config.preferred[(item_name, item_to_place.player)]
return sorted(locations, key=lambda l: 0 if l.name in locs else 1)
return locations return locations
@@ -814,3 +824,26 @@ pot_items = {
} }
valid_pot_items = {y: x for x, y in pot_items.items()} valid_pot_items = {y: x for x, y in pot_items.items()}
if __name__ == '__main__':
import yaml
from yaml.representer import Representer
advanced_placements = {'advanced_placements': {}}
player_map = advanced_placements['advanced_placements']
placement_list = []
player_map[1] = placement_list
for item, location_list in vanilla_mapping.items():
for location in location_list:
placement = {}
placement['type'] = 'LocationGroup'
placement['item'] = item
locations = placement['locations'] = []
locations.append(location)
locations.append('Random')
placement_list.append(placement)
yaml.add_representer(defaultdict, Representer.represent_dict)
with open('fillgen.yaml', 'w') as file:
yaml.dump(advanced_placements, file)