Merged in DR v1.2.0.8

This commit is contained in:
codemann8
2023-02-18 19:32:38 -06:00
33 changed files with 902 additions and 156 deletions

View File

@@ -35,6 +35,8 @@ class World(object):
self.doorShuffle = doorShuffle.copy()
self.intensity = {}
self.door_type_mode = {}
self.trap_door_mode = {}
self.key_logic_algorithm = {}
self.logic = logic.copy()
self.mode = mode.copy()
self.swords = swords.copy()
@@ -162,6 +164,8 @@ class World(object):
set_player_attr('pot_pool', {})
set_player_attr('decoupledoors', False)
set_player_attr('door_type_mode', 'original')
set_player_attr('trap_door_mode', 'optional')
set_player_attr('key_logic_algorithm', 'default')
set_player_attr('shopsanity', False)
set_player_attr('mixed_travel', 'prevent')
@@ -585,6 +589,7 @@ class CollectionState(object):
self.world = parent
if not skip_init:
self.prog_items = Counter()
self.forced_keys = Counter()
self.reachable_regions = {player: dict() for player in range(1, parent.players + 1)}
self.blocked_connections = {player: dict() for player in range(1, parent.players + 1)}
self.events = []
@@ -617,12 +622,13 @@ class CollectionState(object):
queue = deque(self.blocked_connections[player].items())
self.traverse_world(queue, rrp, bc, player)
unresolved_events = [x for y in self.reachable_regions[player] for x in y.locations
if x.event and x.item and (x.item.smallkey or x.item.bigkey or x.item.advancement)
and x not in self.locations_checked and x.can_reach(self)]
unresolved_events = self._do_not_flood_the_keys(unresolved_events)
if len(unresolved_events) == 0:
self.check_key_doors_in_dungeons(rrp, player)
if self.world.key_logic_algorithm[player] == 'default':
unresolved_events = [x for y in self.reachable_regions[player] for x in y.locations
if x.event and x.item and (x.item.smallkey or x.item.bigkey or x.item.advancement)
and x not in self.locations_checked and x.can_reach(self)]
unresolved_events = self._do_not_flood_the_keys(unresolved_events)
if len(unresolved_events) == 0:
self.check_key_doors_in_dungeons(rrp, player)
def traverse_world(self, queue, rrp, bc, player):
# run BFS on all connections, and keep track of those blocked by missing items
@@ -706,6 +712,7 @@ class CollectionState(object):
def check_key_doors_in_dungeons(self, rrp, player):
for dungeon_name, checklist in self.dungeons_to_check[player].items():
# todo: optimization idea - abort exploration if there are unresolved events now
if self.apply_dungeon_exploration(rrp, player, dungeon_name, checklist):
continue
init_door_candidates = self.should_explore_child_state(self, dungeon_name, player)
@@ -905,6 +912,7 @@ class CollectionState(object):
def copy(self):
ret = CollectionState(self.world, skip_init=True)
ret.prog_items = self.prog_items.copy()
ret.forced_keys = self.forced_keys.copy()
ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in range(1, self.world.players + 1)}
ret.blocked_connections = {player: copy.copy(self.blocked_connections[player]) for player in range(1, self.world.players + 1)}
ret.events = copy.copy(self.events)
@@ -1112,6 +1120,14 @@ class CollectionState(object):
return (item, player) in self.prog_items
return self.prog_items[item, player] >= count
def has_sm_key_strict(self, item, player, count=1):
if self.world.keyshuffle[player] == 'universal':
if self.world.mode[player] == 'standard' and self.world.doorShuffle[player] == 'vanilla' and item == 'Small Key (Escape)':
return True # Cannot access the shop until escape is finished. This is safe because the key is manually placed in make_custom_item_pool
return self.can_buy_unlimited('Small Key (Universal)', player)
obtained = self.prog_items[item, player] - self.forced_keys[item, player]
return obtained >= count
def can_buy_unlimited(self, item, player):
for shop in self.world.shops[player]:
if shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(self):
@@ -1341,6 +1357,8 @@ class CollectionState(object):
def collect(self, item, event=False, location=None):
if location:
self.locations_checked.add(location)
if item and item.smallkey and location.forced_item is not None:
self.forced_keys[item.name, item.player] += 1
if not item:
return
changed = False
@@ -1533,10 +1551,10 @@ class Region(object):
or (item.bigkey and not self.world.bigkeyshuffle[item.player])
or (item.map and not self.world.mapshuffle[item.player])
or (item.compass and not self.world.compassshuffle[item.player]))
sewer_hack = self.world.mode[item.player] == 'standard' and item.name == 'Small Key (Escape)'
if sewer_hack or inside_dungeon_item:
# not all small keys to escape must be in escape
# sewer_hack = self.world.mode[item.player] == 'standard' and item.name == 'Small Key (Escape)'
if inside_dungeon_item:
return self.dungeon and self.dungeon.is_dungeon_item(item) and item.player == self.player
return True
def can_cause_bunny(self, player):
@@ -2168,6 +2186,9 @@ class Door(object):
return world.get_room(self.roomIndex, self.player).kind(self)
return None
def dungeon_name(self):
return self.entrance.parent_region.dungeon.name if self.entrance.parent_region.dungeon else 'Cave'
def __eq__(self, other):
return isinstance(other, self.__class__) and self.name == other.name
@@ -2846,6 +2867,8 @@ class Spoiler(object):
'door_shuffle': self.world.doorShuffle,
'intensity': self.world.intensity,
'door_type_mode': self.world.door_type_mode,
'trap_door_mode': self.world.trap_door_mode,
'key_logic': self.world.key_logic_algorithm,
'decoupledoors': self.world.decoupledoors,
'dungeon_counters': self.world.dungeon_counters,
'item_pool': self.world.difficulty,
@@ -3070,6 +3093,8 @@ class Spoiler(object):
if self.metadata['door_shuffle'][player] != 'vanilla':
outfile.write('Intensity:'.ljust(line_width) + '%s\n' % self.metadata['intensity'][player])
outfile.write('Door Type Mode:'.ljust(line_width) + '%s\n' % self.metadata['door_type_mode'][player])
outfile.write('Trap Door Mode:'.ljust(line_width) + '%s\n' % self.metadata['trap_door_mode'][player])
outfile.write('Key Logic Algorithm:'.ljust(line_width) + '%s\n' % self.metadata['key_logic'][player])
outfile.write('Decouple Doors:'.ljust(line_width) + '%s\n' % yn(self.metadata['decoupledoors'][player]))
outfile.write('Experimental:'.ljust(line_width) + '%s\n' % yn(self.metadata['experimental'][player]))
outfile.write('Dungeon Counters:'.ljust(line_width) + '%s\n' % self.metadata['dungeon_counters'][player])
@@ -3408,15 +3433,19 @@ orcrossed_mode = {"none": 0, "polar": 1, "grouped": 2, "limited": 3, "chaos": 4}
# byte 12: KMB? FF?? (keep similar, mixed/tile flip, bonk drops, flute spots)
flutespot_mode = {"vanilla": 0, "balanced": 1, "random": 2}
# byte 13: FBBB, TTSS (flute_mode, bow_mode, take_any, small_key_mode)
# byte 13: FBBB TTSS (flute_mode, bow_mode, take_any, small_key_mode)
flute_mode = {'normal': 0, 'active': 1}
keyshuffle_mode = {'none': 0, 'wild': 1, 'universal': 2} # reserved 8 modes?
take_any_mode = {'none': 0, 'random': 1, 'fixed': 2}
bow_mode = {'progressive': 0, 'silvers': 1, 'retro': 2, 'retro_silvers': 3}
# additions
# psuedoboots does not effect code
# sfx_shuffle and other adjust items does not effect settings code
# byte 14: POOT TKKK (pseudoboots, overworld_map, trap_door_mode, key_logic_algo)
overworld_map_mode = {'default': 0, 'compass': 1, 'map': 2}
trap_door_mode = {'vanilla': 0, 'optional': 1, 'boss': 2, 'oneway': 3}
key_logic_algo = {'default': 0, 'partial': 1, 'strict': 2}
# sfx_shuffle and other adjust items does not affect settings code
# Bump this when making changes that are not backwards compatible (nearly all of them)
settings_version = 1
@@ -3466,7 +3495,10 @@ class Settings(object):
| (0x20 if w.shuffle_bonk_drops[p] else 0) | (flutespot_mode[w.owFluteShuffle[p]] << 4),
(flute_mode[w.flute_mode[p]] << 7 | bow_mode[w.bow_mode[p]] << 4
| take_any_mode[w.take_any[p]] << 2 | keyshuffle_mode[w.keyshuffle[p]])
| take_any_mode[w.take_any[p]] << 2 | keyshuffle_mode[w.keyshuffle[p]]),
((0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 6
| trap_door_mode[w.trap_door_mode[p]] << 4 | key_logic_algo[w.key_logic_algorithm[p]]),
])
return base64.b64encode(code, "+-".encode()).decode()
@@ -3547,6 +3579,12 @@ class Settings(object):
args.take_any[p] = r(take_any_mode)[(settings[13] & 0xC) >> 2]
args.keyshuffle[p] = r(keyshuffle_mode)[settings[13] & 0x3]
if len(settings) > 14:
args.pseudoboots[p] = True if settings[14] & 0x80 else False
args.overworld_map[p] = r(overworld_map_mode)[(settings[14] & 0x60) >> 6]
args.trap_door_mode[p] = r(trap_door_mode)[(settings[14] & 0x14) >> 4]
args.key_logic_algorithm[p] = r(key_logic_algo)[settings[14] & 0x07]
class KeyRuleType(FastEnum):
WorstCase = 0