Merge branch 'Ambrosia' into DoorDevVolatile
# Conflicts: # BaseClasses.py # CLI.py # Main.py
This commit is contained in:
@@ -115,6 +115,7 @@ class World(object):
|
|||||||
set_player_attr('compassshuffle', False)
|
set_player_attr('compassshuffle', False)
|
||||||
set_player_attr('keyshuffle', False)
|
set_player_attr('keyshuffle', False)
|
||||||
set_player_attr('bigkeyshuffle', False)
|
set_player_attr('bigkeyshuffle', False)
|
||||||
|
set_player_attr('restrict_boss_items', 'none')
|
||||||
set_player_attr('bombbag', False)
|
set_player_attr('bombbag', False)
|
||||||
set_player_attr('difficulty_requirements', None)
|
set_player_attr('difficulty_requirements', None)
|
||||||
set_player_attr('boss_shuffle', 'none')
|
set_player_attr('boss_shuffle', 'none')
|
||||||
@@ -326,7 +327,7 @@ class World(object):
|
|||||||
elif item.name.startswith('Bottle'):
|
elif item.name.startswith('Bottle'):
|
||||||
if ret.bottle_count(item.player) < self.difficulty_requirements[item.player].progressive_bottle_limit:
|
if ret.bottle_count(item.player) < self.difficulty_requirements[item.player].progressive_bottle_limit:
|
||||||
ret.prog_items[item.name, item.player] += 1
|
ret.prog_items[item.name, item.player] += 1
|
||||||
elif item.advancement or item.smallkey or item.bigkey:
|
elif item.advancement or item.smallkey or item.bigkey or item.compass or item.map:
|
||||||
ret.prog_items[item.name, item.player] += 1
|
ret.prog_items[item.name, item.player] += 1
|
||||||
|
|
||||||
for item in self.itempool:
|
for item in self.itempool:
|
||||||
@@ -341,6 +342,8 @@ class World(object):
|
|||||||
key_list += [dungeon.big_key.name]
|
key_list += [dungeon.big_key.name]
|
||||||
if len(dungeon.small_keys) > 0:
|
if len(dungeon.small_keys) > 0:
|
||||||
key_list += [x.name for x in dungeon.small_keys]
|
key_list += [x.name for x in dungeon.small_keys]
|
||||||
|
# map/compass may be required now
|
||||||
|
key_list += [x.name for x in dungeon.dungeon_items]
|
||||||
from Items import ItemFactory
|
from Items import ItemFactory
|
||||||
for item in ItemFactory(key_list, p):
|
for item in ItemFactory(key_list, p):
|
||||||
soft_collect(item)
|
soft_collect(item)
|
||||||
@@ -851,7 +854,7 @@ class CollectionState(object):
|
|||||||
reduced = Counter()
|
reduced = Counter()
|
||||||
for item, cnt in self.prog_items.items():
|
for item, cnt in self.prog_items.items():
|
||||||
item_name, item_player = item
|
item_name, item_player = item
|
||||||
if item_player == player and self.check_if_progressive(item_name):
|
if item_player == player and self.check_if_progressive(item_name, player):
|
||||||
if item_name.startswith('Bottle'): # I think magic requirements can require multiple bottles
|
if item_name.startswith('Bottle'): # I think magic requirements can require multiple bottles
|
||||||
bottle_count += cnt
|
bottle_count += cnt
|
||||||
elif item_name in ['Boss Heart Container', 'Sanctuary Heart Container', 'Piece of Heart']:
|
elif item_name in ['Boss Heart Container', 'Sanctuary Heart Container', 'Piece of Heart']:
|
||||||
@@ -867,8 +870,7 @@ class CollectionState(object):
|
|||||||
reduced[('Heart Container', player)] = 1
|
reduced[('Heart Container', player)] = 1
|
||||||
return frozenset(reduced.items())
|
return frozenset(reduced.items())
|
||||||
|
|
||||||
@staticmethod
|
def check_if_progressive(self, item_name, player):
|
||||||
def check_if_progressive(item_name):
|
|
||||||
return (item_name in
|
return (item_name in
|
||||||
['Bow', 'Progressive Bow', 'Progressive Bow (Alt)', 'Book of Mudora', 'Hammer', 'Hookshot',
|
['Bow', 'Progressive Bow', 'Progressive Bow (Alt)', 'Book of Mudora', 'Hammer', 'Hookshot',
|
||||||
'Magic Mirror', 'Ocarina', 'Pegasus Boots', 'Power Glove', 'Cape', 'Mushroom', 'Shovel',
|
'Magic Mirror', 'Ocarina', 'Pegasus Boots', 'Power Glove', 'Cape', 'Mushroom', 'Shovel',
|
||||||
@@ -880,7 +882,8 @@ class CollectionState(object):
|
|||||||
'Mirror Shield', 'Progressive Shield', 'Bug Catching Net', 'Cane of Byrna',
|
'Mirror Shield', 'Progressive Shield', 'Bug Catching Net', 'Cane of Byrna',
|
||||||
'Boss Heart Container', 'Sanctuary Heart Container', 'Piece of Heart', 'Magic Upgrade (1/2)',
|
'Boss Heart Container', 'Sanctuary Heart Container', 'Piece of Heart', 'Magic Upgrade (1/2)',
|
||||||
'Magic Upgrade (1/4)']
|
'Magic Upgrade (1/4)']
|
||||||
or item_name.startswith(('Bottle', 'Small Key', 'Big Key')))
|
or item_name.startswith(('Bottle', 'Small Key', 'Big Key'))
|
||||||
|
or (self.world.restrict_boss_items[player] != 'none' and item_name.startswith(('Map', 'Compass'))))
|
||||||
|
|
||||||
def can_reach(self, spot, resolution_hint=None, player=None):
|
def can_reach(self, spot, resolution_hint=None, player=None):
|
||||||
try:
|
try:
|
||||||
@@ -2192,6 +2195,12 @@ class Item(object):
|
|||||||
item_dungeon = 'Hyrule Castle'
|
item_dungeon = 'Hyrule Castle'
|
||||||
return item_dungeon
|
return item_dungeon
|
||||||
|
|
||||||
|
def is_inside_dungeon_item(self, world):
|
||||||
|
return ((self.smallkey and not world.keyshuffle[self.player])
|
||||||
|
or (self.bigkey and not world.bigkeyshuffle[self.player])
|
||||||
|
or (self.compass and not world.compassshuffle[self.player])
|
||||||
|
or (self.map and not world.mapshuffle[self.player]))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.__unicode__())
|
return str(self.__unicode__())
|
||||||
|
|
||||||
@@ -2411,6 +2420,7 @@ class Spoiler(object):
|
|||||||
'ganon_crystals': self.world.crystals_needed_for_ganon,
|
'ganon_crystals': self.world.crystals_needed_for_ganon,
|
||||||
'open_pyramid': self.world.open_pyramid,
|
'open_pyramid': self.world.open_pyramid,
|
||||||
'accessibility': self.world.accessibility,
|
'accessibility': self.world.accessibility,
|
||||||
|
'restricted_boss_items': self.world.restrict_boss_items,
|
||||||
'hints': self.world.hints,
|
'hints': self.world.hints,
|
||||||
'mapshuffle': self.world.mapshuffle,
|
'mapshuffle': self.world.mapshuffle,
|
||||||
'compassshuffle': self.world.compassshuffle,
|
'compassshuffle': self.world.compassshuffle,
|
||||||
@@ -2426,6 +2436,7 @@ class Spoiler(object):
|
|||||||
'experimental': self.world.experimental,
|
'experimental': self.world.experimental,
|
||||||
'keydropshuffle': self.world.keydropshuffle,
|
'keydropshuffle': self.world.keydropshuffle,
|
||||||
'shopsanity': self.world.shopsanity,
|
'shopsanity': self.world.shopsanity,
|
||||||
|
'pseudoboots': self.world.pseudoboots,
|
||||||
'triforcegoal': self.world.treasure_hunt_count,
|
'triforcegoal': self.world.treasure_hunt_count,
|
||||||
'triforcepool': self.world.treasure_hunt_total,
|
'triforcepool': self.world.treasure_hunt_total,
|
||||||
'code': {p: Settings.make_code(self.world, p) for p in range(1, self.world.players + 1)}
|
'code': {p: Settings.make_code(self.world, p) for p in range(1, self.world.players + 1)}
|
||||||
@@ -2453,6 +2464,9 @@ class Spoiler(object):
|
|||||||
return json.dumps(out)
|
return json.dumps(out)
|
||||||
|
|
||||||
def to_file(self, filename):
|
def to_file(self, filename):
|
||||||
|
def yn(flag):
|
||||||
|
return 'Yes' if flag else 'No'
|
||||||
|
|
||||||
self.parse_data()
|
self.parse_data()
|
||||||
with open(filename, 'w') as outfile:
|
with open(filename, 'w') as outfile:
|
||||||
outfile.write('ALttP Entrance Randomizer Version %s - Seed: %s\n\n' % (self.metadata['version'], self.world.seed))
|
outfile.write('ALttP Entrance Randomizer Version %s - Seed: %s\n\n' % (self.metadata['version'], self.world.seed))
|
||||||
@@ -2477,7 +2491,7 @@ class Spoiler(object):
|
|||||||
outfile.write('Difficulty: %s\n' % self.metadata['item_pool'][player])
|
outfile.write('Difficulty: %s\n' % self.metadata['item_pool'][player])
|
||||||
outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player])
|
outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player])
|
||||||
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player])
|
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player])
|
||||||
outfile.write(f"Link's House Shuffled: {'Yes' if self.metadata['shufflelinks'][player] else 'No'}\n")
|
outfile.write(f"Link's House Shuffled: {yn(self.metadata['shufflelinks'])}\n")
|
||||||
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
|
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
|
||||||
outfile.write('Intensity: %s\n' % self.metadata['intensity'][player])
|
outfile.write('Intensity: %s\n' % self.metadata['intensity'][player])
|
||||||
addition = ' (Random)' if self.world.crystals_gt_orig[player] == 'random' else ''
|
addition = ' (Random)' if self.world.crystals_gt_orig[player] == 'random' else ''
|
||||||
@@ -2486,6 +2500,7 @@ class Spoiler(object):
|
|||||||
outfile.write('Crystals required for Ganon: %s\n' % (str(self.metadata['ganon_crystals'][player]) + addition))
|
outfile.write('Crystals required for Ganon: %s\n' % (str(self.metadata['ganon_crystals'][player]) + addition))
|
||||||
outfile.write('Pyramid hole pre-opened: %s\n' % ('Yes' if self.metadata['open_pyramid'][player] else 'No'))
|
outfile.write('Pyramid hole pre-opened: %s\n' % ('Yes' if self.metadata['open_pyramid'][player] else 'No'))
|
||||||
outfile.write('Accessibility: %s\n' % self.metadata['accessibility'][player])
|
outfile.write('Accessibility: %s\n' % self.metadata['accessibility'][player])
|
||||||
|
outfile.write(f"Restricted Boss Items: {self.metadata['restricted_boss_items'][player]}\n")
|
||||||
outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No'))
|
outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No'))
|
||||||
outfile.write('Compass shuffle: %s\n' % ('Yes' if self.metadata['compassshuffle'][player] else 'No'))
|
outfile.write('Compass shuffle: %s\n' % ('Yes' if self.metadata['compassshuffle'][player] else 'No'))
|
||||||
outfile.write('Small Key shuffle: %s\n' % ('Yes' if self.metadata['keyshuffle'][player] else 'No'))
|
outfile.write('Small Key shuffle: %s\n' % ('Yes' if self.metadata['keyshuffle'][player] else 'No'))
|
||||||
@@ -2494,12 +2509,13 @@ class Spoiler(object):
|
|||||||
outfile.write('Enemy shuffle: %s\n' % self.metadata['enemy_shuffle'][player])
|
outfile.write('Enemy shuffle: %s\n' % self.metadata['enemy_shuffle'][player])
|
||||||
outfile.write('Enemy health: %s\n' % self.metadata['enemy_health'][player])
|
outfile.write('Enemy health: %s\n' % self.metadata['enemy_health'][player])
|
||||||
outfile.write('Enemy damage: %s\n' % self.metadata['enemy_damage'][player])
|
outfile.write('Enemy damage: %s\n' % self.metadata['enemy_damage'][player])
|
||||||
outfile.write('Pot shuffle: %s\n' % ('Yes' if self.metadata['potshuffle'][player] else 'No'))
|
outfile.write(f"Pot shuffle: {yn(self.metadata['potshuffle'][player])}\n")
|
||||||
outfile.write('Hints: %s\n' % ('Yes' if self.metadata['hints'][player] else 'No'))
|
outfile.write(f"Hints: {yn(self.metadata['hints'][player])}\n")
|
||||||
outfile.write('Experimental: %s\n' % ('Yes' if self.metadata['experimental'][player] else 'No'))
|
outfile.write(f"Experimental: {yn(self.metadata['experimental'][player])}\n")
|
||||||
outfile.write('Key Drops shuffled: %s\n' % ('Yes' if self.metadata['keydropshuffle'][player] else 'No'))
|
outfile.write(f"Key Drops shuffled: {yn(self.metadata['keydropshuffle'][player])}\n")
|
||||||
outfile.write(f"Shopsanity: {'Yes' if self.metadata['shopsanity'][player] else 'No'}\n")
|
outfile.write(f"Shopsanity: {yn(self.metadata['shopsanity'][player])}\n")
|
||||||
outfile.write('Bombbag: %s\n' % ('Yes' if self.metadata['bombbag'][player] else 'No'))
|
outfile.write(f"Bombbag: {yn(self.metadata['bombbag'][player])}\n")
|
||||||
|
outfile.write(f"Pseudoboots: {yn(self.metadata['pseudoboots'][player])}\n")
|
||||||
if self.doors:
|
if self.doors:
|
||||||
outfile.write('\n\nDoors:\n\n')
|
outfile.write('\n\nDoors:\n\n')
|
||||||
outfile.write('\n'.join(
|
outfile.write('\n'.join(
|
||||||
@@ -2681,6 +2697,16 @@ enemy_mode = {"none": 0, "shuffled": 1, "random": 2, "chaos": 2, "legacy": 3}
|
|||||||
e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4}
|
e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4}
|
||||||
e_dmg = {"default": 0, "shuffled": 1, "random": 2}
|
e_dmg = {"default": 0, "shuffled": 1, "random": 2}
|
||||||
|
|
||||||
|
# byte 8: RRAA A??? (restrict boss mode, algorithm, ? = unused)
|
||||||
|
rb_mode = {"none": 0, "mapcompass": 1, "dungeon": 2}
|
||||||
|
# algorithm: todo with "biased shuffles"
|
||||||
|
algo_mode = {"balanced": 0, "equitable": 1, "vanilla_fill": 2, "dungeon_only": 3, "district": 4}
|
||||||
|
|
||||||
|
# additions
|
||||||
|
# psuedoboots does not effect code
|
||||||
|
# sfx_shuffle and other adjust items does not effect settings code
|
||||||
|
|
||||||
|
|
||||||
class Settings(object):
|
class Settings(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -2709,7 +2735,9 @@ class Settings(object):
|
|||||||
| (boss_mode[w.boss_shuffle[p]] << 2) | (enemy_mode[w.enemy_shuffle[p]]),
|
| (boss_mode[w.boss_shuffle[p]] << 2) | (enemy_mode[w.enemy_shuffle[p]]),
|
||||||
|
|
||||||
(e_health[w.enemy_health[p]] << 5) | (e_dmg[w.enemy_damage[p]] << 3) | (0x4 if w.potshuffle[p] else 0)
|
(e_health[w.enemy_health[p]] << 5) | (e_dmg[w.enemy_damage[p]] << 3) | (0x4 if w.potshuffle[p] else 0)
|
||||||
| (0x2 if w.bombbag[p] else 0) | (1 if w.shufflelinks[p] else 0)])
|
| (0x2 if w.bombbag[p] else 0) | (1 if w.shufflelinks[p] else 0),
|
||||||
|
|
||||||
|
(rb_mode[w.restrict_boss_items[p]] << 6)])
|
||||||
return base64.b64encode(code, "+-".encode()).decode()
|
return base64.b64encode(code, "+-".encode()).decode()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -2755,6 +2783,8 @@ class Settings(object):
|
|||||||
args.shufflepots[p] = True if settings[7] & 0x4 else False
|
args.shufflepots[p] = True if settings[7] & 0x4 else False
|
||||||
args.bombbag[p] = True if settings[7] & 0x2 else False
|
args.bombbag[p] = True if settings[7] & 0x2 else False
|
||||||
args.shufflelinks[p] = True if settings[7] & 0x1 else False
|
args.shufflelinks[p] = True if settings[7] & 0x1 else False
|
||||||
|
if len(settings) > 8:
|
||||||
|
args.restrict_boss_items[p] = True if r(rb_mode)[(settings[8] & 0x80) >> 6] else False
|
||||||
|
|
||||||
|
|
||||||
class KeyRuleType(FastEnum):
|
class KeyRuleType(FastEnum):
|
||||||
|
|||||||
3
CLI.py
3
CLI.py
@@ -96,7 +96,7 @@ def parse_cli(argv, no_defaults=False):
|
|||||||
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
|
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
|
||||||
'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',
|
||||||
'bombbag', 'overworld_map',
|
'bombbag', 'overworld_map', 'restrict_boss_items',
|
||||||
'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
|
'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
|
||||||
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'pseudoboots',
|
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'pseudoboots',
|
||||||
'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
|
'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
|
||||||
@@ -140,6 +140,7 @@ def parse_settings():
|
|||||||
"progressive": "on",
|
"progressive": "on",
|
||||||
"accessibility": "items",
|
"accessibility": "items",
|
||||||
"algorithm": "balanced",
|
"algorithm": "balanced",
|
||||||
|
"restrict_boss_items": "none",
|
||||||
|
|
||||||
# Shuffle Ganon defaults to TRUE
|
# Shuffle Ganon defaults to TRUE
|
||||||
"openpyramid": False,
|
"openpyramid": False,
|
||||||
|
|||||||
@@ -218,7 +218,8 @@ def gen_dungeon_info(name, available_sectors, entrance_regions, all_regions, pro
|
|||||||
return name == 'Skull Woods 2' and d.name == 'Skull Pinball WS'
|
return name == 'Skull Woods 2' and d.name == 'Skull Pinball WS'
|
||||||
original_state = extend_reachable_state_improved(entrance_regions, start, proposed_map, all_regions,
|
original_state = extend_reachable_state_improved(entrance_regions, start, proposed_map, all_regions,
|
||||||
valid_doors, bk_flag, world, player, exception)
|
valid_doors, bk_flag, world, player, exception)
|
||||||
dungeon['Origin'] = create_graph_piece_from_state(None, original_state, original_state, proposed_map, exception)
|
dungeon['Origin'] = create_graph_piece_from_state(None, original_state, original_state, proposed_map, exception,
|
||||||
|
world, player)
|
||||||
either_crystal = True # if all hooks from the origin are either, explore all bits with either
|
either_crystal = True # if all hooks from the origin are either, explore all bits with either
|
||||||
for hook, crystal in dungeon['Origin'].hooks.items():
|
for hook, crystal in dungeon['Origin'].hooks.items():
|
||||||
if crystal != CrystalBarrier.Either:
|
if crystal != CrystalBarrier.Either:
|
||||||
@@ -239,7 +240,7 @@ def gen_dungeon_info(name, available_sectors, entrance_regions, all_regions, pro
|
|||||||
o_state = extend_reachable_state_improved([parent], init_state, proposed_map, all_regions,
|
o_state = extend_reachable_state_improved([parent], init_state, proposed_map, all_regions,
|
||||||
valid_doors, bk_flag, world, player, exception)
|
valid_doors, bk_flag, world, player, exception)
|
||||||
o_state_cache[door.name] = o_state
|
o_state_cache[door.name] = o_state
|
||||||
piece = create_graph_piece_from_state(door, o_state, o_state, proposed_map, exception)
|
piece = create_graph_piece_from_state(door, o_state, o_state, proposed_map, exception, world, player)
|
||||||
dungeon[door.name] = piece
|
dungeon[door.name] = piece
|
||||||
check_blue_states(hanger_set, dungeon, o_state_cache, proposed_map, all_regions, valid_doors,
|
check_blue_states(hanger_set, dungeon, o_state_cache, proposed_map, all_regions, valid_doors,
|
||||||
group_flags, door_map, world, player, exception)
|
group_flags, door_map, world, player, exception)
|
||||||
@@ -339,7 +340,7 @@ def explore_blue_state(door, dungeon, o_state, proposed_map, all_regions, valid_
|
|||||||
blue_start.big_key_special = o_state.big_key_special
|
blue_start.big_key_special = o_state.big_key_special
|
||||||
b_state = extend_reachable_state_improved([parent], blue_start, proposed_map, all_regions, valid_doors, bk_flag,
|
b_state = extend_reachable_state_improved([parent], blue_start, proposed_map, all_regions, valid_doors, bk_flag,
|
||||||
world, player, exception)
|
world, player, exception)
|
||||||
dungeon[door.name] = create_graph_piece_from_state(door, o_state, b_state, proposed_map, exception)
|
dungeon[door.name] = create_graph_piece_from_state(door, o_state, b_state, proposed_map, exception, world, player)
|
||||||
|
|
||||||
|
|
||||||
def make_a_choice(dungeon, hangers, avail_hooks, prev_choices, name):
|
def make_a_choice(dungeon, hangers, avail_hooks, prev_choices, name):
|
||||||
@@ -603,7 +604,7 @@ def winnow_hangers(hangers, hooks):
|
|||||||
hangers[hanger].remove(door)
|
hangers[hanger].remove(door)
|
||||||
|
|
||||||
|
|
||||||
def create_graph_piece_from_state(door, o_state, b_state, proposed_map, exception):
|
def create_graph_piece_from_state(door, o_state, b_state, proposed_map, exception, world, player):
|
||||||
# todo: info about dungeon events - not sure about that
|
# todo: info about dungeon events - not sure about that
|
||||||
graph_piece = GraphPiece()
|
graph_piece = GraphPiece()
|
||||||
all_unattached = {}
|
all_unattached = {}
|
||||||
@@ -635,16 +636,15 @@ def create_graph_piece_from_state(door, o_state, b_state, proposed_map, exceptio
|
|||||||
graph_piece.visited_regions.update(o_state.visited_orange)
|
graph_piece.visited_regions.update(o_state.visited_orange)
|
||||||
graph_piece.visited_regions.update(b_state.visited_blue)
|
graph_piece.visited_regions.update(b_state.visited_blue)
|
||||||
graph_piece.visited_regions.update(b_state.visited_orange)
|
graph_piece.visited_regions.update(b_state.visited_orange)
|
||||||
graph_piece.possible_bk_locations.update(filter_for_potential_bk_locations(o_state.bk_found))
|
graph_piece.possible_bk_locations.update(filter_for_potential_bk_locations(o_state.bk_found, world, player))
|
||||||
graph_piece.possible_bk_locations.update(filter_for_potential_bk_locations(b_state.bk_found))
|
graph_piece.possible_bk_locations.update(filter_for_potential_bk_locations(b_state.bk_found, world, player))
|
||||||
graph_piece.pinball_used = o_state.pinball_used or b_state.pinball_used
|
graph_piece.pinball_used = o_state.pinball_used or b_state.pinball_used
|
||||||
return graph_piece
|
return graph_piece
|
||||||
|
|
||||||
|
|
||||||
def filter_for_potential_bk_locations(locations):
|
def filter_for_potential_bk_locations(locations, world, player):
|
||||||
return [x for x in locations if
|
return [x for x in locations if '- Big Chest' not in x.name and not reserved_location(x, world, player) and
|
||||||
'- Big Chest' not in x.name and '- Prize' not in x.name and x.name not in dungeon_events
|
not x.forced_item and not prize_or_event(x) and not blind_boss_unavail(x, locations, world, player)]
|
||||||
and not x.forced_item and x.name not in ['Agahnim 1', 'Agahnim 2']]
|
|
||||||
|
|
||||||
|
|
||||||
type_map = {
|
type_map = {
|
||||||
@@ -987,12 +987,8 @@ class ExplorationState(object):
|
|||||||
return self.crystal == CrystalBarrier.Either or door.crystal == self.crystal
|
return self.crystal == CrystalBarrier.Either or door.crystal == self.crystal
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def count_locations_exclude_specials(self):
|
def count_locations_exclude_specials(self, world, player):
|
||||||
cnt = 0
|
return count_locations_exclude_big_chest(self.found_locations, world, player)
|
||||||
for loc in self.found_locations:
|
|
||||||
if '- Big Chest' not in loc.name and '- Prize' not in loc.name and loc.name not in dungeon_events and not loc.forced_item:
|
|
||||||
cnt += 1
|
|
||||||
return cnt
|
|
||||||
|
|
||||||
def validate(self, door, region, world, player):
|
def validate(self, door, region, world, player):
|
||||||
return self.can_traverse(door) and not self.visited(region) and valid_region_to_explore(region, self.dungeon,
|
return self.can_traverse(door) and not self.visited(region) and valid_region_to_explore(region, self.dungeon,
|
||||||
@@ -1033,6 +1029,32 @@ class ExplorationState(object):
|
|||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
|
||||||
|
def count_locations_exclude_big_chest(locations, world, player):
|
||||||
|
cnt = 0
|
||||||
|
for loc in locations:
|
||||||
|
if ('- Big Chest' not in loc.name and not loc.forced_item and not reserved_location(loc, world, player)
|
||||||
|
and not prize_or_event(loc) and not blind_boss_unavail(loc, locations, world, player)):
|
||||||
|
cnt += 1
|
||||||
|
return cnt
|
||||||
|
|
||||||
|
|
||||||
|
def prize_or_event(loc):
|
||||||
|
return loc.name in dungeon_events or '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2']
|
||||||
|
|
||||||
|
|
||||||
|
def reserved_location(loc, world, player):
|
||||||
|
return hasattr(world, 'item_pool_config') and loc.name in world.item_pool_config.reserved_locations[player]
|
||||||
|
|
||||||
|
|
||||||
|
def blind_boss_unavail(loc, locations, world, player):
|
||||||
|
if loc.name == "Thieves' Town - Boss":
|
||||||
|
return (loc.parent_region.dungeon.boss.name == 'Blind' and
|
||||||
|
(not any(x for x in locations if x.name == 'Suspicious Maiden') or
|
||||||
|
(world.get_region('Thieves Attic Window', player).dungeon.name == 'Thieves Town' and
|
||||||
|
not any(x for x in locations if x.name == 'Attic Cracked Floor'))))
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class ExplorableDoor(object):
|
class ExplorableDoor(object):
|
||||||
|
|
||||||
def __init__(self, door, crystal, flag):
|
def __init__(self, door, crystal, flag):
|
||||||
@@ -1056,7 +1078,8 @@ def extend_reachable_state_improved(search_regions, state, proposed_map, all_reg
|
|||||||
explorable_door = local_state.next_avail_door()
|
explorable_door = local_state.next_avail_door()
|
||||||
if explorable_door.door.bigKey:
|
if explorable_door.door.bigKey:
|
||||||
if bk_flag:
|
if bk_flag:
|
||||||
big_not_found = not special_big_key_found(local_state) if local_state.big_key_special else local_state.count_locations_exclude_specials() == 0
|
big_not_found = (not special_big_key_found(local_state) if local_state.big_key_special
|
||||||
|
else local_state.count_locations_exclude_specials(world, player) == 0)
|
||||||
if big_not_found:
|
if big_not_found:
|
||||||
continue # we can't open this door
|
continue # we can't open this door
|
||||||
if explorable_door.door in proposed_map:
|
if explorable_door.door in proposed_map:
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ def fill_dungeons(world):
|
|||||||
def get_dungeon_item_pool(world):
|
def get_dungeon_item_pool(world):
|
||||||
return [item for dungeon in world.dungeons for item in dungeon.all_items]
|
return [item for dungeon in world.dungeons for item in dungeon.all_items]
|
||||||
|
|
||||||
|
|
||||||
def fill_dungeons_restrictive(world, shuffled_locations):
|
def fill_dungeons_restrictive(world, shuffled_locations):
|
||||||
all_state_base = world.get_all_state()
|
all_state_base = world.get_all_state()
|
||||||
|
|
||||||
@@ -137,10 +138,7 @@ def fill_dungeons_restrictive(world, shuffled_locations):
|
|||||||
elif (item.map and world.mapshuffle[item.player]) or (item.compass and world.compassshuffle[item.player]):
|
elif (item.map and world.mapshuffle[item.player]) or (item.compass and world.compassshuffle[item.player]):
|
||||||
item.priority = True
|
item.priority = True
|
||||||
|
|
||||||
dungeon_items = [item for item in get_dungeon_item_pool(world) if ((item.smallkey and not world.keyshuffle[item.player])
|
dungeon_items = [item for item in get_dungeon_item_pool(world) if item.is_inside_dungeon_item(world)]
|
||||||
or (item.bigkey and not world.bigkeyshuffle[item.player])
|
|
||||||
or (item.map and not world.mapshuffle[item.player])
|
|
||||||
or (item.compass and not world.compassshuffle[item.player]))]
|
|
||||||
|
|
||||||
# sort in the order Big Key, Small Key, Other before placing dungeon items
|
# sort in the order Big Key, Small Key, Other before placing dungeon items
|
||||||
sort_order = {"BigKey": 3, "SmallKey": 2}
|
sort_order = {"BigKey": 3, "SmallKey": 2}
|
||||||
@@ -441,7 +439,6 @@ dungeon_bigs = {
|
|||||||
'Ganons Tower': 'Big Key (Ganons Tower)'
|
'Ganons Tower': 'Big Key (Ganons Tower)'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dungeon_hints = {
|
dungeon_hints = {
|
||||||
'Hyrule Castle': 'in Hyrule Castle',
|
'Hyrule Castle': 'in Hyrule Castle',
|
||||||
'Eastern Palace': 'in Eastern Palace',
|
'Eastern Palace': 'in Eastern Palace',
|
||||||
|
|||||||
15
Fill.py
15
Fill.py
@@ -237,7 +237,10 @@ def fill_restrictive(world, base_state, locations, itempool, keys_in_itempool =
|
|||||||
|
|
||||||
|
|
||||||
def valid_key_placement(item, location, itempool, world):
|
def valid_key_placement(item, location, itempool, world):
|
||||||
if (not item.smallkey and not item.bigkey) or item.player != location.player or world.retro[item.player] or world.logic[item.player] == 'nologic':
|
if not valid_reserved_placement(item, location, world):
|
||||||
|
return False
|
||||||
|
if ((not item.smallkey and not item.bigkey) or item.player != location.player
|
||||||
|
or world.retro[item.player] or world.logic[item.player] == 'nologic'):
|
||||||
return True
|
return True
|
||||||
dungeon = location.parent_region.dungeon
|
dungeon = location.parent_region.dungeon
|
||||||
if dungeon:
|
if dungeon:
|
||||||
@@ -251,9 +254,13 @@ def valid_key_placement(item, location, itempool, world):
|
|||||||
cr_count = world.crystals_needed_for_gt[location.player]
|
cr_count = world.crystals_needed_for_gt[location.player]
|
||||||
return key_logic.check_placement(unplaced_keys, location if item.bigkey else None, prize_loc, cr_count)
|
return key_logic.check_placement(unplaced_keys, location if item.bigkey else None, prize_loc, cr_count)
|
||||||
else:
|
else:
|
||||||
inside_dungeon_item = ((item.smallkey and not world.keyshuffle[item.player])
|
return not item.is_inside_dungeon_item(world) # todo: big deal for ambrosia to fix this
|
||||||
or (item.bigkey and not world.bigkeyshuffle[item.player]))
|
|
||||||
return not inside_dungeon_item
|
|
||||||
|
def valid_reserved_placement(item, location, world):
|
||||||
|
if item.player == location.player and item.is_inside_dungeon_item(world):
|
||||||
|
return location.name not in world.item_pool_config.reserved_locations[location.player]
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def track_outside_keys(item, location, world):
|
def track_outside_keys(item, location, world):
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ from collections import defaultdict, deque
|
|||||||
from BaseClasses import DoorType, dungeon_keys, KeyRuleType, RegionType
|
from BaseClasses import DoorType, dungeon_keys, KeyRuleType, RegionType
|
||||||
from Regions import dungeon_events
|
from Regions import dungeon_events
|
||||||
from Dungeons import dungeon_keys, dungeon_bigs, dungeon_table
|
from Dungeons import dungeon_keys, dungeon_bigs, dungeon_table
|
||||||
from DungeonGenerator import ExplorationState, special_big_key_doors
|
from DungeonGenerator import ExplorationState, special_big_key_doors, count_locations_exclude_big_chest, prize_or_event
|
||||||
|
from DungeonGenerator import reserved_location, blind_boss_unavail
|
||||||
|
|
||||||
|
|
||||||
class KeyLayout(object):
|
class KeyLayout(object):
|
||||||
@@ -1100,40 +1101,30 @@ def location_is_bk_locked(loc, key_logic):
|
|||||||
return loc in key_logic.bk_chests or loc in key_logic.bk_locked
|
return loc in key_logic.bk_chests or loc in key_logic.bk_locked
|
||||||
|
|
||||||
|
|
||||||
def prize_or_event(loc):
|
# todo: verfiy this code is defunct
|
||||||
return loc.name in dungeon_events or '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2']
|
# def prize_or_event(loc):
|
||||||
|
# return loc.name in dungeon_events or '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2']
|
||||||
|
#
|
||||||
def boss_unavail(loc, world, player):
|
#
|
||||||
# todo: ambrosia
|
# def reserved_location(loc, world, player):
|
||||||
# return world.bossdrops[player] == 'ambrosia' and "- Boss" in loc.name
|
# return loc in world.item_pool.config.reserved_locations[player]
|
||||||
return False
|
#
|
||||||
|
#
|
||||||
|
# def blind_boss_unavail(loc, state, world, player):
|
||||||
def blind_boss_unavail(loc, state, world, player):
|
# if loc.name == "Thieves' Town - Boss":
|
||||||
if loc.name == "Thieves' Town - Boss":
|
# return (loc.parent_region.dungeon.boss.name == 'Blind' and
|
||||||
# todo: check attic
|
# (not any(x for x in state.found_locations if x.name == 'Suspicious Maiden') or
|
||||||
return (loc.parent_region.dungeon.boss.name == 'Blind' and
|
# (world.get_region('Thieves Attic Window', player).dungeon.name == 'Thieves Town' and
|
||||||
(not any(x for x in state.found_locations if x.name == 'Suspicious Maiden') or
|
# not any(x for x in state.found_locations if x.name == 'Attic Cracked Floor'))))
|
||||||
(world.get_region('Thieves Attic Window', player).dungeon.name == 'Thieves Town' and
|
# return False
|
||||||
not any(x for x in state.found_locations if x.name == 'Attic Cracked Floor'))))
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
|
# counts free locations for keys - hence why reserved locations don't count
|
||||||
def count_free_locations(state, world, player):
|
def count_free_locations(state, world, player):
|
||||||
cnt = 0
|
cnt = 0
|
||||||
for loc in state.found_locations:
|
for loc in state.found_locations:
|
||||||
if (not prize_or_event(loc) and not loc.forced_item and not boss_unavail(loc, world, player)
|
if (not prize_or_event(loc) and not loc.forced_item and not reserved_location(loc, world, player)
|
||||||
and not blind_boss_unavail(loc, state, world, player)):
|
and not blind_boss_unavail(loc, state.found_locations, world, player)):
|
||||||
cnt += 1
|
|
||||||
return cnt
|
|
||||||
|
|
||||||
|
|
||||||
def count_locations_exclude_big_chest(state, world, player):
|
|
||||||
cnt = 0
|
|
||||||
for loc in state.found_locations:
|
|
||||||
if ('- Big Chest' not in loc.name and not loc.forced_item and not boss_unavail(loc, world, player)
|
|
||||||
and not prize_or_event(loc) and not blind_boss_unavail(loc, state, world, player)):
|
|
||||||
cnt += 1
|
cnt += 1
|
||||||
return cnt
|
return cnt
|
||||||
|
|
||||||
@@ -1437,7 +1428,7 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
|
|||||||
if state.big_key_opened:
|
if state.big_key_opened:
|
||||||
ttl_locations = count_free_locations(state, world, player)
|
ttl_locations = count_free_locations(state, world, player)
|
||||||
else:
|
else:
|
||||||
ttl_locations = count_locations_exclude_big_chest(state, world, player)
|
ttl_locations = count_locations_exclude_big_chest(state.found_locations, world, player)
|
||||||
ttl_small_key_only = count_small_key_only_locations(state)
|
ttl_small_key_only = count_small_key_only_locations(state)
|
||||||
available_small_locations = cnt_avail_small_locations(ttl_locations, ttl_small_key_only, state, world, player)
|
available_small_locations = cnt_avail_small_locations(ttl_locations, ttl_small_key_only, state, world, player)
|
||||||
available_big_locations = cnt_avail_big_locations(ttl_locations, state, world, player)
|
available_big_locations = cnt_avail_big_locations(ttl_locations, state, world, player)
|
||||||
@@ -1663,7 +1654,7 @@ def can_open_door(door, state, world, player):
|
|||||||
if state.big_key_opened:
|
if state.big_key_opened:
|
||||||
ttl_locations = count_free_locations(state, world, player)
|
ttl_locations = count_free_locations(state, world, player)
|
||||||
else:
|
else:
|
||||||
ttl_locations = count_locations_exclude_big_chest(state, world, player)
|
ttl_locations = count_locations_exclude_big_chest(state.found_locations, world, player)
|
||||||
if door.smallKey:
|
if door.smallKey:
|
||||||
ttl_small_key_only = count_small_key_only_locations(state)
|
ttl_small_key_only = count_small_key_only_locations(state)
|
||||||
available_small_locations = cnt_avail_small_locations(ttl_locations, ttl_small_key_only, state, world, player)
|
available_small_locations = cnt_avail_small_locations(ttl_locations, ttl_small_key_only, state, world, player)
|
||||||
|
|||||||
4
Main.py
4
Main.py
@@ -29,6 +29,8 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc
|
|||||||
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
|
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
|
||||||
from Utils import output_path, parse_player_names
|
from Utils import output_path, parse_player_names
|
||||||
|
|
||||||
|
from source.item.FillUtil import create_item_pool_config
|
||||||
|
|
||||||
__version__ = '1.0.1.0-v'
|
__version__ = '1.0.1.0-v'
|
||||||
|
|
||||||
from source.classes.BabelFish import BabelFish
|
from source.classes.BabelFish import BabelFish
|
||||||
@@ -105,6 +107,7 @@ def main(args, seed=None, fish=None):
|
|||||||
world.shufflelinks = args.shufflelinks.copy()
|
world.shufflelinks = args.shufflelinks.copy()
|
||||||
world.pseudoboots = args.pseudoboots.copy()
|
world.pseudoboots = args.pseudoboots.copy()
|
||||||
world.overworld_map = args.overworld_map.copy()
|
world.overworld_map = args.overworld_map.copy()
|
||||||
|
world.restrict_boss_items = args.restrict_boss_items.copy()
|
||||||
|
|
||||||
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
|
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
|
||||||
|
|
||||||
@@ -149,6 +152,7 @@ def main(args, seed=None, fish=None):
|
|||||||
create_dungeons(world, player)
|
create_dungeons(world, player)
|
||||||
adjust_locations(world, player)
|
adjust_locations(world, player)
|
||||||
place_bosses(world, player)
|
place_bosses(world, player)
|
||||||
|
create_item_pool_config(world)
|
||||||
|
|
||||||
if any(world.potshuffle.values()):
|
if any(world.potshuffle.values()):
|
||||||
logger.info(world.fish.translate("cli", "cli", "shuffling.pots"))
|
logger.info(world.fish.translate("cli", "cli", "shuffling.pots"))
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ def roll_settings(weights):
|
|||||||
ret.bigkeyshuffle = get_choice('bigkey_shuffle') == 'on' if 'bigkey_shuffle' in weights else dungeon_items in ['full']
|
ret.bigkeyshuffle = get_choice('bigkey_shuffle') == 'on' if 'bigkey_shuffle' in weights else dungeon_items in ['full']
|
||||||
|
|
||||||
ret.accessibility = get_choice('accessibility')
|
ret.accessibility = get_choice('accessibility')
|
||||||
|
ret.restrict_boss_items = get_choice('restrict_boss_items')
|
||||||
|
|
||||||
entrance_shuffle = get_choice('entrance_shuffle')
|
entrance_shuffle = get_choice('entrance_shuffle')
|
||||||
ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla'
|
ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla'
|
||||||
|
|||||||
19
Rules.py
19
Rules.py
@@ -4,6 +4,7 @@ from collections import deque
|
|||||||
|
|
||||||
import OverworldGlitchRules
|
import OverworldGlitchRules
|
||||||
from BaseClasses import CollectionState, RegionType, DoorType, Entrance, CrystalBarrier, KeyRuleType
|
from BaseClasses import CollectionState, RegionType, DoorType, Entrance, CrystalBarrier, KeyRuleType
|
||||||
|
from Dungeons import dungeon_table
|
||||||
from RoomData import DoorKind
|
from RoomData import DoorKind
|
||||||
from OverworldGlitchRules import overworld_glitches_rules
|
from OverworldGlitchRules import overworld_glitches_rules
|
||||||
|
|
||||||
@@ -557,11 +558,29 @@ def global_rules(world, player):
|
|||||||
add_key_logic_rules(world, player)
|
add_key_logic_rules(world, player)
|
||||||
# End of door rando rules.
|
# End of door rando rules.
|
||||||
|
|
||||||
|
if world.restrict_boss_items[player] != 'none':
|
||||||
|
def add_mc_rule(l):
|
||||||
|
boss_location = world.get_location(l, player)
|
||||||
|
d_name = boss_location.parent_region.dungeon.name
|
||||||
|
compass_name = f'Compass ({d_name})'
|
||||||
|
map_name = f'Map ({d_name})'
|
||||||
|
add_rule(boss_location, lambda state: state.has(compass_name, player) and state.has(map_name, player))
|
||||||
|
|
||||||
|
for dungeon, info in dungeon_table.items():
|
||||||
|
if info.prize:
|
||||||
|
d_name = "Thieves' Town" if dungeon.startswith('Thieves') else dungeon
|
||||||
|
for loc in [info.prize, f'{d_name} - Boss']:
|
||||||
|
add_mc_rule(loc)
|
||||||
|
if world.doorShuffle[player] == 'crossed':
|
||||||
|
add_mc_rule('Agahnim 1')
|
||||||
|
add_mc_rule('Agahnim 2')
|
||||||
|
|
||||||
add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player))
|
add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player))
|
||||||
set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)
|
set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)
|
||||||
and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times
|
and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times
|
||||||
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop
|
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop
|
||||||
|
|
||||||
|
|
||||||
def bomb_rules(world, player):
|
def bomb_rules(world, player):
|
||||||
bonkable_doors = ['Two Brothers House Exit (West)', 'Two Brothers House Exit (East)'] # Technically this is incorrectly defined, but functionally the same as what is intended.
|
bonkable_doors = ['Two Brothers House Exit (West)', 'Two Brothers House Exit (East)'] # Technically this is incorrectly defined, but functionally the same as what is intended.
|
||||||
bombable_doors = ['Ice Rod Cave', 'Light World Bomb Hut', 'Light World Death Mountain Shop', 'Mini Moldorm Cave',
|
bombable_doors = ['Ice Rod Cave', 'Light World Bomb Hut', 'Light World Death Mountain Shop', 'Mini Moldorm Cave',
|
||||||
|
|||||||
@@ -254,6 +254,13 @@
|
|||||||
"none"
|
"none"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"restrict_boss_items": {
|
||||||
|
"choices": [
|
||||||
|
"none",
|
||||||
|
"mapcompass",
|
||||||
|
"dungeon"
|
||||||
|
]
|
||||||
|
},
|
||||||
"hints": {
|
"hints": {
|
||||||
"action": "store_true",
|
"action": "store_true",
|
||||||
"type": "bool"
|
"type": "bool"
|
||||||
|
|||||||
@@ -276,6 +276,13 @@
|
|||||||
"Locations: You will be able to reach every location in the game.",
|
"Locations: You will be able to reach every location in the game.",
|
||||||
"None: You will be able to reach enough locations to beat the game."
|
"None: You will be able to reach enough locations to beat the game."
|
||||||
],
|
],
|
||||||
|
"restrict_boss_items": [
|
||||||
|
"Select which dungeon are not allowed on bosses (default: %(default)s)",
|
||||||
|
"None: All items allowed",
|
||||||
|
"Mapcompass: Map and Compass are required before you defeat the boss.",
|
||||||
|
"Dungeon: Same as above and keys too cannot be on the boss. Small key shuffle",
|
||||||
|
" and big key shuffle override this behavior"
|
||||||
|
],
|
||||||
"hints": [ "Make telepathic tiles and storytellers give helpful hints. (default: %(default)s)" ],
|
"hints": [ "Make telepathic tiles and storytellers give helpful hints. (default: %(default)s)" ],
|
||||||
"shuffleganon": [
|
"shuffleganon": [
|
||||||
"Include the Ganon's Tower and Pyramid Hole in the",
|
"Include the Ganon's Tower and Pyramid Hole in the",
|
||||||
|
|||||||
@@ -291,6 +291,10 @@
|
|||||||
"randomizer.item.sortingalgo.vt26": "VT8.26",
|
"randomizer.item.sortingalgo.vt26": "VT8.26",
|
||||||
"randomizer.item.sortingalgo.balanced": "Balanced",
|
"randomizer.item.sortingalgo.balanced": "Balanced",
|
||||||
|
|
||||||
|
"randomizer.item.restrict_boss_items": "Forbidden Boss Items",
|
||||||
|
"randomizer.item.restrict_boss_items.none": "None",
|
||||||
|
"randomizer.item.restrict_boss_items.mapcompass": "Map & Compass",
|
||||||
|
"randomizer.item.restrict_boss_items.dungeon": "Map & Compass & Keys",
|
||||||
|
|
||||||
"bottom.content.worlds": "Worlds",
|
"bottom.content.worlds": "Worlds",
|
||||||
"bottom.content.names": "Player names",
|
"bottom.content.names": "Player names",
|
||||||
|
|||||||
@@ -124,6 +124,15 @@
|
|||||||
"vt26",
|
"vt26",
|
||||||
"balanced"
|
"balanced"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"restrict_boss_items": {
|
||||||
|
"type": "selectbox",
|
||||||
|
"default": "none",
|
||||||
|
"options": [
|
||||||
|
"none",
|
||||||
|
"mapcompass",
|
||||||
|
"dungeon"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ SETTINGSTOPROCESS = {
|
|||||||
"progressives": "progressive",
|
"progressives": "progressive",
|
||||||
"accessibility": "accessibility",
|
"accessibility": "accessibility",
|
||||||
"sortingalgo": "algorithm",
|
"sortingalgo": "algorithm",
|
||||||
"beemizer": "beemizer"
|
"beemizer": "beemizer",
|
||||||
|
"restrict_boss_items": "restrict_boss_items"
|
||||||
},
|
},
|
||||||
"entrance": {
|
"entrance": {
|
||||||
"openpyramid": "openpyramid",
|
"openpyramid": "openpyramid",
|
||||||
|
|||||||
26
source/item/FillUtil.py
Normal file
26
source/item/FillUtil.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from Dungeons import dungeon_table
|
||||||
|
|
||||||
|
class ItemPoolConfig(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.reserved_locations = defaultdict(set)
|
||||||
|
|
||||||
|
|
||||||
|
def create_item_pool_config(world):
|
||||||
|
world.item_pool_config = config = ItemPoolConfig()
|
||||||
|
player_set = set()
|
||||||
|
for player in range(1, world.players+1):
|
||||||
|
if world.restrict_boss_items[player] != 'none':
|
||||||
|
player_set.add(player)
|
||||||
|
if world.restrict_boss_items[player] == 'dungeon':
|
||||||
|
for dungeon, info in dungeon_table.items():
|
||||||
|
if info.prize:
|
||||||
|
d_name = "Thieves' Town" if dungeon.startswith('Thieves') else dungeon
|
||||||
|
config.reserved_locations[player].add(f'{d_name} - Boss')
|
||||||
|
for dungeon in world.dungeons:
|
||||||
|
if world.restrict_boss_items[dungeon.player] != 'none':
|
||||||
|
for item in dungeon.all_items:
|
||||||
|
if item.map or item.compass:
|
||||||
|
item.advancement = True
|
||||||
Reference in New Issue
Block a user