Completionist fixes

This commit is contained in:
aerinon
2022-12-07 15:57:51 -07:00
parent 9bf0391e60
commit e67ff4d5dc
8 changed files with 24 additions and 13 deletions

View File

@@ -470,7 +470,10 @@ class World(object):
if self.has_beaten_game(state): if self.has_beaten_game(state):
return True 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: while prog_locations:
sphere = [] sphere = []
@@ -1038,8 +1041,10 @@ class CollectionState(object):
return self.prog_items[item, player] return self.prog_items[item, player]
def everything(self, 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]) 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): def has_crystals(self, count, player):
crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'] crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7']

View File

@@ -6,7 +6,7 @@ from enum import unique, Flag
from typing import DefaultDict, Dict, List from typing import DefaultDict, Dict, List
from itertools import chain 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 BaseClasses import PotFlags, LocationType, Direction
from Doors import reset_portals from Doors import reset_portals
from Dungeons import dungeon_regions, region_starts, standard_starts, split_region_starts 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 RoomData import DoorKind, PairedDoor, reset_rooms
from source.dungeon.DungeonStitcher import GenerationException, generate_dungeon from source.dungeon.DungeonStitcher import GenerationException, generate_dungeon
from source.dungeon.DungeonStitcher import ExplorationState as ExplorationState2 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 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 dungeon_portals, dungeon_drops, connect_doors, count_reserved_locations
from DungeonGenerator import valid_region_to_explore from DungeonGenerator import valid_region_to_explore
from KeyDoorShuffle import analyze_dungeon, build_key_layout, validate_key_layout, determine_prize_lock 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 from Utils import ncr, kth_combination

View File

@@ -12,7 +12,7 @@ from typing import List
from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, PolSlot, flooded_keys, Sector from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, PolSlot, flooded_keys, Sector
from BaseClasses import Hook, hook_from_door, Door from BaseClasses import Hook, hook_from_door, Door
from Regions import dungeon_events, flooded_keys_reverse 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 RoomData import DoorKind
from source.dungeon.DungeonStitcher import generate_dungeon_find_proposal from source.dungeon.DungeonStitcher import generate_dungeon_find_proposal

View File

@@ -617,7 +617,8 @@ def create_playthrough(world):
world = copy_world(world) world = copy_world(world)
# get locations containing progress items # 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'] optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop', 'Skull Star Tile']
state_cache = [None] state_cache = [None]
collection_spheres = [] collection_spheres = []
@@ -654,6 +655,8 @@ def create_playthrough(world):
for num, sphere in reversed(list(enumerate(collection_spheres))): for num, sphere in reversed(list(enumerate(collection_spheres))):
to_delete = set() to_delete = set()
for location in sphere: 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 # 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) logging.getLogger('').debug('Checking if %s (Player %d) is required to beat the game.', location.item.name, location.item.player)
old_item = location.item old_item = location.item

6
Rom.py
View File

@@ -15,7 +15,7 @@ try:
except ImportError: except ImportError:
raise Exception('Could not load BPS module') 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 DoorShuffle import compass_data, DROptions, boss_indicator, dungeon_portals
from Dungeons import dungeon_music_addresses, dungeon_table from Dungeons import dungeon_music_addresses, dungeon_table
from Regions import location_table, shop_to_location_table, retro_shops from Regions import location_table, shop_to_location_table, retro_shops
@@ -37,7 +37,7 @@ from source.dungeon.RoomList import Room0127
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'fb7f9a0d501ba9ecd0a31066f9a0a973' RANDOMIZERBASEHASH = '54cfc4c5e85c80fb2958cb458d36ad14'
class JsonRom(object): class JsonRom(object):
@@ -1344,7 +1344,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
elif world.goal[player] in ['ganonhunt']: elif world.goal[player] in ['ganonhunt']:
rom.write_byte(0x18003E, 0x05) # make ganon invincible until all triforce pieces collected rom.write_byte(0x18003E, 0x05) # make ganon invincible until all triforce pieces collected
elif world.goal[player] in ['completionist']: 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: else:
rom.write_byte(0x18003E, 0x03) # make ganon invincible until all crystals and aga 2 are collected rom.write_byte(0x18003E, 0x03) # make ganon invincible until all crystals and aga 2 are collected

View File

@@ -2075,7 +2075,8 @@ def add_key_logic_rules(world, player):
for chest in d_logic.bk_chests: for chest in d_logic.bk_chests:
big_chest = world.get_location(chest.name, player) big_chest = world.get_location(chest.name, player)
add_rule(big_chest, create_rule(d_logic.bk_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)) set_always_allow(big_chest, allow_big_key_in_big_chest(d_logic.bk_name, player))
if world.keyshuffle[player] == 'universal': if world.keyshuffle[player] == 'universal':
for d_name, layout in world.key_layout[player].items(): for d_name, layout in world.key_layout[player].items():

Binary file not shown.

View File

@@ -1,5 +1,7 @@
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
import RaceRandom as random import RaceRandom as random
import logging
import time import time
from collections import Counter, defaultdict 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 # seed = 635441530
random.seed(seed) random.seed(seed)
world = World(1, {1: shuffle_mode}, {1: 'vanilla'}, {1: 'noglitches'}, {1: main_mode}, {}, {}, {}, world = World(1, {1: shuffle_mode}, {1: 'vanilla'}, {1: 'noglitches'}, {1: main_mode}, {}, {}, {},
{}, {}, {}, {}, {}, True, {}, {}, [], {}) {}, {}, {}, {}, {}, True, {}, [], {})
world.customizer = False world.customizer = False
world.shufflelinks = {1: links} world.shufflelinks = {1: links}
if world.mode[1] != 'inverted': if world.mode[1] != 'inverted':