diff --git a/BaseClasses.py b/BaseClasses.py index 6cbc146e..358a40e0 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -470,7 +470,10 @@ class World(object): if self.has_beaten_game(state): return True - prog_locations = [location for location in self.get_locations() if location.item is not None and (location.item.advancement or location.event) and location not in state.locations_checked] + prog_locations = [location for location in self.get_locations() if location.item is not None + and (location.item.advancement or location.event + or self.goal[location.player] == 'completionist') + and location not in state.locations_checked] while prog_locations: sphere = [] @@ -1038,8 +1041,10 @@ class CollectionState(object): return self.prog_items[item, player] def everything(self, player): + all_locations = self.world.get_filled_locations(player) + all_locations.remove(self.world.get_location('Ganon', player)) return (len([x for x in self.locations_checked if x.player == player]) - >= len(self.world.get_filled_locations(player))) + >= len(all_locations)) def has_crystals(self, count, player): crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'] diff --git a/DoorShuffle.py b/DoorShuffle.py index 44685e5d..31e5d13b 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -6,7 +6,7 @@ from enum import unique, Flag from typing import DefaultDict, Dict, List from itertools import chain -from BaseClasses import RegionType, Region, Door, DoorType, Direction, Sector, CrystalBarrier, DungeonInfo, dungeon_keys +from BaseClasses import RegionType, Region, Door, DoorType, Sector, CrystalBarrier, DungeonInfo, dungeon_keys from BaseClasses import PotFlags, LocationType, Direction from Doors import reset_portals from Dungeons import dungeon_regions, region_starts, standard_starts, split_region_starts @@ -15,12 +15,12 @@ from Items import ItemFactory from RoomData import DoorKind, PairedDoor, reset_rooms from source.dungeon.DungeonStitcher import GenerationException, generate_dungeon from source.dungeon.DungeonStitcher import ExplorationState as ExplorationState2 -from DungeonGenerator import ExplorationState, convert_regions, pre_validate, determine_required_paths, drop_entrances +from DungeonGenerator import ExplorationState, convert_regions, determine_required_paths, drop_entrances from DungeonGenerator import create_dungeon_builders, split_dungeon_builder, simple_dungeon_builder, default_dungeon_entrances 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, check_bk_special +from KeyDoorShuffle import validate_bk_layout from Utils import ncr, kth_combination diff --git a/DungeonGenerator.py b/DungeonGenerator.py index 6a21817b..af0ef5c0 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -12,7 +12,7 @@ from typing import List from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, PolSlot, flooded_keys, Sector from BaseClasses import Hook, hook_from_door, Door from Regions import dungeon_events, flooded_keys_reverse -from Dungeons import dungeon_regions, split_region_starts +from Dungeons import split_region_starts from RoomData import DoorKind from source.dungeon.DungeonStitcher import generate_dungeon_find_proposal diff --git a/Main.py b/Main.py index 48ba4c81..2897f839 100644 --- a/Main.py +++ b/Main.py @@ -617,7 +617,8 @@ def create_playthrough(world): world = copy_world(world) # get locations containing progress items - prog_locations = [location for location in world.get_filled_locations() if location.item.advancement] + prog_locations = [location for location in world.get_filled_locations() if location.item.advancement + or world.goal[location.player] == 'completionist'] optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop', 'Skull Star Tile'] state_cache = [None] collection_spheres = [] @@ -654,6 +655,8 @@ def create_playthrough(world): for num, sphere in reversed(list(enumerate(collection_spheres))): to_delete = set() for location in sphere: + if world.goal[location.player] == 'completionist': + continue # every location for that player is required # we remove the item at location and check if game is still beatable logging.getLogger('').debug('Checking if %s (Player %d) is required to beat the game.', location.item.name, location.item.player) old_item = location.item diff --git a/Rom.py b/Rom.py index 1f1dfed8..f5a529fd 100644 --- a/Rom.py +++ b/Rom.py @@ -15,7 +15,7 @@ try: except ImportError: raise Exception('Could not load BPS module') -from BaseClasses import ShopType, Region, Location, Door, DoorType, RegionType, LocationType, Item +from BaseClasses import ShopType, Region, Location, Door, DoorType, RegionType, LocationType from DoorShuffle import compass_data, DROptions, boss_indicator, dungeon_portals from Dungeons import dungeon_music_addresses, dungeon_table from Regions import location_table, shop_to_location_table, retro_shops @@ -37,7 +37,7 @@ from source.dungeon.RoomList import Room0127 JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'fb7f9a0d501ba9ecd0a31066f9a0a973' +RANDOMIZERBASEHASH = '54cfc4c5e85c80fb2958cb458d36ad14' class JsonRom(object): @@ -1344,7 +1344,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): elif world.goal[player] in ['ganonhunt']: rom.write_byte(0x18003E, 0x05) # make ganon invincible until all triforce pieces collected elif world.goal[player] in ['completionist']: - rom.write_byte(0x18003E, 0x09) # make ganon invincible until everything is collected + rom.write_byte(0x18003E, 0x0a) # make ganon invincible until everything is collected else: rom.write_byte(0x18003E, 0x03) # make ganon invincible until all crystals and aga 2 are collected diff --git a/Rules.py b/Rules.py index 3f4e6b4d..0190aaa4 100644 --- a/Rules.py +++ b/Rules.py @@ -2075,7 +2075,8 @@ def add_key_logic_rules(world, player): for chest in d_logic.bk_chests: big_chest = world.get_location(chest.name, player) add_rule(big_chest, create_rule(d_logic.bk_name, player)) - if len(d_logic.bk_doors) == 0 and len(d_logic.bk_chests) <= 1: + if (len(d_logic.bk_doors) == 0 and len(d_logic.bk_chests) <= 1 + and world.accessibility[player] != 'locations'): set_always_allow(big_chest, allow_big_key_in_big_chest(d_logic.bk_name, player)) if world.keyshuffle[player] == 'universal': for d_name, layout in world.key_layout[player].items(): diff --git a/data/base2current.bps b/data/base2current.bps index a3983a65..e9709333 100644 Binary files a/data/base2current.bps and b/data/base2current.bps differ diff --git a/test/stats/EntranceShuffleStats.py b/test/stats/EntranceShuffleStats.py index 2be02071..25c0cbfd 100644 --- a/test/stats/EntranceShuffleStats.py +++ b/test/stats/EntranceShuffleStats.py @@ -1,5 +1,7 @@ +import os +import sys +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..')) import RaceRandom as random -import logging import time from collections import Counter, defaultdict @@ -106,7 +108,7 @@ def test_loop(tests, entrance_set, exit_set, ctr, shuffle_mode, main_mode, links # seed = 635441530 random.seed(seed) world = World(1, {1: shuffle_mode}, {1: 'vanilla'}, {1: 'noglitches'}, {1: main_mode}, {}, {}, {}, - {}, {}, {}, {}, {}, True, {}, {}, [], {}) + {}, {}, {}, {}, {}, True, {}, [], {}) world.customizer = False world.shufflelinks = {1: links} if world.mode[1] != 'inverted':