From ebd86e80d79675a526854edd04f795b925568ff8 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 12 Apr 2024 17:32:35 -0600 Subject: [PATCH] fix(logic): exception for vanilla mire key door layout, crystal switches can be reached with two keys, just can't tell which one was chosen --- BaseClasses.py | 4 +++- DoorShuffle.py | 23 +++++++++++++++++++++-- RELEASENOTES.md | 2 +- Rules.py | 24 ++++++++++++++++++++---- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index ab4f4158..64b33852 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -695,7 +695,7 @@ class CollectionState(object): @staticmethod def valid_crystal(door, new_crystal_state): return (not door.crystal or door.crystal == CrystalBarrier.Either or new_crystal_state == CrystalBarrier.Either - or new_crystal_state == door.crystal) + or new_crystal_state == door.crystal or door.alternative_crystal_rule) def check_key_doors_in_dungeons(self, rrp, player): for dungeon_name, checklist in self.dungeons_to_check[player].items(): @@ -1828,6 +1828,7 @@ class Door(object): self.bigKey = False # There's a big key door on this side self.ugly = False # Indicates that it can't be seen from the front (e.g. back of a big key door) self.crystal = CrystalBarrier.Null # How your crystal state changes if you use this door + self.alternative_crystal_rule = False self.req_event = None # if a dungeon event is required for this door - swamp palace mostly self.controller = None self.dependents = [] @@ -3209,3 +3210,4 @@ class KeyRuleType(FastEnum): WorstCase = 0 AllowSmall = 1 Lock = 2 + CrystalAlternative = 3 diff --git a/DoorShuffle.py b/DoorShuffle.py index c961dee7..e6fd8baa 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -20,7 +20,7 @@ from DungeonGenerator import create_dungeon_builders, split_dungeon_builder, sim from DungeonGenerator import dungeon_portals, dungeon_drops, connect_doors, count_reserved_locations from DungeonGenerator import valid_region_to_explore from KeyDoorShuffle import analyze_dungeon, build_key_layout, validate_key_layout, determine_prize_lock -from KeyDoorShuffle import validate_bk_layout +from KeyDoorShuffle import validate_bk_layout, DoorRules from Utils import ncr, kth_combination @@ -263,7 +263,7 @@ def vanilla_key_logic(world, player): log_key_logic(builder.name, key_layout.key_logic) # special adjustments for vanilla if world.keyshuffle[player] != 'universal': - if world.mode[player] != 'standard' and world.dropshuffle[player] == 'none' : + if world.mode[player] != 'standard' and world.dropshuffle[player] == 'none': # adjust hc doors def adjust_hc_door(door_rule): if door_rule.new_rules[KeyRuleType.WorstCase] == 3: @@ -279,9 +279,28 @@ def vanilla_key_logic(world, player): if pod_front.new_rules[KeyRuleType.WorstCase] == 6: pod_front.new_rules[KeyRuleType.WorstCase] = 1 pod_front.small_key_num = 1 + # adjust mire key logic - this currently cannot be done dynamically + create_alternative_door_rules('Mire Hub Upper Blue Barrier', 2, 'Misery Mire', world, player) + create_alternative_door_rules('Mire Hub Lower Blue Barrier', 2, 'Misery Mire', world, player) + create_alternative_door_rules('Mire Hub Right Blue Barrier', 2, 'Misery Mire', world, player) + create_alternative_door_rules('Mire Hub Top Blue Barrier', 2, 'Misery Mire', world, player) + create_alternative_door_rules('Mire Hub Switch Blue Barrier N', 2, 'Misery Mire', world, player) + create_alternative_door_rules('Mire Hub Switch Blue Barrier S', 2, 'Misery Mire', world, player) + create_alternative_door_rules('Mire Map Spot Blue Barrier', 2, 'Misery Mire', world, player) + create_alternative_door_rules('Mire Map Spike Side Blue Barrier', 2, 'Misery Mire', world, player) + create_alternative_door_rules('Mire Crystal Dead End Left Barrier', 2, 'Misery Mire', world, player) + create_alternative_door_rules('Mire Crystal Dead End Right Barrier', 2, 'Misery Mire', world, player) # gt logic? I'm unsure it needs adjusting +def create_alternative_door_rules(door, amount, dungeon, world, player): + rules = DoorRules(0, True) + world.key_logic[player][dungeon].door_rules[door] = rules + rules.new_rules[KeyRuleType.CrystalAlternative] = amount + world.get_door(door, player).alternative_crystal_rule = True + + + def validate_vanilla_reservation(dungeon, world, player): return validate_key_layout(world.key_layout[player][dungeon.name], world, player) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3b65289d..795bad41 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -142,7 +142,7 @@ These are now independent of retro mode and have three options: None, Random, an # Patch Notes * 1.4.1.10u - * ? + * Vanilla key logic: Fix for vanilla layout Misery Mire which allows more complex key logic. Locations blocked by crystal switch access is only locked by 2 keys thanks to that being the minimun in Mire to reach one of two crystal switches. * Autotracking: Fix for chest turn counter with chest containing multiworld items (Thanks Hiimcody) * Rom: Code prettification and fixing byte designations by Codemann * 1.4.1.9u diff --git a/Rules.py b/Rules.py index b2eb91f1..ce6eb08a 100644 --- a/Rules.py +++ b/Rules.py @@ -2332,10 +2332,13 @@ def add_key_logic_rules(world, player): for d_name, d_logic in key_logic.items(): for door_name, rule in d_logic.door_rules.items(): door_entrance = world.get_entrance(door_name, player) - add_rule(door_entrance, eval_func(door_name, d_name, player)) - if door_entrance.door.dependents: - for dep in door_entrance.door.dependents: - add_rule(dep.entrance, eval_func(door_name, d_name, player)) + if not door_entrance.door.smallKey and door_entrance.door.crystal == CrystalBarrier.Blue: + add_rule(door_entrance, eval_alternative_crystal(door_name, d_name, player), 'or') + else: + add_rule(door_entrance, eval_func(door_name, d_name, player)) + if door_entrance.door.dependents: + for dep in door_entrance.door.dependents: + add_rule(dep.entrance, eval_func(door_name, d_name, player)) for location in d_logic.bk_restricted: if not location.forced_item: forbid_item(location, d_logic.bk_name, player) @@ -2427,6 +2430,15 @@ def eval_small_key_door_strict_main(state, door_name, dungeon, player): return state.has_sm_key_strict(key_layout.key_logic.small_key_name, player, number) +def eval_alternative_crystal_main(state, door_name, dungeon, player): + key_logic = state.world.key_logic[player][dungeon] + door_rule = key_logic.door_rules[door_name] + for ruleType, number in door_rule.new_rules.items(): + if ruleType == KeyRuleType.CrystalAlternative: + return state.has_sm_key(key_logic.small_key_name, player, number) + return False + + def eval_small_key_door(door_name, dungeon, player): return lambda state: eval_small_key_door_main(state, door_name, dungeon, player) @@ -2439,6 +2451,10 @@ def eval_small_key_door_strict(door_name, dungeon, player): return lambda state: eval_small_key_door_strict_main(state, door_name, dungeon, player) +def eval_alternative_crystal(door_name, dungeon, player): + return lambda state: eval_alternative_crystal_main(state, door_name, dungeon, player) + + def allow_big_key_in_big_chest(bk_name, player): return lambda state, item: item.name == bk_name and item.player == player