Merge branch 'OverworldShuffleDev' into OverworldShuffle

This commit is contained in:
codemann8
2025-01-20 22:46:30 -06:00
29 changed files with 213 additions and 99 deletions
+1
View File
@@ -29,6 +29,7 @@ weights/
/Players/ /Players/
/QUsb2Snes/ /QUsb2Snes/
/output/ /output/
/enemizer/
base2current.json base2current.json
+7 -2
View File
@@ -168,6 +168,7 @@ class World(object):
set_player_attr('potshuffle', False) set_player_attr('potshuffle', False)
set_player_attr('pot_contents', None) set_player_attr('pot_contents', None)
set_player_attr('pseudoboots', False) set_player_attr('pseudoboots', False)
set_player_attr('mirrorscroll', False)
set_player_attr('collection_rate', False) set_player_attr('collection_rate', False)
set_player_attr('colorizepots', True) set_player_attr('colorizepots', True)
set_player_attr('pot_pool', {}) set_player_attr('pot_pool', {})
@@ -177,6 +178,7 @@ class World(object):
set_player_attr('trap_door_mode', 'optional') set_player_attr('trap_door_mode', 'optional')
set_player_attr('key_logic_algorithm', 'partial') set_player_attr('key_logic_algorithm', 'partial')
set_player_attr('aga_randomness', True) set_player_attr('aga_randomness', True)
set_player_attr('money_balance', 100)
set_player_attr('shopsanity', False) set_player_attr('shopsanity', False)
set_player_attr('mixed_travel', 'prevent') set_player_attr('mixed_travel', 'prevent')
@@ -3059,6 +3061,7 @@ class Spoiler(object):
'potshuffle': self.world.potshuffle, 'potshuffle': self.world.potshuffle,
'shopsanity': self.world.shopsanity, 'shopsanity': self.world.shopsanity,
'pseudoboots': self.world.pseudoboots, 'pseudoboots': self.world.pseudoboots,
'mirrorscroll': self.world.mirrorscroll,
'triforcegoal': self.world.treasure_hunt_count, 'triforcegoal': self.world.treasure_hunt_count,
'triforcepool': self.world.treasure_hunt_total, 'triforcepool': self.world.treasure_hunt_total,
'race': self.world.settings.world_rep['meta']['race'], 'race': self.world.settings.world_rep['meta']['race'],
@@ -3307,6 +3310,7 @@ class Spoiler(object):
outfile.write('Enemy Logic:'.ljust(line_width) + '%s\n' % self.metadata['any_enemy_logic'][player]) outfile.write('Enemy Logic:'.ljust(line_width) + '%s\n' % self.metadata['any_enemy_logic'][player])
outfile.write('\n') outfile.write('\n')
outfile.write('Pseudoboots:'.ljust(line_width) + '%s\n' % yn(self.metadata['pseudoboots'][player])) outfile.write('Pseudoboots:'.ljust(line_width) + '%s\n' % yn(self.metadata['pseudoboots'][player]))
outfile.write('Mirror Scroll:'.ljust(line_width) + '%s\n' % yn(self.metadata['mirrorscroll'][player]))
outfile.write('Hints:'.ljust(line_width) + '%s\n' % yn(self.metadata['hints'][player])) outfile.write('Hints:'.ljust(line_width) + '%s\n' % yn(self.metadata['hints'][player]))
outfile.write('Race:'.ljust(line_width) + '%s\n' % yn(self.world.settings.world_rep['meta']['race'])) outfile.write('Race:'.ljust(line_width) + '%s\n' % yn(self.world.settings.world_rep['meta']['race']))
@@ -3681,7 +3685,7 @@ overworld_map_mode = {'default': 0, 'compass': 1, 'map': 2}
trap_door_mode = {'vanilla': 0, 'optional': 1, 'boss': 2, 'oneway': 3} trap_door_mode = {'vanilla': 0, 'optional': 1, 'boss': 2, 'oneway': 3}
key_logic_algo = {'dangerous': 0, 'partial': 1, 'strict': 2} key_logic_algo = {'dangerous': 0, 'partial': 1, 'strict': 2}
# byte 15: SSLL ??DD (skullwoods, linked_drops, 2 free bytes, door_type) # byte 15: SSLL M?DD (skullwoods, linked_drops, mirrorscroll, 1 free byte, door_type)
skullwoods_mode = {'original': 0, 'restricted': 1, 'loose': 2, 'followlinked': 3} skullwoods_mode = {'original': 0, 'restricted': 1, 'loose': 2, 'followlinked': 3}
linked_drops_mode = {'unset': 0, 'linked': 1, 'independent': 2} linked_drops_mode = {'unset': 0, 'linked': 1, 'independent': 2}
door_type_mode = {'original': 0, 'big': 1, 'all': 2, 'chaos': 3} door_type_mode = {'original': 0, 'big': 1, 'all': 2, 'chaos': 3}
@@ -3742,7 +3746,7 @@ class Settings(object):
| trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]), | trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]),
(skullwoods_mode[w.skullwoods[p]] << 6 | linked_drops_mode[w.linked_drops[p]] << 4 (skullwoods_mode[w.skullwoods[p]] << 6 | linked_drops_mode[w.linked_drops[p]] << 4
| door_type_mode[w.door_type_mode[p]]), | (0x8 if w.mirrorscroll[p] else 0) | door_type_mode[w.door_type_mode[p]]),
]) ])
return base64.b64encode(code, "+-".encode()).decode() return base64.b64encode(code, "+-".encode()).decode()
@@ -3835,6 +3839,7 @@ class Settings(object):
if len(settings) > 15: if len(settings) > 15:
args.skullwoods[p] = r(skullwoods_mode)[(settings[15] & 0xc0) >> 6] args.skullwoods[p] = r(skullwoods_mode)[(settings[15] & 0xc0) >> 6]
args.linked_drops[p] = r(linked_drops_mode)[(settings[15] & 0x30) >> 4] args.linked_drops[p] = r(linked_drops_mode)[(settings[15] & 0x30) >> 4]
args.mirrorscroll[p] = True if settings[15] & 0x8 else False
args.door_type_mode[p] = r(door_type_mode)[(settings[15] & 0x3)] args.door_type_mode[p] = r(door_type_mode)[(settings[15] & 0x3)]
+4
View File
@@ -1,5 +1,9 @@
# Changelog # Changelog
## 0.5.1.4
- Fixed incorrect ganon silvers hint
- \~Merged in DR v1.4.8.1~
## 0.5.1.3 ## 0.5.1.3
- Fixed some minor issues with mystery multiworld - Fixed some minor issues with mystery multiworld
- Corrected some generation issues with Nearby dungeon item shuffles - Corrected some generation issues with Nearby dungeon item shuffles
+5 -2
View File
@@ -139,13 +139,14 @@ def parse_cli(argv, no_defaults=False):
'triforce_max_difference', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_max_difference', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern', 'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern',
'skullwoods', 'linked_drops', 'skullwoods', 'linked_drops',
'pseudoboots', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', 'pseudoboots', 'mirrorscroll', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots', 'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor',
'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle', 'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle',
'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx', 'shuffle_sfxinstruments', 'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx', 'shuffle_sfxinstruments',
'shuffle_songinstruments', 'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode', 'shuffle_songinstruments', 'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode',
'bonk_drops', 'trap_door_mode', 'key_logic_algorithm', 'door_self_loops', 'any_enemy_logic', 'aga_randomness']: 'bonk_drops', 'trap_door_mode', 'key_logic_algorithm', 'door_self_loops', 'any_enemy_logic', 'aga_randomness',
'money_balance']:
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
if player == 1: if player == 1:
setattr(ret, name, {1: value}) setattr(ret, name, {1: value})
@@ -208,6 +209,7 @@ def parse_settings():
"overworld_map": "default", "overworld_map": "default",
"take_any": "none", "take_any": "none",
"pseudoboots": False, "pseudoboots": False,
"mirrorscroll": False,
"shuffleenemies": "none", "shuffleenemies": "none",
"shufflebosses": "none", "shufflebosses": "none",
@@ -239,6 +241,7 @@ def parse_settings():
"mixed_travel": "prevent", "mixed_travel": "prevent",
"standardize_palettes": "standardize", "standardize_palettes": "standardize",
'aga_randomness': True, 'aga_randomness': True,
'money_balance': 100,
"triforce_pool": 0, "triforce_pool": 0,
"triforce_goal": 0, "triforce_goal": 0,
+4 -2
View File
@@ -3884,8 +3884,10 @@ logical_connections = [
('TR Crystaroller Chest to Middle Barrier - Blue', 'TR Crystaroller Middle'), ('TR Crystaroller Chest to Middle Barrier - Blue', 'TR Crystaroller Middle'),
('TR Crystaroller Middle Ranged Crystal Exit', 'TR Crystaroller Middle'), ('TR Crystaroller Middle Ranged Crystal Exit', 'TR Crystaroller Middle'),
('TR Crystaroller Bottom Ranged Crystal Exit', 'TR Crystaroller Bottom'), ('TR Crystaroller Bottom Ranged Crystal Exit', 'TR Crystaroller Bottom'),
('TR Dark Ride Path', 'TR Dark Ride Ledges'), ('TR Dark Ride Normal Path', 'TR Dark Ride South Platform'),
('TR Dark Ride Ledges Path', 'TR Dark Ride'), ('TR Dark Ride Backward Path', 'TR Dark Ride North Platform'),
('TR Dark Ride Ledge Path', 'TR Dark Ride Ledges'),
('TR Dark Ride Return Path', 'TR Dark Ride South Platform'),
('TR Crystal Maze Start to Interior Barrier - Blue', 'TR Crystal Maze Interior'), ('TR Crystal Maze Start to Interior Barrier - Blue', 'TR Crystal Maze Interior'),
('TR Crystal Maze Start to Crystal', 'TR Crystal Maze Start - Crystal'), ('TR Crystal Maze Start to Crystal', 'TR Crystal Maze Start - Crystal'),
('TR Crystal Maze Start Crystal Exit', 'TR Crystal Maze Start'), ('TR Crystal Maze Start Crystal Exit', 'TR Crystal Maze Start'),
+4 -2
View File
@@ -1061,8 +1061,10 @@ def create_doors(world, player):
create_door(player, 'TR Crystaroller Down Stairs', Sprl).dir(Dn, 0x04, 0, HTH).ss(A, 0x12, 0x80, True, True).small_key().pos(0), create_door(player, 'TR Crystaroller Down Stairs', Sprl).dir(Dn, 0x04, 0, HTH).ss(A, 0x12, 0x80, True, True).small_key().pos(0),
create_door(player, 'TR Dark Ride Up Stairs', Sprl).dir(Up, 0xb5, 0, HTH).ss(A, 0x1b, 0x6c), create_door(player, 'TR Dark Ride Up Stairs', Sprl).dir(Up, 0xb5, 0, HTH).ss(A, 0x1b, 0x6c),
create_door(player, 'TR Dark Ride SW', Nrml).dir(So, 0xb5, Left, High).trap(0x4).pos(0).portal(Z, 0x22), create_door(player, 'TR Dark Ride SW', Nrml).dir(So, 0xb5, Left, High).trap(0x4).pos(0).portal(Z, 0x22),
create_door(player, 'TR Dark Ride Path', Lgcl), create_door(player, 'TR Dark Ride Normal Path', Lgcl),
create_door(player, 'TR Dark Ride Ledges Path', Lgcl), create_door(player, 'TR Dark Ride Ledge Path', Lgcl),
create_door(player, 'TR Dark Ride Backward Path', Lgcl),
create_door(player, 'TR Dark Ride Return Path', Lgcl),
create_door(player, 'TR Dash Bridge NW', Nrml).dir(No, 0xc5, Left, High).pos(1), create_door(player, 'TR Dash Bridge NW', Nrml).dir(No, 0xc5, Left, High).pos(1),
create_door(player, 'TR Dash Bridge SW', Nrml).dir(So, 0xc5, Left, High).pos(2).portal(Z, 0x02), create_door(player, 'TR Dash Bridge SW', Nrml).dir(So, 0xc5, Left, High).pos(2).portal(Z, 0x02),
create_door(player, 'TR Dash Bridge WS', Nrml).dir(We, 0xc5, Bot, High).small_key().pos(0), create_door(player, 'TR Dash Bridge WS', Nrml).dir(We, 0xc5, Bot, High).small_key().pos(0),
+2 -1
View File
@@ -181,7 +181,8 @@ tr_regions = [
'TR Big View', 'TR Big Chest', 'TR Big Chest Entrance', 'TR Lazy Eyes', 'TR Dash Room', 'TR Tongue Pull', 'TR Big View', 'TR Big Chest', 'TR Big Chest Entrance', 'TR Lazy Eyes', 'TR Dash Room', 'TR Tongue Pull',
'TR Rupees', 'TR Crystaroller Bottom', 'TR Crystaroller Middle', 'TR Crystaroller Top', 'TR Rupees', 'TR Crystaroller Bottom', 'TR Crystaroller Middle', 'TR Crystaroller Top',
'TR Crystaroller Top - Crystal', 'TR Crystaroller Chest', 'TR Crystaroller Middle - Ranged Crystal', 'TR Crystaroller Top - Crystal', 'TR Crystaroller Chest', 'TR Crystaroller Middle - Ranged Crystal',
'TR Crystaroller Bottom - Ranged Crystal', 'TR Dark Ride', 'TR Dark Ride Ledges', 'TR Dash Bridge', 'TR Eye Bridge', 'TR Crystaroller Bottom - Ranged Crystal', 'TR Dark Ride North Platform', 'TR Dark Ride South Platform',
'TR Dark Ride Ledges', 'TR Dash Bridge', 'TR Eye Bridge',
'TR Crystal Maze Start', 'TR Crystal Maze Start - Crystal', 'TR Crystal Maze Interior', 'TR Crystal Maze End', 'TR Crystal Maze Start', 'TR Crystal Maze Start - Crystal', 'TR Crystal Maze Interior', 'TR Crystal Maze End',
'TR Crystal Maze End - Ranged Crystal', 'TR Final Abyss Balcony', 'TR Final Abyss Ledge', 'TR Boss', 'TR Boss Spoils', 'TR Crystal Maze End - Ranged Crystal', 'TR Final Abyss Balcony', 'TR Final Abyss Ledge', 'TR Boss', 'TR Boss Spoils',
'Turtle Rock Main Portal', 'Turtle Rock Lazy Eyes Portal', 'Turtle Rock Chest Portal', 'Turtle Rock Main Portal', 'Turtle Rock Lazy Eyes Portal', 'Turtle Rock Chest Portal',
+7 -6
View File
@@ -79,7 +79,6 @@ def fill_dungeons_restrictive(world, shuffled_locations):
break break
for wix in reversed(to_remove): for wix in reversed(to_remove):
del smalls[wix] del smalls[wix]
# remove 2 swamp locations from pool # remove 2 swamp locations from pool
hybrid_locations = [] hybrid_locations = []
to_remove = [] to_remove = []
@@ -91,7 +90,6 @@ def fill_dungeons_restrictive(world, shuffled_locations):
break break
for i in reversed(to_remove): for i in reversed(to_remove):
shuffled_locations.pop(i) shuffled_locations.pop(i)
# place 2 HMG keys # place 2 HMG keys
hybrid_state_base = all_state_base.copy() hybrid_state_base = all_state_base.copy()
for x in bigs + smalls + prizes + others: for x in bigs + smalls + prizes + others:
@@ -1122,14 +1120,16 @@ def balance_money_progression(world):
solvent = set() solvent = set()
insolvent = set() insolvent = set()
for player in range(1, world.players+1): for player in range(1, world.players+1):
if wallet[player] >= sphere_costs[player] >= 0: modifier = world.money_balance[player]/100
if wallet[player] >= sphere_costs[player] * modifier >= 0:
solvent.add(player) solvent.add(player)
if sphere_costs[player] > 0 and sphere_costs[player] > wallet[player]: if sphere_costs[player] > 0 and sphere_costs[player] * modifier > wallet[player]:
insolvent.add(player) insolvent.add(player)
if len([p for p in solvent if len(locked_by_money[p]) > 0]) == 0: if len([p for p in solvent if len(locked_by_money[p]) > 0]) == 0:
if len(insolvent) > 0: if len(insolvent) > 0:
target_player = min(insolvent, key=lambda p: sphere_costs[p]-wallet[p]) target_player = min(insolvent, key=lambda p: sphere_costs[p]-wallet[p])
difference = sphere_costs[target_player]-wallet[target_player] target_modifier = world.money_balance[target_player]/100
difference = sphere_costs[target_player] * target_modifier - wallet[target_player]
logger.debug(f'Money balancing needed: Player {target_player} short {difference}') logger.debug(f'Money balancing needed: Player {target_player} short {difference}')
else: else:
difference = 0 difference = 0
@@ -1169,7 +1169,8 @@ def balance_money_progression(world):
solvent.add(target_player) solvent.add(target_player)
# apply solvency # apply solvency
for player in solvent: for player in solvent:
wallet[player] -= sphere_costs[player] modifier = world.money_balance[player]/100
wallet[player] -= sphere_costs[player] * modifier
for location in locked_by_money[player]: for location in locked_by_money[player]:
if isinstance(location, str) and location == 'Kiki': if isinstance(location, str) and location == 'Kiki':
kiki_paid[player] = True kiki_paid[player] = True
+1 -1
View File
@@ -124,7 +124,7 @@ class InitialSram:
if startingstate.has('Beat Agahnim 1', player): if startingstate.has('Beat Agahnim 1', player):
self.pre_open_lumberjack() self.pre_open_lumberjack()
if world.mode[player] == 'standard': if world.mode[player] == 'standard':
self.set_progress_indicator(0x80) self.set_progress_indicator(0x80) # todo: probably missing some code rom side for this
else: else:
self.set_progress_indicator(0x03) self.set_progress_indicator(0x03)
+24 -36
View File
@@ -837,7 +837,8 @@ def customize_shops(world, player):
if retro_bow and item.name == 'Single Arrow': if retro_bow and item.name == 'Single Arrow':
price = 80 price = 80
# randomize price # randomize price
shop.add_inventory(idx, item.name, randomize_price(price), max_repeat, player=item.player) price = final_price(loc, price, world, player)
shop.add_inventory(idx, item.name, price, max_repeat, player=item.player)
if item.name in cap_replacements and shop_name not in retro_shops and item.player == player: if item.name in cap_replacements and shop_name not in retro_shops and item.player == player:
possible_replacements.append((shop, idx, location, item)) possible_replacements.append((shop, idx, location, item))
# randomize shopkeeper # randomize shopkeeper
@@ -854,8 +855,10 @@ def customize_shops(world, player):
if len(choices) > 0: if len(choices) > 0:
shop, idx, loc, item = random.choice(choices) shop, idx, loc, item = random.choice(choices)
upgrade = ItemFactory('Bomb Upgrade (+5)', player) upgrade = ItemFactory('Bomb Upgrade (+5)', player)
shop.add_inventory(idx, upgrade.name, randomize_price(upgrade.price), 6, up_price = final_price(loc.name, upgrade.price, world, player)
item.name, randomize_price(item.price), player=item.player) rep_price = final_price(loc.name, item.price, world, player)
shop.add_inventory(idx, upgrade.name, up_price, 6,
item.name, rep_price, player=item.player)
loc.item = upgrade loc.item = upgrade
upgrade.location = loc upgrade.location = loc
if not found_arrow_upgrade and len(possible_replacements) > 0: if not found_arrow_upgrade and len(possible_replacements) > 0:
@@ -866,8 +869,10 @@ def customize_shops(world, player):
if len(choices) > 0: if len(choices) > 0:
shop, idx, loc, item = random.choice(choices) shop, idx, loc, item = random.choice(choices)
upgrade = ItemFactory('Arrow Upgrade (+5)', player) upgrade = ItemFactory('Arrow Upgrade (+5)', player)
shop.add_inventory(idx, upgrade.name, randomize_price(upgrade.price), 6, up_price = final_price(loc.name, upgrade.price, world, player)
item.name, randomize_price(item.price), player=item.player) rep_price = final_price(loc.name, item.price, world, player)
shop.add_inventory(idx, upgrade.name, up_price, 6,
item.name, rep_price, player=item.player)
loc.item = upgrade loc.item = upgrade
upgrade.location = loc upgrade.location = loc
change_shop_items_to_rupees(world, player, shops_to_customize) change_shop_items_to_rupees(world, player, shops_to_customize)
@@ -875,6 +880,15 @@ def customize_shops(world, player):
check_hints(world, player) check_hints(world, player)
def final_price(location, price, world, player):
if world.customizer and world.customizer.get_prices(player):
custom_prices = world.customizer.get_prices(player)
if location in custom_prices:
# todo: validate valid price
return custom_prices[location]
return randomize_price(price)
def randomize_price(price): def randomize_price(price):
half_price = price // 2 half_price = price // 2
max_price = price - half_price max_price = price - half_price
@@ -914,12 +928,16 @@ def balance_prices(world, player):
shop_locations = [] shop_locations = []
for shop, loc_list in shop_to_location_table.items(): for shop, loc_list in shop_to_location_table.items():
for loc in loc_list: for loc in loc_list:
if world.customizer and world.customizer.get_prices(player) and loc in world.customizer.get_prices(player):
needed_money += world.customizer.get_prices(player)[loc]
continue # considered a fixed price and shouldn't be altered
loc = world.get_location(loc, player) loc = world.get_location(loc, player)
shop_locations.append(loc) shop_locations.append(loc)
slot = shop_to_location_table[loc.parent_region.name].index(loc.name) slot = shop_to_location_table[loc.parent_region.name].index(loc.name)
needed_money += loc.parent_region.shop.inventory[slot]['price'] needed_money += loc.parent_region.shop.inventory[slot]['price']
target = available_money - needed_money modifier = world.money_balance[player]/100
target = available_money - needed_money * modifier
# remove the first set of shops from consideration (or used them for discounting) # remove the first set of shops from consideration (or used them for discounting)
state, done = CollectionState(world), False state, done = CollectionState(world), False
unchecked_locations = world.get_locations().copy() unchecked_locations = world.get_locations().copy()
@@ -1255,32 +1273,11 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer
assert loc not in placed_items assert loc not in placed_items
placed_items[loc] = item placed_items[loc] = item
# Correct for insanely oversized item counts and take initial steps to handle undersized pools.
# Bow to Silver Arrows Upgrade, including Generic Keys & Rupoors
for x in [*range(0, 69)]:
key = CONST.CUSTOMITEMS[x]
if customitemarray[key] > total_items_to_place:
customitemarray[key] = total_items_to_place
# Triforce
if customitemarray["triforce"] > total_items_to_place:
customitemarray["triforce"] = total_items_to_place
# Triforce Pieces # Triforce Pieces
if goal in ['triforcehunt', 'trinity', 'ganonhunt']: if goal in ['triforcehunt', 'trinity', 'ganonhunt']:
g, t = set_default_triforce(goal, customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"]) g, t = set_default_triforce(goal, customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"])
customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"] = g, t customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"] = g, t
itemtotal = 0
# Bow to Silver Arrows Upgrade, including Generic Keys & Rupoors
for x in [*range(0, 66 + 1), 68, 69]:
key = CONST.CUSTOMITEMS[x]
itemtotal = itemtotal + customitemarray[key]
# Triforce
itemtotal = itemtotal + customitemarray["triforce"]
# Generic Keys
itemtotal = itemtotal + customitemarray["generickeys"]
customitems = [ customitems = [
"Bow", "Silver Arrows", "Blue Boomerang", "Red Boomerang", "Hookshot", "Mushroom", "Magic Powder", "Bow", "Silver Arrows", "Blue Boomerang", "Red Boomerang", "Hookshot", "Mushroom", "Magic Powder",
"Fire Rod", "Ice Rod", "Bombos", "Ether", "Quake", "Fire Rod", "Ice Rod", "Bombos", "Ether", "Quake",
@@ -1322,7 +1319,6 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer
and (goal in ['triforcehunt', 'trinity', 'ganonhunt']) and (customitemarray["triforce"] == 0)): and (goal in ['triforcehunt', 'trinity', 'ganonhunt']) and (customitemarray["triforce"] == 0)):
extrapieces = treasure_hunt_count - customitemarray["triforcepieces"] extrapieces = treasure_hunt_count - customitemarray["triforcepieces"]
pool.extend(['Triforce Piece'] * extrapieces) pool.extend(['Triforce Piece'] * extrapieces)
itemtotal = itemtotal + extrapieces
if timer in ['display', 'timed', 'timed-countdown']: if timer in ['display', 'timed', 'timed-countdown']:
clock_mode = 'countdown' if timer == 'timed-countdown' else 'stopwatch' clock_mode = 'countdown' if timer == 'timed-countdown' else 'stopwatch'
@@ -1333,7 +1329,6 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer
if goal in ['pedestal', 'trinity']: if goal in ['pedestal', 'trinity']:
place_item('Master Sword Pedestal', 'Triforce') place_item('Master Sword Pedestal', 'Triforce')
itemtotal = itemtotal + 1
if mode == 'standard': if mode == 'standard':
if world.keyshuffle[player] == 'universal': if world.keyshuffle[player] == 'universal':
@@ -1350,13 +1345,6 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer
pool.extend(['Magic Mirror'] * customitemarray["mirror"]) pool.extend(['Magic Mirror'] * customitemarray["mirror"])
pool.extend(['Moon Pearl'] * customitemarray["pearl"]) pool.extend(['Moon Pearl'] * customitemarray["pearl"])
if world.keyshuffle[player] == 'universal':
itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in Retro Mode
if itemtotal < total_items_to_place:
nothings = total_items_to_place - itemtotal
# print("Placing " + str(nothings) + " Nothings")
pool.extend(['Nothing'] * nothings)
start_inventory = [x for x in world.precollected_items if x.player == player] start_inventory = [x for x in world.precollected_items if x.player == player]
if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and all(x.name != 'Pegasus Boots' for x in start_inventory): if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and all(x.name != 'Pegasus Boots' for x in start_inventory):
precollected_items.append('Pegasus Boots') precollected_items.append('Pegasus Boots')
+7 -1
View File
@@ -814,7 +814,13 @@ def key_wasted(new_door, old_door, old_counter, new_counter, key_layout, world,
def find_next_counter(new_door, old_counter, key_layout, prize_flag=None): def find_next_counter(new_door, old_counter, key_layout, prize_flag=None):
proposed_doors = {**old_counter.open_doors, **dict.fromkeys([new_door, new_door.dest])} prop_doors = next((item_or_tuple for item_or_tuple in key_layout.proposal
if new_door == item_or_tuple or (isinstance(item_or_tuple, tuple) and new_door in item_or_tuple)), None)
if prop_doors:
prop_doors = list(prop_doors) if isinstance(prop_doors, tuple) else [prop_doors]
proposed_doors = {**old_counter.open_doors, **dict.fromkeys(prop_doors)}
else:
proposed_doors = {**old_counter.open_doors}
bk_open = old_counter.big_key_opened or new_door.bigKey bk_open = old_counter.big_key_opened or new_door.bigKey
prize_flag = prize_flag if prize_flag else old_counter.prize_doors_opened prize_flag = prize_flag if prize_flag else old_counter.prize_doors_opened
return find_counter(proposed_doors, bk_open, key_layout, prize_flag) return find_counter(proposed_doors, bk_open, key_layout, prize_flag)
+17 -1
View File
@@ -40,7 +40,7 @@ from source.enemizer.DamageTables import DamageTable
from source.enemizer.Enemizer import randomize_enemies from source.enemizer.Enemizer import randomize_enemies
from source.rom.DataTables import init_data_tables from source.rom.DataTables import init_data_tables
version_number = '1.4.7.2' version_number = '1.4.8.1'
version_branch = '-u' version_branch = '-u'
__version__ = f'{version_number}{version_branch}' __version__ = f'{version_number}{version_branch}'
@@ -501,12 +501,14 @@ def init_world(args, fish):
world.skullwoods = args.skullwoods.copy() world.skullwoods = args.skullwoods.copy()
world.linked_drops = args.linked_drops.copy() world.linked_drops = args.linked_drops.copy()
world.pseudoboots = args.pseudoboots.copy() world.pseudoboots = args.pseudoboots.copy()
world.mirrorscroll = args.mirrorscroll.copy()
world.overworld_map = args.overworld_map.copy() world.overworld_map = args.overworld_map.copy()
world.take_any = args.take_any.copy() world.take_any = args.take_any.copy()
world.restrict_boss_items = args.restrict_boss_items.copy() world.restrict_boss_items = args.restrict_boss_items.copy()
world.collection_rate = args.collection_rate.copy() world.collection_rate = args.collection_rate.copy()
world.colorizepots = args.colorizepots.copy() world.colorizepots = args.colorizepots.copy()
world.aga_randomness = args.aga_randomness.copy() world.aga_randomness = args.aga_randomness.copy()
world.money_balance = args.money_balance.copy()
return world return world
@@ -589,7 +591,13 @@ def copy_world(world):
ret.shuffle_bonk_drops = world.shuffle_bonk_drops.copy() ret.shuffle_bonk_drops = world.shuffle_bonk_drops.copy()
ret.open_pyramid = world.open_pyramid.copy() ret.open_pyramid = world.open_pyramid.copy()
ret.shufflelinks = world.shufflelinks.copy() ret.shufflelinks = world.shufflelinks.copy()
ret.shuffletavern = world.shuffletavern.copy()
ret.shuffle_ganon = world.shuffle_ganon.copy() ret.shuffle_ganon = world.shuffle_ganon.copy()
ret.skullwoods = world.skullwoods.copy()
ret.linked_drops = world.linked_drops.copy()
ret.pseudoboots = world.pseudoboots.copy()
ret.mirrorscroll = world.mirrorscroll.copy()
ret.overworld_map = world.overworld_map.copy()
ret.take_any = world.take_any.copy() ret.take_any = world.take_any.copy()
ret.boss_shuffle = world.boss_shuffle.copy() ret.boss_shuffle = world.boss_shuffle.copy()
ret.enemy_shuffle = world.enemy_shuffle.copy() ret.enemy_shuffle = world.enemy_shuffle.copy()
@@ -604,6 +612,7 @@ def copy_world(world):
ret.trap_door_mode = world.trap_door_mode.copy() ret.trap_door_mode = world.trap_door_mode.copy()
ret.key_logic_algorithm = world.key_logic_algorithm.copy() ret.key_logic_algorithm = world.key_logic_algorithm.copy()
ret.aga_randomness = world.aga_randomness.copy() ret.aga_randomness = world.aga_randomness.copy()
ret.money_balance = world.money_balance.copy()
ret.experimental = world.experimental.copy() ret.experimental = world.experimental.copy()
ret.shopsanity = world.shopsanity.copy() ret.shopsanity = world.shopsanity.copy()
ret.dropshuffle = world.dropshuffle.copy() ret.dropshuffle = world.dropshuffle.copy()
@@ -802,7 +811,13 @@ def copy_world_premature(world, player):
ret.shuffle_bonk_drops = world.shuffle_bonk_drops.copy() ret.shuffle_bonk_drops = world.shuffle_bonk_drops.copy()
ret.open_pyramid = world.open_pyramid.copy() ret.open_pyramid = world.open_pyramid.copy()
ret.shufflelinks = world.shufflelinks.copy() ret.shufflelinks = world.shufflelinks.copy()
ret.shuffletavern = world.shuffletavern.copy()
ret.shuffle_ganon = world.shuffle_ganon.copy() ret.shuffle_ganon = world.shuffle_ganon.copy()
ret.skullwoods = world.skullwoods.copy()
ret.linked_drops = world.linked_drops.copy()
ret.pseudoboots = world.pseudoboots.copy()
ret.mirrorscroll = world.mirrorscroll.copy()
ret.overworld_map = world.overworld_map.copy()
ret.take_any = world.take_any.copy() ret.take_any = world.take_any.copy()
ret.boss_shuffle = world.boss_shuffle.copy() ret.boss_shuffle = world.boss_shuffle.copy()
ret.enemy_shuffle = world.enemy_shuffle.copy() ret.enemy_shuffle = world.enemy_shuffle.copy()
@@ -817,6 +832,7 @@ def copy_world_premature(world, player):
ret.trap_door_mode = world.trap_door_mode.copy() ret.trap_door_mode = world.trap_door_mode.copy()
ret.key_logic_algorithm = world.key_logic_algorithm.copy() ret.key_logic_algorithm = world.key_logic_algorithm.copy()
ret.aga_randomness = world.aga_randomness.copy() ret.aga_randomness = world.aga_randomness.copy()
ret.money_balance = world.money_balance.copy()
ret.experimental = world.experimental.copy() ret.experimental = world.experimental.copy()
ret.shopsanity = world.shopsanity.copy() ret.shopsanity = world.shopsanity.copy()
ret.dropshuffle = world.dropshuffle.copy() ret.dropshuffle = world.dropshuffle.copy()
+1 -1
View File
@@ -8,7 +8,7 @@ from OWEdges import OWTileRegions, OWEdgeGroups, OWEdgeGroupsTerrain, OWExitType
from OverworldGlitchRules import create_owg_connections from OverworldGlitchRules import create_owg_connections
from Utils import bidict from Utils import bidict
version_number = '0.5.1.3' version_number = '0.5.1.4'
# branch indicator is intentionally different across branches # branch indicator is intentionally different across branches
version_branch = '' version_branch = ''
+14 -16
View File
@@ -141,20 +141,18 @@ These are now independent of retro mode and have three options: None, Random, an
# Patch Notes # Patch Notes
* 1.4.7.2 * 1.4.8.1
- Fixed an issue with shuffle_ganon/fix_gtower_exit causing a generation failure - Fixed broken doors generation
- Fixed bomb/arrow upgrade ignoring custom pricing
- Extended `money_balance` to apply to price balancing for non-custom shops.
* 1.4.8
- New option: Mirror Scroll - to add the item to the starting inventory in non-doors modes (Thanks Telethar!)
- Customizer: Ability to customize shop prices and control money balancing. `money_balance` is a percentage betwen 0 and 100 that attempts to ensure you have that much percentage of money available for purchases. (100 is default, 0 essentially ignores money considerations)
- Fixed a key logic bug with decoupled doors when a big key door leads to a small key door (the small key door was missing appropriate logic)
- Fixed an ER bug where Bonk Fairy could be used for a mandatory connector in standard mode (boots could allow escape to be skipped)
- Fixed an issue with flute activation in rain mode. (thanks Codemann!)
- Fixed an issue with enemies in TR Dark Ride room not requiring Somaria. (Refactored the room for decoupled logic better)
- More HMG fixes by Muffins - More HMG fixes by Muffins
* 1.4.7.1 - Fixed an issue with multi-player HMG
- Fixed an issue with the repaired "beemizer" setting not being backwards compatible - Fixed an issue limiting number of items specified in the item pool on the GUI
* 1.4.7 - Minor documentation fixes (thanks Codemann!)
- Fixed generation error with Big Key in starting inventory (thanks Cody!)
- HMG/NL logic fixes by Muffins
- Enemizer: Disabled Walking Zora in the UW due to crash with Swamola (they ignore a lot of collison anyway)
- Enemizer: Fixed an issue with enemizer bush sprites
- Enemizer: Banned new Mimics from being the randomized bush sprite due to crash
- "Beatable" or "accessibility: none" can now use randomized trap doors to seal off entire parts of dungeons (was intended, bug prevented the logic skip)
- Logic error with enemizer and standard should use new enemy logic rules
- Fixed a bug with the inconsistent treatment of the beemizer setting
- Fixed an issue with returning Blacksmith in Simple shuffle (when blacksmith is at Link's House)
- Fixed an issue with dark sanctuary spawn at tavern north door (thanks Codemann!)
- Various enemy bans for the last few months
+3 -2
View File
@@ -907,8 +907,9 @@ def create_dungeon_regions(world, player):
create_dungeon_region(player, 'TR Crystaroller Chest', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['TR Crystaroller Chest to Middle Barrier - Blue']), create_dungeon_region(player, 'TR Crystaroller Chest', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['TR Crystaroller Chest to Middle Barrier - Blue']),
create_dungeon_region(player, 'TR Crystaroller Middle - Ranged Crystal', 'Turtle Rock', None, ['TR Crystaroller Middle Ranged Crystal Exit']), create_dungeon_region(player, 'TR Crystaroller Middle - Ranged Crystal', 'Turtle Rock', None, ['TR Crystaroller Middle Ranged Crystal Exit']),
create_dungeon_region(player, 'TR Crystaroller Bottom - Ranged Crystal', 'Turtle Rock', None, ['TR Crystaroller Bottom Ranged Crystal Exit']), create_dungeon_region(player, 'TR Crystaroller Bottom - Ranged Crystal', 'Turtle Rock', None, ['TR Crystaroller Bottom Ranged Crystal Exit']),
create_dungeon_region(player, 'TR Dark Ride', 'Turtle Rock', None, ['TR Dark Ride Up Stairs', 'TR Dark Ride SW', 'TR Dark Ride Path']), create_dungeon_region(player, 'TR Dark Ride North Platform', 'Turtle Rock', None, ['TR Dark Ride Up Stairs', 'TR Dark Ride Normal Path', 'TR Dark Ride Ledge Path']),
create_dungeon_region(player, 'TR Dark Ride Ledges', 'Turtle Rock', None, ['TR Dark Ride Ledges Path']), create_dungeon_region(player, 'TR Dark Ride South Platform', 'Turtle Rock', None, ['TR Dark Ride SW', 'TR Dark Ride Backward Path']),
create_dungeon_region(player, 'TR Dark Ride Ledges', 'Turtle Rock', None, ['TR Dark Ride Return Path']),
create_dungeon_region(player, 'TR Dash Bridge', 'Turtle Rock', None, ['TR Dash Bridge NW', 'TR Dash Bridge SW', 'TR Dash Bridge WS']), create_dungeon_region(player, 'TR Dash Bridge', 'Turtle Rock', None, ['TR Dash Bridge NW', 'TR Dash Bridge SW', 'TR Dash Bridge WS']),
create_dungeon_region(player, 'TR Eye Bridge', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', create_dungeon_region(player, 'TR Eye Bridge', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'], 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
+6 -2
View File
@@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'a84f59e5e76492f6a5693835823e6292' RANDOMIZERBASEHASH = 'b439d99c7f41914eedb9f4097ecc8cc7'
class JsonRom(object): class JsonRom(object):
@@ -690,7 +690,11 @@ def patch_rom(world, rom, player, team, is_mystery=False):
rom.write_byte(0x157D0, exit.target) rom.write_byte(0x157D0, exit.target)
# setup dr option flags based on experimental, etc. # setup dr option flags based on experimental, etc.
dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' else DROptions.Town_Portal dr_flags = DROptions.NoOptions
if world.mirrorscroll[player] or world.doorShuffle[player] != 'vanilla':
dr_flags |= DROptions.Town_Portal
if world.doorShuffle[player] == 'vanilla':
dr_flags |= DROptions.Eternal_Mini_Bosses
if world.doorShuffle[player] not in ['vanilla', 'basic']: if world.doorShuffle[player] not in ['vanilla', 'basic']:
dr_flags |= DROptions.Map_Info dr_flags |= DROptions.Map_Info
if ((world.collection_rate[player] or world.goal[player] == 'completionist') if ((world.collection_rate[player] or world.goal[player] == 'completionist')
+11 -9
View File
@@ -601,10 +601,11 @@ def global_rules(world, player):
lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player)) lambda state: state.has('Cane of Somaria', player) and state.has('Fire Rod', player))
set_rule(world.get_entrance('TR Big Chest Entrance Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player)) set_rule(world.get_entrance('TR Big Chest Entrance Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has('Hookshot', player))
set_rule(world.get_entrance('TR Big Chest Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has_Boots(player)) set_rule(world.get_entrance('TR Big Chest Gap', player), lambda state: state.has('Cane of Somaria', player) or state.has_Boots(player))
set_rule(world.get_entrance('TR Dark Ride Up Stairs', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Dark Ride SW', player), lambda state: state.has('Cane of Somaria', player)) # due to needing the switch
set_rule(world.get_entrance('TR Dark Ride SW', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Dark Ride Normal Path', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('TR Dark Ride Path', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Dark Ride Backward Path', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('TR Dark Ride Ledges Path', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Dark Ride Return Path', player), lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('TR Dark Ride Ledge Path', player), lambda state: state.has('Cane of Somaria', player))
for location in world.get_region('TR Dark Ride Ledges', player).locations: for location in world.get_region('TR Dark Ride Ledges', player).locations:
set_rule(location, lambda state: state.has('Cane of Somaria', player)) set_rule(location, lambda state: state.has('Cane of Somaria', player))
set_rule(world.get_entrance('TR Final Abyss Balcony Path', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Final Abyss Balcony Path', player), lambda state: state.has('Cane of Somaria', player))
@@ -1385,8 +1386,9 @@ def add_conditional_lamps(world, player):
add_lamp_requirement(spot, player) add_lamp_requirement(spot, player)
dark_rooms = { dark_rooms = {
'TR Dark Ride': {'sewer': False, 'entrances': ['TR Dark Ride Up Stairs', 'TR Dark Ride SW', 'TR Dark Ride Path'], 'locations': []}, 'TR Dark Ride North Platform': {'sewer': False, 'entrances': ['TR Dark Ride Up Stairs', 'TR Dark Ride Normal Path', 'TR Dark Ride Ledge Path'], 'locations': []},
'TR Dark Ride Ledges': {'sewer': False, 'entrances': ['TR Dark Ride Ledges Path'], 'locations': []}, 'TR Dark Ride South Platform': {'sewer': False, 'entrances': ['TR Dark Ride SW', 'TR Dark Ride Backward Path'], 'locations': []},
'TR Dark Ride Ledges': {'sewer': False, 'entrances': ['TR Dark Ride Return Path'], 'locations': []},
'Mire Dark Shooters': {'sewer': False, 'entrances': ['Mire Dark Shooters Up Stairs', 'Mire Dark Shooters SW', 'Mire Dark Shooters SE'], 'locations': []}, 'Mire Dark Shooters': {'sewer': False, 'entrances': ['Mire Dark Shooters Up Stairs', 'Mire Dark Shooters SW', 'Mire Dark Shooters SE'], 'locations': []},
'Mire Key Rupees': {'sewer': False, 'entrances': ['Mire Key Rupees NE'], 'locations': []}, 'Mire Key Rupees': {'sewer': False, 'entrances': ['Mire Key Rupees NE'], 'locations': []},
'Mire Block X': {'sewer': False, 'entrances': ['Mire Block X NW', 'Mire Block X WS'], 'locations': []}, 'Mire Block X': {'sewer': False, 'entrances': ['Mire Block X NW', 'Mire Block X WS'], 'locations': []},
@@ -1920,7 +1922,7 @@ bunny_revivable_entrances = {
"Ice Many Pots", "Mire South Fish", "Mire Right Bridge", "Mire Left Bridge", "Ice Many Pots", "Mire South Fish", "Mire Right Bridge", "Mire Left Bridge",
"TR Boss", "Eastern Hint Tile Blocked Path", "Thieves Spike Switch", "TR Boss", "Eastern Hint Tile Blocked Path", "Thieves Spike Switch",
"Thieves Boss", "Mire Spike Barrier", "Mire Cross", "Mire Hidden Shooters", "Thieves Boss", "Mire Spike Barrier", "Mire Cross", "Mire Hidden Shooters",
"Mire Spikes", "TR Final Abyss Balcony", "TR Dark Ride", "TR Pokey 1", "TR Tile Room", "Mire Spikes", "TR Final Abyss Balcony", "TR Dark Ride South Platform", "TR Pokey 1", "TR Tile Room",
"TR Roller Room", "Eastern Cannonball", "Thieves Hallway", "Ice Switch Room", "TR Roller Room", "Eastern Cannonball", "Thieves Hallway", "Ice Switch Room",
"Mire Tile Room", "Mire Conveyor Crystal", "Mire Hub", "TR Dash Bridge", "Mire Tile Room", "Mire Conveyor Crystal", "Mire Hub", "TR Dash Bridge",
"TR Hub", "Eastern Boss", "Eastern Lobby", "Thieves Ambush", "TR Hub", "Eastern Boss", "Eastern Lobby", "Thieves Ambush",
@@ -1988,8 +1990,8 @@ bunny_impassible_doors = {
'TR Lobby Ledge Gap', 'TR Hub SW', 'TR Hub SE', 'TR Hub ES', 'TR Hub EN', 'TR Hub NW', 'TR Hub NE', 'TR Hub Path', 'TR Lobby Ledge Gap', 'TR Hub SW', 'TR Hub SE', 'TR Hub ES', 'TR Hub EN', 'TR Hub NW', 'TR Hub NE', 'TR Hub Path',
'TR Hub Ledges Path', 'TR Torches NW', 'TR Pokey 2 Bottom to Top Barrier - Blue', 'TR Hub Ledges Path', 'TR Torches NW', 'TR Pokey 2 Bottom to Top Barrier - Blue',
'TR Pokey 2 Top to Bottom Barrier - Blue', 'TR Twin Pokeys SW', 'TR Twin Pokeys EN', 'TR Big Chest Gap', 'TR Pokey 2 Top to Bottom Barrier - Blue', 'TR Twin Pokeys SW', 'TR Twin Pokeys EN', 'TR Big Chest Gap',
'TR Big Chest Entrance Gap', 'TR Lazy Eyes ES', 'TR Tongue Pull WS', 'TR Tongue Pull NE', 'TR Dark Ride Up Stairs', 'TR Big Chest Entrance Gap', 'TR Lazy Eyes ES', 'TR Tongue Pull WS', 'TR Tongue Pull NE', 'TR Dark Ride SW', # due to needing the switch
'TR Dark Ride SW', 'TR Dark Ride Path', 'TR Dark Ride Ledges Path', 'TR Dark Ride Normal Path', 'TR Dark Ride Ledge Path', 'TR Dark Ride Backward Path', 'TR Dark Ride Return Path',
'TR Crystal Maze Start to Interior Barrier - Blue', 'TR Crystal Maze End to Interior Barrier - Blue', 'TR Crystal Maze Start to Interior Barrier - Blue', 'TR Crystal Maze End to Interior Barrier - Blue',
'TR Final Abyss Balcony Path', 'TR Final Abyss Ledge Path', 'GT Hope Room EN', 'GT Blocked Stairs Block Path', 'TR Final Abyss Balcony Path', 'TR Final Abyss Ledge Path', 'GT Hope Room EN', 'GT Blocked Stairs Block Path',
'GT Bob\'s Room Hole', 'GT Speed Torch SE', 'GT Speed Torch South Path', 'GT Speed Torch North Path', 'GT Bob\'s Room Hole', 'GT Speed Torch SE', 'GT Speed Torch South Path', 'GT Speed Torch North Path',
Binary file not shown.
+22
View File
@@ -46,6 +46,13 @@ Player 1's settings will be determined by rolling the mystery weights and player
Start inventory is not supported here. It has a separate section. Start inventory is not supported here. It has a separate section.
#### Extra Settings
Some settings are intended to only be accessed using the customizer:
* `aga_randomness` setting this to false, turns off blue balls and ganon warp randomness
* `money_balance` this is a percent (0-100). For numbers less than 100, both price balancing and money balancing will only attempt to ensure the player has access to only a percentage of the required funds. When 0, this should effectively disable money balancing. Grinding for rupees may be necessary whenever this is less than 100.
###### Not Yet Implemented ###### Not Yet Implemented
Rom/Adjust flags like sprite, quickswap are not outputing with the print_template_yaml or print_custom_yaml settings Rom/Adjust flags like sprite, quickswap are not outputing with the print_template_yaml or print_custom_yaml settings
@@ -310,3 +317,18 @@ drops:
``` ```
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. 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.
## prices
This must be defined by player. You may have the prices of items in shops defined using the following
```
prices:
1:
Capacity Upgrade - Left: 100
Capacity Upgrade - Right: 200
Dark Death Mountain Shop - Left: 100
Dark Death Mountain Shop - Middle: 150
Dark Death Mountain Shop - Right: 300
Dark Lake Hylia Shop - Left: 200
```
+4
View File
@@ -166,6 +166,10 @@ bosses:
Palace of Darkness: Arrghus Palace of Darkness: Arrghus
Thieves Town: Blind Thieves Town: Blind
Ganons Tower (top): Vitreous Ganons Tower (top): Vitreous
prices:
1:
Capacity Upgrade - Left: 5
Capacity Upgrade - Right: 216
start_inventory: start_inventory:
1: 1:
- Pegasus Boots - Pegasus Boots
+5
View File
@@ -553,6 +553,10 @@
"action": "store_true", "action": "store_true",
"type": "bool" "type": "bool"
}, },
"mirrorscroll": {
"action": "store_true",
"type": "bool"
},
"calc_playthrough": { "calc_playthrough": {
"action": "store_false", "action": "store_false",
"type": "bool" "type": "bool"
@@ -623,6 +627,7 @@
"action": "store_false", "action": "store_false",
"type": "bool" "type": "bool"
}, },
"money_balance": {},
"settingsonload": { "settingsonload": {
"choices": [ "choices": [
"default", "default",
+1
View File
@@ -394,6 +394,7 @@
], ],
"collection_rate": [ "Display collection rate (default: %(default)s)" ], "collection_rate": [ "Display collection rate (default: %(default)s)" ],
"pseudoboots": [ " Start with pseudo boots that allow dashing but no item checks (default: %(default)s)"], "pseudoboots": [ " Start with pseudo boots that allow dashing but no item checks (default: %(default)s)"],
"mirrorscroll": [ " Players starts with mirror scroll that allows mirror in dungeons but not overworld (default: %(default)s"],
"bombbag": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ], "bombbag": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ],
"any_enemy_logic": [ "any_enemy_logic": [
"How to handle potential traversal between dungeon in Crossed door shuffle", "How to handle potential traversal between dungeon in Crossed door shuffle",
+1
View File
@@ -287,6 +287,7 @@
"randomizer.item.retro": "Retro mode", "randomizer.item.retro": "Retro mode",
"randomizer.item.pseudoboots": "Pseudoboots", "randomizer.item.pseudoboots": "Pseudoboots",
"randomizer.item.collection_rate": "Display Collection Rate", "randomizer.item.collection_rate": "Display Collection Rate",
"randomizer.item.mirrorscroll": "Mirror Scroll",
"randomizer.item.worldstate": "World State", "randomizer.item.worldstate": "World State",
"randomizer.item.worldstate.standard": "Standard", "randomizer.item.worldstate.standard": "Standard",
@@ -2,6 +2,7 @@
"checkboxes": { "checkboxes": {
"hints": { "type": "checkbox" }, "hints": { "type": "checkbox" },
"pseudoboots": { "type": "checkbox" }, "pseudoboots": { "type": "checkbox" },
"mirrorscroll": { "type": "checkbox" },
"collection_rate": {"type": "checkbox"}, "collection_rate": {"type": "checkbox"},
"race": { "type": "checkbox" } "race": { "type": "checkbox" }
}, },
+44
View File
@@ -21,11 +21,20 @@ class CustomSettings(object):
self.relative_dir = None self.relative_dir = None
self.world_rep = {} self.world_rep = {}
self.player_range = None self.player_range = None
self.player_map = {} # player number to name
def load_yaml(self, file): def load_yaml(self, file):
self.file_source = load_yaml(file) self.file_source = load_yaml(file)
head, filename = os.path.split(file) head, filename = os.path.split(file)
self.relative_dir = head self.relative_dir = head
if 'version' in self.file_source and self.file_source['version'].startswith('2'):
player_number = 1
for key in self.file_source.keys():
if key in ['meta', 'version']:
continue
else:
self.player_map[player_number] = key
player_number += 1
def determine_seed(self, default_seed): def determine_seed(self, default_seed):
if 'meta' in self.file_source: if 'meta' in self.file_source:
@@ -179,6 +188,7 @@ class CustomSettings(object):
args.restrict_boss_items[p] = get_setting(settings['restrict_boss_items'], args.restrict_boss_items[p]) args.restrict_boss_items[p] = get_setting(settings['restrict_boss_items'], args.restrict_boss_items[p])
args.overworld_map[p] = get_setting(settings['overworld_map'], args.overworld_map[p]) args.overworld_map[p] = get_setting(settings['overworld_map'], args.overworld_map[p])
args.pseudoboots[p] = get_setting(settings['pseudoboots'], args.pseudoboots[p]) args.pseudoboots[p] = get_setting(settings['pseudoboots'], args.pseudoboots[p])
args.mirrorscroll[p] = get_setting(settings['mirrorscroll'], args.mirrorscroll[p])
args.triforce_goal[p] = get_setting(settings['triforce_goal'], args.triforce_goal[p]) args.triforce_goal[p] = get_setting(settings['triforce_goal'], args.triforce_goal[p])
args.triforce_pool[p] = get_setting(settings['triforce_pool'], args.triforce_pool[p]) args.triforce_pool[p] = get_setting(settings['triforce_pool'], args.triforce_pool[p])
args.triforce_goal_min[p] = get_setting(settings['triforce_goal_min'], args.triforce_goal_min[p]) args.triforce_goal_min[p] = get_setting(settings['triforce_goal_min'], args.triforce_goal_min[p])
@@ -189,6 +199,7 @@ class CustomSettings(object):
args.triforce_max_difference[p] = get_setting(settings['triforce_max_difference'], args.triforce_max_difference[p]) args.triforce_max_difference[p] = get_setting(settings['triforce_max_difference'], args.triforce_max_difference[p])
args.beemizer[p] = get_setting(settings['beemizer'], args.beemizer[p]) args.beemizer[p] = get_setting(settings['beemizer'], args.beemizer[p])
args.aga_randomness[p] = get_setting(settings['aga_randomness'], args.aga_randomness[p]) args.aga_randomness[p] = get_setting(settings['aga_randomness'], args.aga_randomness[p])
args.money_balance[p] = get_setting(settings['money_balance'], args.money_balance[p])
# mystery usage # mystery usage
args.usestartinventory[p] = get_setting(settings['usestartinventory'], args.usestartinventory[p]) args.usestartinventory[p] = get_setting(settings['usestartinventory'], args.usestartinventory[p])
@@ -219,6 +230,9 @@ class CustomSettings(object):
return self.file_source['placements'] return self.file_source['placements']
return None return None
def get_prices(self, player):
return self.get_attribute_by_player_composite('prices', player)
def get_advanced_placements(self): def get_advanced_placements(self):
if 'advanced_placements' in self.file_source: if 'advanced_placements' in self.file_source:
return self.file_source['advanced_placements'] return self.file_source['advanced_placements']
@@ -284,6 +298,34 @@ class CustomSettings(object):
return self.file_source['enemies'] return self.file_source['enemies']
return None return None
def get_attribute_by_player_composite(self, attribute, player):
attempt = self.get_attribute_by_player_new(attribute, player)
if attempt is not None:
return attempt
attempt = self.get_attribute_by_player(attribute, player)
return attempt
def get_attribute_by_player(self, attribute, player):
if attribute in self.file_source:
if player in self.file_source[attribute]:
return self.file_source[attribute][player]
return None
def get_attribute_by_player_new(self, attribute, player):
player_id = self.get_player_id(player)
if player_id is not None:
if attribute in self.file_source[player_id]:
return self.file_source[player_id][attribute]
return None
def get_player_id(self, player):
if player in self.file_source:
return player
if player in self.player_map and self.player_map[player] in self.file_source:
return self.player_map[player]
return None
def create_from_world(self, world, settings): def create_from_world(self, world, settings):
self.player_range = range(1, world.players + 1) self.player_range = range(1, world.players + 1)
settings_dict, meta_dict = {}, {} settings_dict, meta_dict = {}, {}
@@ -355,10 +397,12 @@ class CustomSettings(object):
settings_dict[p]['linked_drops'] = world.linked_drops[p] settings_dict[p]['linked_drops'] = world.linked_drops[p]
settings_dict[p]['overworld_map'] = world.overworld_map[p] settings_dict[p]['overworld_map'] = world.overworld_map[p]
settings_dict[p]['pseudoboots'] = world.pseudoboots[p] settings_dict[p]['pseudoboots'] = world.pseudoboots[p]
settings_dict[p]['mirrorscroll'] = world.mirrorscroll[p]
settings_dict[p]['triforce_goal'] = world.treasure_hunt_count[p] settings_dict[p]['triforce_goal'] = world.treasure_hunt_count[p]
settings_dict[p]['triforce_pool'] = world.treasure_hunt_total[p] settings_dict[p]['triforce_pool'] = world.treasure_hunt_total[p]
settings_dict[p]['beemizer'] = world.beemizer[p] settings_dict[p]['beemizer'] = world.beemizer[p]
settings_dict[p]['aga_randomness'] = world.aga_randomness[p] settings_dict[p]['aga_randomness'] = world.aga_randomness[p]
settings_dict[p]['money_balance'] = world.money_balance[p]
if world.precollected_items: if world.precollected_items:
start_inv[p] = [] start_inv[p] = []
for item in world.precollected_items: for item in world.precollected_items:
+1
View File
@@ -57,6 +57,7 @@ SETTINGSTOPROCESS = {
"item": { "item": {
"hints": "hints", "hints": "hints",
"pseudoboots": "pseudoboots", "pseudoboots": "pseudoboots",
"mirrorscroll": "mirrorscroll",
'collection_rate': 'collection_rate', 'collection_rate': 'collection_rate',
"race": "race", "race": "race",
+3 -3
View File
@@ -1657,9 +1657,9 @@ def init_vanilla_sprites():
create_sprite(0x00b3, EnemySprite.Beamos, 0x00, 0, 0x06, 0x18, 'Mire Spikes') create_sprite(0x00b3, EnemySprite.Beamos, 0x00, 0, 0x06, 0x18, 'Mire Spikes')
create_sprite(0x00b3, EnemySprite.FourWayShooter, 0x00, 0, 0x0a, 0x1a, 'Mire Spikes') create_sprite(0x00b3, EnemySprite.FourWayShooter, 0x00, 0, 0x0a, 0x1a, 'Mire Spikes')
create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x1c, 'Mire Spikes') create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x1c, 'Mire Spikes')
create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x0a, 'TR Dark Ride') create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x0a, 'TR Dark Ride Ledges')
create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x09, 0x0f, 'TR Dark Ride') create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x09, 0x0f, 'TR Dark Ride Ledges')
create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x16, 'TR Dark Ride') create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x16, 'TR Dark Ride Ledges')
create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x06, 0x07, 'TR Chain Chomps Top') create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x06, 0x07, 'TR Chain Chomps Top')
create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x0a, 0x07, 'TR Chain Chomps Top') create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x0a, 0x07, 'TR Chain Chomps Top')
create_sprite(0x00b6, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x04) create_sprite(0x00b6, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x04)
+11 -11
View File
@@ -275,7 +275,7 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
rem_entrances.update(lw_entrances) rem_entrances.update(lw_entrances)
rem_entrances.update(dw_entrances) rem_entrances.update(dw_entrances)
else: else:
# cross world mandantory # cross world mandatory
entrance_list = list(entrances) entrance_list = list(entrances)
if avail.swapped: if avail.swapped:
forbidden = [e for e in Forbidden_Swap_Entrances if e in entrance_list] forbidden = [e for e in Forbidden_Swap_Entrances if e in entrance_list]
@@ -321,16 +321,13 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
avail.decoupled_exits.remove(bomb_shop) avail.decoupled_exits.remove(bomb_shop)
rem_exits.remove(bomb_shop) rem_exits.remove(bomb_shop)
def bonk_fairy_exception(x): # (Bonk Fairy not eligible in standard)
return not avail.is_standard() or x != 'Bonk Fairy (Light)'
# old man S&Q cave # old man S&Q cave
if not cross_world and not avail.assumed_loose_caves: if not cross_world and not avail.assumed_loose_caves:
#TODO: Add Swapped ER support for this #TODO: Add Swapped ER support for this
# OM Cave entrance in lw/dw if cross_world off # OM Cave entrance in lw/dw if cross_world off
if 'Old Man Cave Exit (West)' in rem_exits: if 'Old Man Cave Exit (West)' in rem_exits:
world_limiter = DW_Entrances if avail.inverted else LW_Entrances world_limiter = DW_Entrances if avail.inverted else LW_Entrances
om_cave_options = sorted([x for x in rem_entrances if x in world_limiter and bonk_fairy_exception(x)]) om_cave_options = sorted([x for x in rem_entrances if x in world_limiter and bonk_fairy_exception(avail, x)])
om_cave_choice = random.choice(om_cave_options) om_cave_choice = random.choice(om_cave_options)
if not avail.coupled: if not avail.coupled:
connect_exit('Old Man Cave Exit (West)', om_cave_choice, avail) connect_exit('Old Man Cave Exit (West)', om_cave_choice, avail)
@@ -344,7 +341,7 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
for ext in om_house: for ext in om_house:
if ext in rem_exits: if ext in rem_exits:
world_limiter = DW_Entrances if avail.inverted else LW_Entrances world_limiter = DW_Entrances if avail.inverted else LW_Entrances
om_house_options = [x for x in rem_entrances if x in world_limiter and bonk_fairy_exception(x)] om_house_options = [x for x in rem_entrances if x in world_limiter and bonk_fairy_exception(avail, x)]
om_house_choice = random.choice(om_house_options) om_house_choice = random.choice(om_house_options)
if not avail.coupled: if not avail.coupled:
connect_exit(ext, om_house_choice, avail) connect_exit(ext, om_house_choice, avail)
@@ -361,7 +358,7 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
lw_entrances, dw_entrances = [], [] lw_entrances, dw_entrances = [], []
left = sorted(rem_entrances) left = sorted(rem_entrances)
for x in left: for x in left:
if bonk_fairy_exception(x): if bonk_fairy_exception(avail, x):
lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x) lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x)
do_same_world_connectors(lw_entrances, dw_entrances, multi_exit_caves, avail) do_same_world_connectors(lw_entrances, dw_entrances, multi_exit_caves, avail)
if avail.world.doorShuffle[avail.player] != 'vanilla': if avail.world.doorShuffle[avail.player] != 'vanilla':
@@ -371,7 +368,7 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
unused_entrances.update(lw_entrances) unused_entrances.update(lw_entrances)
unused_entrances.update(dw_entrances) unused_entrances.update(dw_entrances)
else: else:
entrance_list = sorted([x for x in rem_entrances if bonk_fairy_exception(x)]) entrance_list = sorted([x for x in rem_entrances if bonk_fairy_exception(avail, x)])
do_cross_world_connectors(entrance_list, multi_exit_caves, avail) do_cross_world_connectors(entrance_list, multi_exit_caves, avail)
unused_entrances.update(entrance_list) unused_entrances.update(entrance_list)
@@ -1527,6 +1524,8 @@ def do_vanilla_connect(pool_def, avail):
avail.entrances.remove(entrance) avail.entrances.remove(entrance)
avail.exits.remove(target) avail.exits.remove(target)
def bonk_fairy_exception(avail, x): # (Bonk Fairy not eligible in standard)
return not avail.is_standard() or x != 'Bonk Fairy (Light)'
def do_mandatory_connections(avail, entrances, cave_options, must_exit): def do_mandatory_connections(avail, entrances, cave_options, must_exit):
if len(must_exit) == 0: if len(must_exit) == 0:
@@ -1614,7 +1613,8 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
if len(cave) == 2: if len(cave) == 2:
entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit]
and e not in invalid_cave_connections[tuple(cave)] and e not in must_exit and e not in invalid_cave_connections[tuple(cave)] and e not in must_exit
and (not avail.swapped or rnd_cave[0] != avail.combine_map[e])) and (not avail.swapped or rnd_cave[0] != avail.combine_map[e])
and bonk_fairy_exception(avail, e))
entrances.remove(entrance) entrances.remove(entrance)
connect_two_way(entrance, rnd_cave[0], avail) connect_two_way(entrance, rnd_cave[0], avail)
if avail.swapped and avail.combine_map[entrance] != rnd_cave[0]: if avail.swapped and avail.combine_map[entrance] != rnd_cave[0]:
@@ -1639,7 +1639,7 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
cave_entrances.append(entrance) cave_entrances.append(entrance)
else: else:
entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] and e not in must_exit entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] and e not in must_exit
and (not avail.swapped or cave_exit != avail.combine_map[e])) and (not avail.swapped or cave_exit != avail.combine_map[e]) and bonk_fairy_exception(avail, e))
cave_entrances.append(entrance) cave_entrances.append(entrance)
entrances.remove(entrance) entrances.remove(entrance)
connect_two_way(entrance, cave_exit, avail) connect_two_way(entrance, cave_exit, avail)
@@ -1670,7 +1670,7 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
continue continue
else: else:
entrance = next(e for e in entrances[::-1] if e not in invalid_cave_connections[tuple(cave)] entrance = next(e for e in entrances[::-1] if e not in invalid_cave_connections[tuple(cave)]
and (not avail.swapped or cave_exit != avail.combine_map[e])) and (not avail.swapped or cave_exit != avail.combine_map[e]) and bonk_fairy_exception(avail, e))
invalid_cave_connections[tuple(cave)] = set() invalid_cave_connections[tuple(cave)] = set()
entrances.remove(entrance) entrances.remove(entrance)
connect_two_way(entrance, cave_exit, avail) connect_two_way(entrance, cave_exit, avail)
+1
View File
@@ -152,6 +152,7 @@ def roll_settings(weights):
ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle != 'none' else 'off' ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle != 'none' else 'off'
ret.pseudoboots = get_choice_bool('pseudoboots') ret.pseudoboots = get_choice_bool('pseudoboots')
ret.mirrorscroll = get_choice_bool('mirrorscroll')
ret.shopsanity = get_choice_bool('shopsanity') ret.shopsanity = get_choice_bool('shopsanity')
keydropshuffle = get_choice_bool('keydropshuffle') keydropshuffle = get_choice_bool('keydropshuffle')
ret.dropshuffle = get_choice('dropshuffle') if 'dropshuffle' in weights else 'none' ret.dropshuffle = get_choice('dropshuffle') if 'dropshuffle' in weights else 'none'