From 6e33181f05f280a7bd2c88a22df44ba82d6aca01 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Tue, 8 Jun 2021 08:58:16 -0500 Subject: [PATCH 01/16] Changed advancement_goal to a Range option --- Options.py | 9 ++++----- playerSettings.yaml | 7 ++----- test/minecraft/TestMinecraft.py | 2 +- worlds/minecraft/Rules.py | 11 ++++------- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/Options.py b/Options.py index 5200bed0..4717b3a7 100644 --- a/Options.py +++ b/Options.py @@ -386,11 +386,10 @@ factorio_options: typing.Dict[str, type(Option)] = {"max_science_pack": MaxScien "recipe_time": RecipeTime} -class AdvancementGoal(Choice): - option_few = 0 - option_normal = 1 - option_many = 2 - default = 1 +class AdvancementGoal(Range): + range_start = 0 + range_end = 87 + default = 30 class CombatDifficulty(Choice): diff --git a/playerSettings.yaml b/playerSettings.yaml index 2bc168c4..ef1aaac8 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -92,10 +92,7 @@ starting_items: burner-mining-drill: 19 stone-furnace: 19 # Minecraft options: -advancement_goal: # Number of advancements required (out of 92 total) to spawn the Ender Dragon and complete the game. - few: 0 # 30 advancements - normal: 1 # 50 - many: 0 # 70 +advancement_goal: 50 # Number of advancements required to spawn the Ender Dragon and complete the game (currently 87 max) combat_difficulty: # Modifies the level of items logically required for exploring dangerous areas and fighting bosses. easy: 0 normal: 1 @@ -109,7 +106,7 @@ include_insane_advancements: # Junk-fills extremely difficult advancements; this include_postgame_advancements: # Some advancements require defeating the Ender Dragon; this will junk-fill them so you won't have to finish to send some items. on: 0 off: 1 -shuffle_structures: # CURRENTLY DISABLED; enables shuffling of villages, outposts, fortresses, bastions, and end cities. +shuffle_structures: # Enables shuffling of villages, outposts, fortresses, bastions, and end cities. on: 0 off: 1 # A Link to the Past options: diff --git a/test/minecraft/TestMinecraft.py b/test/minecraft/TestMinecraft.py index 375de5f5..f2fdcd22 100644 --- a/test/minecraft/TestMinecraft.py +++ b/test/minecraft/TestMinecraft.py @@ -31,7 +31,7 @@ class TestMinecraft(TestBase): exclusion_pools = ['hard', 'insane', 'postgame'] for pool in exclusion_pools: setattr(self.world, f"include_{pool}_advancements", [False, False]) - setattr(self.world, "advancement_goal", [0, Options.AdvancementGoal(value=0)]) + setattr(self.world, "advancement_goal", [0, Options.AdvancementGoal(value=30)]) setattr(self.world, "shuffle_structures", [False, False]) setattr(self.world, "combat_difficulty", [0, Options.CombatDifficulty(value=1)]) minecraft_create_regions(self.world, 1) diff --git a/worlds/minecraft/Rules.py b/worlds/minecraft/Rules.py index b4a58476..4a1316b6 100644 --- a/worlds/minecraft/Rules.py +++ b/worlds/minecraft/Rules.py @@ -15,13 +15,10 @@ def set_rules(world: MultiWorld, player: int): (location.name not in postgame_advancements) and location.can_reach(state)] - # 92 total advancements, 16 are typically excluded, 1 is Free the End. Goal is to complete X advancements and then Free the End. - goal_map = { - 'few': 30, - 'normal': 50, - 'many': 70 - } - goal = goal_map[getattr(world, 'advancement_goal')[player].get_option_name()] + # 92 total advancements. Goal is to complete X advancements and then Free the End. + # There are 5 advancements which cannot be included for dragon spawning (4 postgame, Free the End) + # Hence the true maximum is (92 - 5) = 87 + goal = int(world.advancement_goal[player].value) can_complete = lambda state: len(reachable_locations(state)) >= goal and state.can_reach('The End', 'Region', player) and state.can_kill_ender_dragon(player) if world.logic[player] != 'nologic': From d7a46f089ef6aed3b4275191c64398b96aaf4701 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Tue, 8 Jun 2021 08:59:06 -0500 Subject: [PATCH 02/16] added get_option_name to Range option for spoiler generation --- Options.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Options.py b/Options.py index 4717b3a7..ca71cb3a 100644 --- a/Options.py +++ b/Options.py @@ -139,6 +139,8 @@ class Range(Option): return cls(data) return cls.from_text(str(data)) + def get_option_name(self): + return str(self.value) class OptionNameSet(Option): default = frozenset() From 059946d59e373e9b0dcac48137742b752a825e8c Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Tue, 15 Jun 2021 18:15:05 -0500 Subject: [PATCH 03/16] Shifted Minecraft to the new AutoWorld system --- Main.py | 12 +--- worlds/minecraft/Regions.py | 20 +----- worlds/minecraft/__init__.py | 127 +++++++++++++++++++---------------- 3 files changed, 71 insertions(+), 88 deletions(-) diff --git a/Main.py b/Main.py index c35f5b9d..2b3ec51b 100644 --- a/Main.py +++ b/Main.py @@ -23,8 +23,6 @@ from worlds.alttp.ItemPool import generate_itempool, difficulties, fill_prizes from Utils import output_path, parse_player_names, get_options, __version__, _version_tuple from worlds.hk import gen_hollow from worlds.hk import create_regions as hk_create_regions -from worlds.minecraft import gen_minecraft, fill_minecraft_slot_data, generate_mc_data -from worlds.minecraft.Regions import minecraft_create_regions from worlds.generic.Rules import locality_rules from worlds import Games, lookup_any_item_name_to_id, AutoWorld import Patch @@ -201,9 +199,6 @@ def main(args, seed=None): AutoWorld.call_all(world, "create_regions") - for player in world.minecraft_player_ids: - minecraft_create_regions(world, player) - for player in world.alttp_player_ids: if world.open_pyramid[player] == 'goal': world.open_pyramid[player] = world.goal[player] in {'crystals', 'ganontriforcehunt', @@ -268,9 +263,6 @@ def main(args, seed=None): AutoWorld.call_all(world, "generate_basic") - for player in world.minecraft_player_ids: - gen_minecraft(world, player) - logger.info("Running Item Plando") for item in world.itempool: @@ -526,7 +518,7 @@ def main(args, seed=None): option = getattr(world, option_name)[slot] slots_data[option_name] = int(option.value) for slot in world.minecraft_player_ids: - slot_data[slot] = fill_minecraft_slot_data(world, slot) + slot_data[slot] = AutoWorld.call_single(world, "fill_slot_data", slot) locations_data: Dict[int, Dict[int, Tuple[int, int]]] = {player: {} for player in world.player_ids} for location in world.get_filled_locations(): @@ -578,8 +570,6 @@ def main(args, seed=None): if multidata_task: multidata_task.result() # retrieve exception if one exists pool.shutdown() # wait for all queued tasks to complete - for player in world.minecraft_player_ids: # Doing this after shutdown prevents the .apmc from being generated if there's an error - generate_mc_data(world, player) if not args.skip_playthrough: logger.info('Calculating playthrough.') create_playthrough(world) diff --git a/worlds/minecraft/Regions.py b/worlds/minecraft/Regions.py index 2475e038..75469271 100644 --- a/worlds/minecraft/Regions.py +++ b/worlds/minecraft/Regions.py @@ -1,26 +1,10 @@ -from .Locations import MinecraftAdvancement, advancement_table -from BaseClasses import Region, Entrance, Location, MultiWorld, Item - -def minecraft_create_regions(world: MultiWorld, player: int): - - def MCRegion(region_name: str, exits=[]): - ret = Region(region_name, None, region_name, player) - ret.world = world - ret.locations = [ MinecraftAdvancement(player, loc_name, loc_data.id, ret) - for loc_name, loc_data in advancement_table.items() - if loc_data.region == region_name ] - for exit in exits: - ret.exits.append(Entrance(player, exit, ret)) - return ret - - world.regions += [MCRegion(*r) for r in mc_regions] +def link_minecraft_structures(world, player: int): + # Link mandatory connections first for (exit, region) in mandatory_connections: world.get_entrance(exit, player).connect(world.get_region(region, player)) -def link_minecraft_structures(world: MultiWorld, player: int): - # Get all unpaired exits and all regions without entrances (except the Menu) # This function is destructive on these lists. exits = [exit.name for r in world.regions if r.player == player for exit in r.exits if exit.connected_region == None] diff --git a/worlds/minecraft/__init__.py b/worlds/minecraft/__init__.py index f81c2bcf..4e27215f 100644 --- a/worlds/minecraft/__init__.py +++ b/worlds/minecraft/__init__.py @@ -1,81 +1,90 @@ -from random import Random from .Items import MinecraftItem, item_table, item_frequencies -from .Locations import exclusion_table, events_table -from .Regions import link_minecraft_structures +from .Locations import MinecraftAdvancement, advancement_table, exclusion_table, events_table +from .Regions import mc_regions, link_minecraft_structures from .Rules import set_rules -from BaseClasses import MultiWorld +from BaseClasses import Region, Entrance from Options import minecraft_options from ..AutoWorld import World +client_version = (0, 3) class MinecraftWorld(World): game: str = "Minecraft" -client_version = (0, 3) + def _get_mc_data(self): + exits = ["Overworld Structure 1", "Overworld Structure 2", "Nether Structure 1", "Nether Structure 2", + "The End Structure"] + return { + 'world_seed': self.world.slot_seeds[self.player].getrandbits(32), + # consistent and doesn't interfere with other generation + 'seed_name': self.world.seed_name, + 'player_name': self.world.get_player_names(self.player), + 'player_id': self.player, + 'client_version': client_version, + 'structures': {exit: self.world.get_entrance(exit, self.player).connected_region.name for exit in exits} + } -def get_mc_data(world: MultiWorld, player: int): - exits = ["Overworld Structure 1", "Overworld Structure 2", "Nether Structure 1", "Nether Structure 2", - "The End Structure"] - return { - 'world_seed': world.slot_seeds[player].getrandbits(32), - # consistent and doesn't interfere with other generation - 'seed_name': world.seed_name, - 'player_name': world.get_player_names(player), - 'player_id': player, - 'client_version': client_version, - 'structures': {exit: world.get_entrance(exit, player).connected_region.name for exit in exits} - } + def generate_basic(self): + link_minecraft_structures(self.world, self.player) + + pool = [] + for item_name, item_data in item_table.items(): + for count in range(item_frequencies.get(item_name, 1)): + pool.append(MinecraftItem(item_name, item_data.progression, item_data.code, self.player)) + + prefill_pool = {} + prefill_pool.update(events_table) + exclusion_pools = ['hard', 'insane', 'postgame'] + for key in exclusion_pools: + if not getattr(self.world, f"include_{key}_advancements")[self.player]: + prefill_pool.update(exclusion_table[key]) + + for loc_name, item_name in prefill_pool.items(): + item_data = item_table[item_name] + location = self.world.get_location(loc_name, self.player) + item = MinecraftItem(item_name, item_data.progression, item_data.code, self.player) + self.world.push_item(location, item, collect=False) + pool.remove(item) + location.event = item_data.progression + location.locked = True + + self.world.itempool += pool -def generate_mc_data(world: MultiWorld, player: int): - import base64, json - from Utils import output_path - - data = get_mc_data(world, player) - filename = f"AP_{world.seed_name}_P{player}_{world.get_player_names(player)}.apmc" - with open(output_path(filename), 'wb') as f: - f.write(base64.b64encode(bytes(json.dumps(data), 'utf-8'))) + def set_rules(self): + set_rules(self.world, self.player) -def fill_minecraft_slot_data(world: MultiWorld, player: int): - slot_data = get_mc_data(world, player) - for option_name in minecraft_options: - option = getattr(world, option_name)[player] - slot_data[option_name] = int(option.value) - return slot_data + def create_regions(self): + def MCRegion(region_name: str, exits=[]): + ret = Region(region_name, None, region_name, self.player) + ret.world = self.world + ret.locations = [ MinecraftAdvancement(self.player, loc_name, loc_data.id, ret) + for loc_name, loc_data in advancement_table.items() + if loc_data.region == region_name ] + for exit in exits: + ret.exits.append(Entrance(self.player, exit, ret)) + return ret + + self.world.regions += [MCRegion(*r) for r in mc_regions] -# Generates the item pool given the table and frequencies in Items.py. -def minecraft_gen_item_pool(world: MultiWorld, player: int): - pool = [] - for item_name, item_data in item_table.items(): - for count in range(item_frequencies.get(item_name, 1)): - pool.append(MinecraftItem(item_name, item_data.progression, item_data.code, player)) + def generate_output(self): + import base64, json + from Utils import output_path - prefill_pool = {} - prefill_pool.update(events_table) - exclusion_pools = ['hard', 'insane', 'postgame'] - for key in exclusion_pools: - if not getattr(world, f"include_{key}_advancements")[player]: - prefill_pool.update(exclusion_table[key]) - - for loc_name, item_name in prefill_pool.items(): - item_data = item_table[item_name] - location = world.get_location(loc_name, player) - item = MinecraftItem(item_name, item_data.progression, item_data.code, player) - world.push_item(location, item, collect=False) - pool.remove(item) - location.event = item_data.progression - location.locked = True - - world.itempool += pool + data = self._get_mc_data() + filename = f"AP_{self.world.seed_name}_P{self.player}_{self.world.get_player_names(self.player)}.apmc" + with open(output_path(filename), 'wb') as f: + f.write(base64.b64encode(bytes(json.dumps(data), 'utf-8'))) -# Generate Minecraft world. -def gen_minecraft(world: MultiWorld, player: int): - link_minecraft_structures(world, player) - minecraft_gen_item_pool(world, player) - set_rules(world, player) + def fill_slot_data(self): + slot_data = self._get_mc_data() + for option_name in minecraft_options: + option = getattr(self.world, option_name)[self.player] + slot_data[option_name] = int(option.value) + return slot_data From e49d10ab22cd62942fd8cf94be47b39f07ef0a08 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Tue, 15 Jun 2021 18:22:12 -0500 Subject: [PATCH 04/16] Clean up imports --- worlds/minecraft/Locations.py | 2 +- worlds/minecraft/Regions.py | 2 +- worlds/minecraft/Rules.py | 3 +-- worlds/minecraft/__init__.py | 5 +++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/worlds/minecraft/Locations.py b/worlds/minecraft/Locations.py index aff4c403..0cc75ce5 100644 --- a/worlds/minecraft/Locations.py +++ b/worlds/minecraft/Locations.py @@ -1,4 +1,4 @@ -from BaseClasses import Region, Entrance, Location, MultiWorld, Item +from BaseClasses import Location import typing class AdvData(typing.NamedTuple): diff --git a/worlds/minecraft/Regions.py b/worlds/minecraft/Regions.py index 75469271..91d21ca3 100644 --- a/worlds/minecraft/Regions.py +++ b/worlds/minecraft/Regions.py @@ -1,5 +1,5 @@ -def link_minecraft_structures(world, player: int): +def link_minecraft_structures(world, player): # Link mandatory connections first for (exit, region) in mandatory_connections: diff --git a/worlds/minecraft/Rules.py b/worlds/minecraft/Rules.py index 4a1316b6..b1c4da8f 100644 --- a/worlds/minecraft/Rules.py +++ b/worlds/minecraft/Rules.py @@ -1,9 +1,8 @@ from ..generic.Rules import set_rule from .Locations import exclusion_table, events_table -from BaseClasses import Region, Entrance, Location, MultiWorld, Item from Options import AdvancementGoal -def set_rules(world: MultiWorld, player: int): +def set_rules(world, player): def reachable_locations(state): postgame_advancements = set(exclusion_table['postgame'].keys()) diff --git a/worlds/minecraft/__init__.py b/worlds/minecraft/__init__.py index 4e27215f..2f74447f 100644 --- a/worlds/minecraft/__init__.py +++ b/worlds/minecraft/__init__.py @@ -73,13 +73,14 @@ class MinecraftWorld(World): def generate_output(self): - import base64, json + import json + from base64 import b64encode from Utils import output_path data = self._get_mc_data() filename = f"AP_{self.world.seed_name}_P{self.player}_{self.world.get_player_names(self.player)}.apmc" with open(output_path(filename), 'wb') as f: - f.write(base64.b64encode(bytes(json.dumps(data), 'utf-8'))) + f.write(b64encode(bytes(json.dumps(data), 'utf-8'))) def fill_slot_data(self): From b29d0b8276b70afd73d80700e406c53e165aec80 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Tue, 15 Jun 2021 22:27:51 -0500 Subject: [PATCH 05/16] Fixed some options in the Minecraft section of playerSettings --- playerSettings.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/playerSettings.yaml b/playerSettings.yaml index b6f6f6c3..e6df0628 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -99,10 +99,7 @@ Factorio: burner-mining-drill: 19 stone-furnace: 19 Minecraft: - advancement_goal: # Number of advancements required (out of 92 total) to spawn the Ender Dragon and complete the game. - few: 0 # 30 advancements - normal: 1 # 50 - many: 0 # 70 + advancement_goal: 50 # Number of advancements required (out of 92 total) to spawn the Ender Dragon and complete the game. combat_difficulty: # Modifies the level of items logically required for exploring dangerous areas and fighting bosses. easy: 0 normal: 1 @@ -116,7 +113,7 @@ Minecraft: include_postgame_advancements: # Some advancements require defeating the Ender Dragon; this will junk-fill them so you won't have to finish to send some items. on: 0 off: 1 - shuffle_structures: # CURRENTLY DISABLED; enables shuffling of villages, outposts, fortresses, bastions, and end cities. + shuffle_structures: # Enables shuffling of villages, outposts, fortresses, bastions, and end cities. on: 0 off: 1 A Link to the Past: From cd0306d513ccc38cce8858069a88e925940a28fc Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Wed, 16 Jun 2021 01:16:19 -0500 Subject: [PATCH 06/16] additional import cleanup --- worlds/minecraft/Items.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/minecraft/Items.py b/worlds/minecraft/Items.py index 89de300e..93561d3c 100644 --- a/worlds/minecraft/Items.py +++ b/worlds/minecraft/Items.py @@ -1,4 +1,4 @@ -from BaseClasses import Region, Entrance, Location, MultiWorld, Item +from BaseClasses import Item import typing class ItemData(typing.NamedTuple): From 16ae77ca1c71b9ad55c6e5f77ad4b6da644453c3 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Wed, 16 Jun 2021 20:24:36 -0500 Subject: [PATCH 07/16] Plandoing structures causes them to output in the spoiler log --- worlds/minecraft/Regions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/minecraft/Regions.py b/worlds/minecraft/Regions.py index 91d21ca3..3eb7619a 100644 --- a/worlds/minecraft/Regions.py +++ b/worlds/minecraft/Regions.py @@ -66,7 +66,7 @@ def link_minecraft_structures(world, player): for exit, struct in pairs.items(): world.get_entrance(exit, player).connect(world.get_region(struct, player)) - if world.shuffle_structures[player]: + if world.shuffle_structures[player] or world.plando_connections[player]: world.spoiler.set_entrance(exit, struct, 'entrance', player) # (Region name, list of exits) From f778a263a75a4ad4e1960c0ca3d4ff5b2a837d55 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Fri, 25 Jun 2021 12:37:06 -0500 Subject: [PATCH 08/16] Forbid villages from spawning in the Nether --- worlds/minecraft/Regions.py | 68 +++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/worlds/minecraft/Regions.py b/worlds/minecraft/Regions.py index 3eb7619a..77920b58 100644 --- a/worlds/minecraft/Regions.py +++ b/worlds/minecraft/Regions.py @@ -9,49 +9,36 @@ def link_minecraft_structures(world, player): # This function is destructive on these lists. exits = [exit.name for r in world.regions if r.player == player for exit in r.exits if exit.connected_region == None] structs = [r.name for r in world.regions if r.player == player and r.entrances == [] and r.name != 'Menu'] + exits_spoiler = exits[:] # copy the original order for the spoiler log try: assert len(exits) == len(structs) except AssertionError as e: # this should never happen - raise Exception(f"Could not obtain equal numbers of Minecraft exits and structures for player {player}") from e + raise Exception(f"Could not obtain equal numbers of Minecraft exits and structures for player {player} ({world.player_names[player]})") num_regions = len(exits) pairs = {} - def check_valid_connection(exit, struct): - if (exit in exits) and (struct in structs) and (exit not in pairs): - return True - return False - def set_pair(exit, struct): - try: - assert exit in exits - assert struct in structs - except AssertionError as e: - raise Exception(f"Invalid connection: {exit} => {struct} for player {player}") - pairs[exit] = struct - exits.remove(exit) - structs.remove(struct) + if (exit in exits) and (struct in structs) and (exit not in illegal_connections.get(struct, [])): + pairs[exit] = struct + exits.remove(exit) + structs.remove(struct) + else: + raise Exception(f"Invalid connection: {exit} => {struct} for player {player} ({world.player_names[player]})") - # Plando stuff. Remove any utilized exits/structs from the lists. - # Raise error if trying to put Nether Fortress in the End. + # Connect plando structures first if world.plando_connections[player]: - for connection in world.plando_connections[player]: - try: - if connection.entrance == 'The End Structure' and connection.exit == 'Nether Fortress': - raise Exception(f"Cannot place Nether Fortress in the End for player {player}") - set_pair(connection.entrance, connection.exit) - except Exception as e: - raise Exception(f"Could not connect using {connection}") from e + for conn in world.plando_connections[player]: + set_pair(conn.entrance, conn.exit) + # The algorithm tries to place the most restrictive structures first. This algorithm always works on the + # relatively small set of restrictions here, but does not work on all possible inputs with valid configurations. if world.shuffle_structures[player]: - # Can't put Nether Fortress in the End - if 'The End Structure' in exits and 'Nether Fortress' in structs: + structs.sort(reverse=True, key=lambda s: len(illegal_connections.get(s, []))) + for struct in structs[:]: try: - end_struct = world.random.choice([s for s in structs if s != 'Nether Fortress']) - set_pair('The End Structure', end_struct) - except IndexError as e: # should only happen if structs is emptied by plando - raise Exception(f"Plando forced Nether Fortress in the End for player {player}") from e - world.random.shuffle(structs) - for exit, struct in zip(exits[:], structs[:]): + exit = world.random.choice([e for e in exits if e not in illegal_connections.get(struct, [])]) + except IndexError: + raise Exception(f"No valid structure placements remaining for player {player} ({world.player_names[player]})") set_pair(exit, struct) else: # write remaining default connections for (exit, struct) in default_connections: @@ -61,13 +48,15 @@ def link_minecraft_structures(world, player): # Make sure we actually paired everything; might fail if plando try: assert len(exits) == len(structs) == 0 - except AssertionError as e: - raise Exception(f"Failed to connect all Minecraft structures for player {player}; check plando settings in yaml") from e + except AssertionError: + raise Exception(f"Failed to connect all Minecraft structures for player {player} ({world.player_names[player]})") - for exit, struct in pairs.items(): - world.get_entrance(exit, player).connect(world.get_region(struct, player)) + for exit in exits_spoiler: + world.get_entrance(exit, player).connect(world.get_region(pairs[exit], player)) if world.shuffle_structures[player] or world.plando_connections[player]: - world.spoiler.set_entrance(exit, struct, 'entrance', player) + world.spoiler.set_entrance(exit, pairs[exit], 'entrance', player) + + # (Region name, list of exits) mc_regions = [ @@ -96,3 +85,10 @@ default_connections = { ('Nether Structure 2', 'Bastion Remnant'), ('The End Structure', 'End City') } + +# Structure: illegal locations +illegal_connections = { + 'Village': ['Nether Structure 1', 'Nether Structure 2'], # remove this once raid spawning is fixed in the client + 'Nether Fortress': ['The End Structure'] +} + From 6837cd2917df56a6c5e09413aefb7c6f95a8181a Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Fri, 25 Jun 2021 12:43:59 -0500 Subject: [PATCH 09/16] Require the ability to respawn the dragon for all dragon-related advancements --- BaseClasses.py | 12 +++++++----- worlds/minecraft/Rules.py | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 59e16b44..9dfccb25 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -888,13 +888,15 @@ class CollectionState(object): return self.fortress_loot(player) and normal_kill def can_kill_ender_dragon(self, player: int): + # Since it is possible to kill the dragon without getting any of the advancements related to it, we need to require that it can be respawned. + respawn_dragon = self.can_reach('The Nether', 'Region', player) and self.has('Ingot Crafting', player) if self.combat_difficulty(player) == 'easy': - return self.has("Progressive Weapons", player, 3) and self.has("Progressive Armor", player, 2) and self.has('Archery', player) and \ - self.can_brew_potions(player) and self.can_enchant(player) + return respawn_dragon and self.has("Progressive Weapons", player, 3) and self.has("Progressive Armor", player, 2) and \ + self.has('Archery', player) and self.can_brew_potions(player) and self.can_enchant(player) if self.combat_difficulty(player) == 'hard': - return (self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player)) or \ - (self.has('Progressive Weapons', player, 1) and self.has('Bed', player)) - return self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player) and self.has('Archery', player) + return respawn_dragon and ((self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player)) or \ + (self.has('Progressive Weapons', player, 1) and self.has('Bed', player))) + return respawn_dragon and self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player) and self.has('Archery', player) def collect(self, item: Item, event: bool = False, location: Location = None) -> bool: diff --git a/worlds/minecraft/Rules.py b/worlds/minecraft/Rules.py index b1c4da8f..2051189d 100644 --- a/worlds/minecraft/Rules.py +++ b/worlds/minecraft/Rules.py @@ -41,7 +41,7 @@ def set_rules(world, player): set_rule(world.get_location("Very Very Frightening", player), lambda state: state.has("Channeling Book", player) and state.can_use_anvil(player) and state.can_enchant(player) and \ ((world.get_region('Village', player).entrances[0].parent_region.name != 'The End' and state.can_reach('Village', 'Region', player)) or state.can_reach('Zombie Doctor', 'Location', player))) # need villager into the overworld for lightning strike set_rule(world.get_location("Hot Stuff", player), lambda state: state.has("Bucket", player) and state.has_iron_ingots(player)) - set_rule(world.get_location("Free the End", player), lambda state: can_complete(state) and state.has('Ingot Crafting', player) and state.can_reach('The Nether', 'Region', player)) + set_rule(world.get_location("Free the End", player), lambda state: can_complete(state)) set_rule(world.get_location("A Furious Cocktail", player), lambda state: state.can_brew_potions(player) and state.has("Fishing Rod", player) and # Water Breathing state.can_reach('The Nether', 'Region', player) and # Regeneration, Fire Resistance, gold nuggets @@ -84,7 +84,7 @@ def set_rules(world, player): set_rule(world.get_location("Total Beelocation", player), lambda state: state.has("Silk Touch Book", player) and state.can_use_anvil(player) and state.can_enchant(player)) set_rule(world.get_location("Arbalistic", player), lambda state: state.craft_crossbow(player) and state.has("Piercing IV Book", player) and state.can_use_anvil(player) and state.can_enchant(player)) - set_rule(world.get_location("The End... Again...", player), lambda state: can_complete(state) and state.has("Ingot Crafting", player) and state.can_reach('The Nether', 'Region', player)) # furnace for glass, nether for ghast tears + set_rule(world.get_location("The End... Again...", player), lambda state: can_complete(state)) set_rule(world.get_location("Acquire Hardware", player), lambda state: state.has_iron_ingots(player)) set_rule(world.get_location("Not Quite \"Nine\" Lives", player), lambda state: state.can_piglin_trade(player) and state.has("Resource Blocks", player)) set_rule(world.get_location("Cover Me With Diamonds", player), lambda state: state.has("Progressive Armor", player, 2) and state.can_reach("Diamonds!", "Location", player)) From fd811bfd1bcaff36786703a1972041bcb1bea855 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Fri, 25 Jun 2021 13:02:45 -0500 Subject: [PATCH 10/16] fix minecraft tests --- test/minecraft/TestMinecraft.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/test/minecraft/TestMinecraft.py b/test/minecraft/TestMinecraft.py index f2fdcd22..aaad06ed 100644 --- a/test/minecraft/TestMinecraft.py +++ b/test/minecraft/TestMinecraft.py @@ -1,8 +1,7 @@ from test.TestBase import TestBase from BaseClasses import MultiWorld -from worlds.minecraft import minecraft_gen_item_pool -from worlds.minecraft.Regions import minecraft_create_regions, link_minecraft_structures -from worlds.minecraft.Rules import set_rules +from worlds import AutoWorld +from worlds.minecraft import MinecraftWorld from worlds.minecraft.Items import MinecraftItem, item_table import Options @@ -28,16 +27,16 @@ class TestMinecraft(TestBase): def setUp(self): self.world = MultiWorld(1) self.world.game[1] = "Minecraft" + self.world.worlds[1] = MinecraftWorld(self.world, 1) exclusion_pools = ['hard', 'insane', 'postgame'] for pool in exclusion_pools: setattr(self.world, f"include_{pool}_advancements", [False, False]) - setattr(self.world, "advancement_goal", [0, Options.AdvancementGoal(value=30)]) - setattr(self.world, "shuffle_structures", [False, False]) - setattr(self.world, "combat_difficulty", [0, Options.CombatDifficulty(value=1)]) - minecraft_create_regions(self.world, 1) - link_minecraft_structures(self.world, 1) - minecraft_gen_item_pool(self.world, 1) - set_rules(self.world, 1) + setattr(self.world, "advancement_goal", {1: Options.AdvancementGoal(30)}) + setattr(self.world, "shuffle_structures", {1: False}) + setattr(self.world, "combat_difficulty", {1: Options.CombatDifficulty(1)}) # normal + AutoWorld.call_single(self.world, "create_regions", 1) + AutoWorld.call_single(self.world, "generate_basic", 1) + AutoWorld.call_single(self.world, "set_rules", 1) def _get_items(self, item_pool, all_except): if all_except and len(all_except) > 0: From 719f9d7d481ff5b1a4ec6dfd2e504237c6d3a6d5 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Fri, 25 Jun 2021 13:57:09 -0500 Subject: [PATCH 11/16] Monsters Hunted made a hard-postgame advancement, so both flags must be set for it to be not junkfilled --- worlds/minecraft/Locations.py | 1 + 1 file changed, 1 insertion(+) diff --git a/worlds/minecraft/Locations.py b/worlds/minecraft/Locations.py index 0cc75ce5..4ca20092 100644 --- a/worlds/minecraft/Locations.py +++ b/worlds/minecraft/Locations.py @@ -114,6 +114,7 @@ exclusion_table = { "Two by Two": "100 XP", "Two Birds, One Arrow": "50 XP", "Arbalistic": "100 XP", + "Monsters Hunted": "100 XP", "Beaconator": "50 XP", "A Balanced Diet": "100 XP", "Uneasy Alliance": "100 XP", From 75891b2d38021f2a500800ba3572b58a8c949fd3 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Fri, 25 Jun 2021 19:59:44 -0500 Subject: [PATCH 12/16] fix tests again --- test/minecraft/TestMinecraft.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/minecraft/TestMinecraft.py b/test/minecraft/TestMinecraft.py index a970def5..0ff4bf3c 100644 --- a/test/minecraft/TestMinecraft.py +++ b/test/minecraft/TestMinecraft.py @@ -4,7 +4,7 @@ from BaseClasses import MultiWorld from worlds import AutoWorld from worlds.minecraft import MinecraftWorld from worlds.minecraft.Items import MinecraftItem, item_table -import Options +from worlds.minecraft.Options import AdvancementGoal, CombatDifficulty # Converts the name of an item into an item object def MCItemFactory(items, player: int): @@ -32,9 +32,9 @@ class TestMinecraft(TestBase): exclusion_pools = ['hard', 'insane', 'postgame'] for pool in exclusion_pools: setattr(self.world, f"include_{pool}_advancements", [False, False]) - setattr(self.world, "advancement_goal", {1: Options.AdvancementGoal(30)}) + setattr(self.world, "advancement_goal", {1: AdvancementGoal(30)}) setattr(self.world, "shuffle_structures", {1: False}) - setattr(self.world, "combat_difficulty", {1: Options.CombatDifficulty(1)}) # normal + setattr(self.world, "combat_difficulty", {1: CombatDifficulty(1)}) # normal AutoWorld.call_single(self.world, "create_regions", 1) AutoWorld.call_single(self.world, "generate_basic", 1) AutoWorld.call_single(self.world, "set_rules", 1) From 57c761aa7dd009c4270b145c67a67a78401bd2db Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Fri, 25 Jun 2021 20:15:07 -0500 Subject: [PATCH 13/16] Made AdvancementGoal a Range again also fixed the awful rules formatting --- worlds/minecraft/Options.py | 12 ++-- worlds/minecraft/Rules.py | 140 ++++++++++++------------------------ 2 files changed, 51 insertions(+), 101 deletions(-) diff --git a/worlds/minecraft/Options.py b/worlds/minecraft/Options.py index 8dd6ba2d..a61e11a5 100644 --- a/worlds/minecraft/Options.py +++ b/worlds/minecraft/Options.py @@ -1,13 +1,11 @@ import typing - -from Options import Choice, Option, Toggle +from Options import Choice, Option, Toggle, Range -class AdvancementGoal(Choice): - option_few = 0 - option_normal = 1 - option_many = 2 - default = 1 +class AdvancementGoal(Range): + range_start = 0 + range_end = 87 + default = 50 class CombatDifficulty(Choice): diff --git a/worlds/minecraft/Rules.py b/worlds/minecraft/Rules.py index 0ad36f99..ca7aced9 100644 --- a/worlds/minecraft/Rules.py +++ b/worlds/minecraft/Rules.py @@ -18,19 +18,15 @@ def set_rules(world: MultiWorld, player: int): # There are 5 advancements which cannot be included for dragon spawning (4 postgame, Free the End) # Hence the true maximum is (92 - 5) = 87 goal = int(world.advancement_goal[player].value) - can_complete = lambda state: len(reachable_locations(state)) >= goal and state.can_reach('The End', 'Region', - player) and state.can_kill_ender_dragon( - player) + can_complete = lambda state: len(reachable_locations(state)) >= goal and state.can_reach('The End', 'Region', player) and state.can_kill_ender_dragon(player) if world.logic[player] != 'nologic': world.completion_condition[player] = lambda state: state.has('Victory', player) - set_rule(world.get_entrance("Nether Portal", player), lambda state: state.has('Flint and Steel', player) and - (state.has('Bucket', player) or state.has( - 'Progressive Tools', player, 3)) and - state.has_iron_ingots(player)) - set_rule(world.get_entrance("End Portal", player), - lambda state: state.enter_stronghold(player) and state.has('3 Ender Pearls', player, 4)) + set_rule(world.get_entrance("Nether Portal", player), lambda state: state.has('Flint and Steel', player) and + (state.has('Bucket', player) or state.has('Progressive Tools', player, 3)) and + state.has_iron_ingots(player)) + set_rule(world.get_entrance("End Portal", player), lambda state: state.enter_stronghold(player) and state.has('3 Ender Pearls', player, 4)) set_rule(world.get_entrance("Overworld Structure 1", player), lambda state: state.can_adventure(player)) set_rule(world.get_entrance("Overworld Structure 2", player), lambda state: state.can_adventure(player)) set_rule(world.get_entrance("Nether Structure 1", player), lambda state: state.can_adventure(player)) @@ -52,49 +48,35 @@ def set_rules(world: MultiWorld, player: int): state.can_reach('Village', 'Region', player) and # Night Vision, Invisibility state.can_reach('Bring Home the Beacon', 'Location', player)) # Resistance set_rule(world.get_location("Best Friends Forever", player), lambda state: True) - set_rule(world.get_location("Bring Home the Beacon", player), - lambda state: state.can_kill_wither(player) and state.has_diamond_pickaxe(player) and - state.has("Ingot Crafting", player) and state.has("Resource Blocks", player)) - set_rule(world.get_location("Not Today, Thank You", player), - lambda state: state.has("Shield", player) and state.has_iron_ingots(player)) - set_rule(world.get_location("Isn't It Iron Pick", player), - lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player)) + set_rule(world.get_location("Bring Home the Beacon", player), lambda state: state.can_kill_wither(player) and + state.has_diamond_pickaxe(player) and state.has("Ingot Crafting", player) and state.has("Resource Blocks", player)) + set_rule(world.get_location("Not Today, Thank You", player), lambda state: state.has("Shield", player) and state.has_iron_ingots(player)) + set_rule(world.get_location("Isn't It Iron Pick", player), lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player)) set_rule(world.get_location("Local Brewery", player), lambda state: state.can_brew_potions(player)) set_rule(world.get_location("The Next Generation", player), lambda state: can_complete(state)) set_rule(world.get_location("Fishy Business", player), lambda state: state.has("Fishing Rod", player)) set_rule(world.get_location("Hot Tourist Destinations", player), lambda state: True) - set_rule(world.get_location("This Boat Has Legs", player), - lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and state.has("Fishing Rod", - player)) + set_rule(world.get_location("This Boat Has Legs", player), lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and state.has("Fishing Rod", player)) set_rule(world.get_location("Sniper Duel", player), lambda state: state.has("Archery", player)) set_rule(world.get_location("Nether", player), lambda state: True) set_rule(world.get_location("Great View From Up Here", player), lambda state: state.basic_combat(player)) - set_rule(world.get_location("How Did We Get Here?", player), - lambda state: state.can_brew_potions(player) and state.has_gold_ingots( - player) and # most effects; Absorption - state.can_reach('End City', 'Region', player) and state.can_reach('The Nether', 'Region', - player) and # Levitation; potion ingredients - state.has("Fishing Rod", player) and state.has("Archery", - player) and # Pufferfish, Nautilus Shells; spectral arrows + set_rule(world.get_location("How Did We Get Here?", player), lambda state: state.can_brew_potions(player) and + state.has_gold_ingots(player) and # Absorption + state.can_reach('End City', 'Region', player) and # Levitation + state.can_reach('The Nether', 'Region', player) and # potion ingredients + state.has("Fishing Rod", player) and state.has("Archery",player) and # Pufferfish, Nautilus Shells; spectral arrows state.can_reach("Bring Home the Beacon", "Location", player) and # Haste state.can_reach("Hero of the Village", "Location", player)) # Bad Omen, Hero of the Village - set_rule(world.get_location("Bullseye", player), - lambda state: state.has("Archery", player) and state.has("Progressive Tools", player, - 2) and state.has_iron_ingots(player)) + set_rule(world.get_location("Bullseye", player), lambda state: state.has("Archery", player) and state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player)) set_rule(world.get_location("Spooky Scary Skeleton", player), lambda state: state.basic_combat(player)) - set_rule(world.get_location("Two by Two", player), - lambda state: state.has_iron_ingots(player) and state.can_adventure( - player)) # shears > seagrass > turtles; nether > striders; gold carrots > horses skips ingots + set_rule(world.get_location("Two by Two", player), lambda state: state.has_iron_ingots(player) and state.can_adventure(player)) # shears > seagrass > turtles; nether > striders; gold carrots > horses skips ingots set_rule(world.get_location("Stone Age", player), lambda state: True) - set_rule(world.get_location("Two Birds, One Arrow", player), - lambda state: state.craft_crossbow(player) and state.can_enchant(player)) + set_rule(world.get_location("Two Birds, One Arrow", player), lambda state: state.craft_crossbow(player) and state.can_enchant(player)) set_rule(world.get_location("We Need to Go Deeper", player), lambda state: True) set_rule(world.get_location("Who's the Pillager Now?", player), lambda state: state.craft_crossbow(player)) set_rule(world.get_location("Getting an Upgrade", player), lambda state: state.has("Progressive Tools", player)) - set_rule(world.get_location("Tactical Fishing", player), - lambda state: state.has("Bucket", player) and state.has_iron_ingots(player)) - set_rule(world.get_location("Zombie Doctor", player), - lambda state: state.can_brew_potions(player) and state.has_gold_ingots(player)) + set_rule(world.get_location("Tactical Fishing", player), lambda state: state.has("Bucket", player) and state.has_iron_ingots(player)) + set_rule(world.get_location("Zombie Doctor", player), lambda state: state.can_brew_potions(player) and state.has_gold_ingots(player)) set_rule(world.get_location("The City at the End of the Game", player), lambda state: True) set_rule(world.get_location("Ice Bucket Challenge", player), lambda state: state.has_diamond_pickaxe(player)) set_rule(world.get_location("Remote Getaway", player), lambda state: True) @@ -106,71 +88,47 @@ def set_rules(world: MultiWorld, player: int): state.can_use_anvil(player) and state.can_enchant(player)) set_rule(world.get_location("The End... Again...", player), lambda state: can_complete(state)) set_rule(world.get_location("Acquire Hardware", player), lambda state: state.has_iron_ingots(player)) - set_rule(world.get_location("Not Quite \"Nine\" Lives", player), - lambda state: state.can_piglin_trade(player) and state.has("Resource Blocks", player)) - set_rule(world.get_location("Cover Me With Diamonds", player), - lambda state: state.has("Progressive Armor", player, 2) and state.can_reach("Diamonds!", "Location", - player)) + set_rule(world.get_location("Not Quite \"Nine\" Lives", player), lambda state: state.can_piglin_trade(player) and state.has("Resource Blocks", player)) + set_rule(world.get_location("Cover Me With Diamonds", player), lambda state: state.has("Progressive Armor", player, 2) and state.can_reach("Diamonds!", "Location", player)) set_rule(world.get_location("Sky's the Limit", player), lambda state: state.basic_combat(player)) - set_rule(world.get_location("Hired Help", player), - lambda state: state.has("Resource Blocks", player) and state.has_iron_ingots(player)) + set_rule(world.get_location("Hired Help", player), lambda state: state.has("Resource Blocks", player) and state.has_iron_ingots(player)) set_rule(world.get_location("Return to Sender", player), lambda state: True) - set_rule(world.get_location("Sweet Dreams", player), - lambda state: state.has("Bed", player) or state.can_reach('Village', 'Region', player)) - set_rule(world.get_location("You Need a Mint", player), - lambda state: can_complete(state) and state.has_bottle_mc(player)) + set_rule(world.get_location("Sweet Dreams", player), lambda state: state.has("Bed", player) or state.can_reach('Village', 'Region', player)) + set_rule(world.get_location("You Need a Mint", player), lambda state: can_complete(state) and state.has_bottle_mc(player)) set_rule(world.get_location("Adventure", player), lambda state: True) - set_rule(world.get_location("Monsters Hunted", player), - lambda state: can_complete(state) and state.can_kill_wither(player) and state.has("Fishing Rod", - player)) # pufferfish for Water Breathing + set_rule(world.get_location("Monsters Hunted", player), lambda state: can_complete(state) and state.can_kill_wither(player) and state.has("Fishing Rod", player)) # pufferfish for Water Breathing set_rule(world.get_location("Enchanter", player), lambda state: state.can_enchant(player)) set_rule(world.get_location("Voluntary Exile", player), lambda state: state.basic_combat(player)) set_rule(world.get_location("Eye Spy", player), lambda state: state.enter_stronghold(player)) set_rule(world.get_location("The End", player), lambda state: True) - set_rule(world.get_location("Serious Dedication", player), - lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots( - player)) + set_rule(world.get_location("Serious Dedication", player), lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots(player)) set_rule(world.get_location("Postmortal", player), lambda state: state.complete_raid(player)) set_rule(world.get_location("Monster Hunter", player), lambda state: True) set_rule(world.get_location("Adventuring Time", player), lambda state: state.can_adventure(player)) set_rule(world.get_location("A Seedy Place", player), lambda state: True) set_rule(world.get_location("Those Were the Days", player), lambda state: True) set_rule(world.get_location("Hero of the Village", player), lambda state: state.complete_raid(player)) - set_rule(world.get_location("Hidden in the Depths", player), - lambda state: state.can_brew_potions(player) and state.has("Bed", player) and state.has_diamond_pickaxe( - player)) # bed mining :) - set_rule(world.get_location("Beaconator", player), - lambda state: state.can_kill_wither(player) and state.has_diamond_pickaxe(player) and + set_rule(world.get_location("Hidden in the Depths", player), lambda state: state.can_brew_potions(player) and state.has("Bed", player) and state.has_diamond_pickaxe(player)) # bed mining :) + set_rule(world.get_location("Beaconator", player), lambda state: state.can_kill_wither(player) and state.has_diamond_pickaxe(player) and state.has("Ingot Crafting", player) and state.has("Resource Blocks", player)) set_rule(world.get_location("Withering Heights", player), lambda state: state.can_kill_wither(player)) - set_rule(world.get_location("A Balanced Diet", player), - lambda state: state.has_bottle_mc(player) and state.has_gold_ingots(player) and # honey bottle; gapple - state.has("Resource Blocks", player) and state.can_reach('The End', 'Region', - player)) # notch apple, chorus fruit + set_rule(world.get_location("A Balanced Diet", player), lambda state: state.has_bottle_mc(player) and state.has_gold_ingots(player) and # honey bottle; gapple + state.has("Resource Blocks", player) and state.can_reach('The End', 'Region', player)) # notch apple, chorus fruit set_rule(world.get_location("Subspace Bubble", player), lambda state: state.has_diamond_pickaxe(player)) set_rule(world.get_location("Husbandry", player), lambda state: True) - set_rule(world.get_location("Country Lode, Take Me Home", player), - lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots( - player)) - set_rule(world.get_location("Bee Our Guest", player), - lambda state: state.has("Campfire", player) and state.has_bottle_mc(player)) + set_rule(world.get_location("Country Lode, Take Me Home", player), lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state.has_gold_ingots(player)) + set_rule(world.get_location("Bee Our Guest", player), lambda state: state.has("Campfire", player) and state.has_bottle_mc(player)) set_rule(world.get_location("What a Deal!", player), lambda state: True) - set_rule(world.get_location("Uneasy Alliance", player), - lambda state: state.has_diamond_pickaxe(player) and state.has('Fishing Rod', player)) - set_rule(world.get_location("Diamonds!", player), - lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player)) - set_rule(world.get_location("A Terrible Fortress", player), - lambda state: True) # since you don't have to fight anything + set_rule(world.get_location("Uneasy Alliance", player), lambda state: state.has_diamond_pickaxe(player) and state.has('Fishing Rod', player)) + set_rule(world.get_location("Diamonds!", player), lambda state: state.has("Progressive Tools", player, 2) and state.has_iron_ingots(player)) + set_rule(world.get_location("A Terrible Fortress", player), lambda state: True) # since you don't have to fight anything set_rule(world.get_location("A Throwaway Joke", player), lambda state: True) # kill drowned set_rule(world.get_location("Minecraft", player), lambda state: True) - set_rule(world.get_location("Sticky Situation", player), - lambda state: state.has("Campfire", player) and state.has_bottle_mc(player)) + set_rule(world.get_location("Sticky Situation", player), lambda state: state.has("Campfire", player) and state.has_bottle_mc(player)) set_rule(world.get_location("Ol' Betsy", player), lambda state: state.craft_crossbow(player)) - set_rule(world.get_location("Cover Me in Debris", player), - lambda state: state.has("Progressive Armor", player, 2) and + set_rule(world.get_location("Cover Me in Debris", player), lambda state: state.has("Progressive Armor", player, 2) and state.has("8 Netherite Scrap", player, 2) and state.has("Ingot Crafting", player) and - state.can_reach("Diamonds!", "Location", player) and state.can_reach("Hidden in the Depths", - "Location", player)) + state.can_reach("Diamonds!", "Location", player) and state.can_reach("Hidden in the Depths", "Location", player)) set_rule(world.get_location("The End?", player), lambda state: True) set_rule(world.get_location("The Parrots and the Bats", player), lambda state: True) set_rule(world.get_location("A Complete Catalogue", player), lambda state: True) # kill fish for raw @@ -178,19 +136,13 @@ def set_rules(world: MultiWorld, player: int): set_rule(world.get_location("Time to Mine!", player), lambda state: True) set_rule(world.get_location("Hot Topic", player), lambda state: state.has("Ingot Crafting", player)) set_rule(world.get_location("Bake Bread", player), lambda state: True) - set_rule(world.get_location("The Lie", player), - lambda state: state.has_iron_ingots(player) and state.has("Bucket", player)) - set_rule(world.get_location("On a Rail", player), - lambda state: state.has_iron_ingots(player) and state.has('Progressive Tools', player, 2)) # powered rails + set_rule(world.get_location("The Lie", player), lambda state: state.has_iron_ingots(player) and state.has("Bucket", player)) + set_rule(world.get_location("On a Rail", player), lambda state: state.has_iron_ingots(player) and state.has('Progressive Tools', player, 2)) # powered rails set_rule(world.get_location("Time to Strike!", player), lambda state: True) set_rule(world.get_location("Cow Tipper", player), lambda state: True) - set_rule(world.get_location("When Pigs Fly", player), - lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and state.has("Fishing Rod", - player) and state.can_adventure( - player)) - set_rule(world.get_location("Overkill", player), lambda state: state.can_brew_potions(player) and ( - state.has("Progressive Weapons", player) or state.can_reach('The Nether', 'Region', - player))) # strength 1 + stone axe crit OR strength 2 + wood axe crit + set_rule(world.get_location("When Pigs Fly", player), lambda state: (state.fortress_loot(player) or state.complete_raid(player)) and + state.has("Fishing Rod", player) and state.can_adventure(player)) + set_rule(world.get_location("Overkill", player), lambda state: state.can_brew_potions(player) and + (state.has("Progressive Weapons", player) or state.can_reach('The Nether', 'Region', player))) # strength 1 + stone axe crit OR strength 2 + wood axe crit set_rule(world.get_location("Librarian", player), lambda state: state.has("Enchanting", player)) - set_rule(world.get_location("Overpowered", player), - lambda state: state.has("Resource Blocks", player) and state.has_gold_ingots(player)) + set_rule(world.get_location("Overpowered", player), lambda state: state.has("Resource Blocks", player) and state.has_gold_ingots(player)) From f918d340981bf384b0dc9d6453572ccfca32baf9 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Mon, 28 Jun 2021 14:41:33 -0500 Subject: [PATCH 14/16] un-disabled villages spawning in nether --- worlds/minecraft/Regions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/worlds/minecraft/Regions.py b/worlds/minecraft/Regions.py index 77920b58..c35d2b7c 100644 --- a/worlds/minecraft/Regions.py +++ b/worlds/minecraft/Regions.py @@ -88,7 +88,6 @@ default_connections = { # Structure: illegal locations illegal_connections = { - 'Village': ['Nether Structure 1', 'Nether Structure 2'], # remove this once raid spawning is fixed in the client 'Nether Fortress': ['The End Structure'] } From e37ca97bde7762b98d507dcd018948c0e1535228 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Fri, 2 Jul 2021 10:10:35 -0500 Subject: [PATCH 15/16] add bee traps --- worlds/minecraft/Items.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/worlds/minecraft/Items.py b/worlds/minecraft/Items.py index 93561d3c..d6f5e2f0 100644 --- a/worlds/minecraft/Items.py +++ b/worlds/minecraft/Items.py @@ -46,6 +46,7 @@ item_table = { "8 Gold Ore": ItemData(45032, False), "Rotten Flesh": ItemData(45033, False), "Single Arrow": ItemData(45034, False), + "Bee Trap (Minecraft)": ItemData(45100, False), "Victory": ItemData(0, True) } @@ -67,8 +68,9 @@ item_frequencies = { "4 Lapis Lazuli": 2, "16 Porkchops": 8, "8 Gold Ore": 4, - "Rotten Flesh": 4, - "Single Arrow": 0 + "Rotten Flesh": 2, + "Single Arrow": 0, + "Bee Trap (Minecraft)": 2 } lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code} From 1e9047086269ffd3508b65b908b9a5e93f3cc962 Mon Sep 17 00:00:00 2001 From: espeon65536 Date: Fri, 2 Jul 2021 10:12:06 -0500 Subject: [PATCH 16/16] increment MC client version and network_data_package version --- worlds/__init__.py | 2 +- worlds/minecraft/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/__init__.py b/worlds/__init__.py index 5222f2cb..f25fb225 100644 --- a/worlds/__init__.py +++ b/worlds/__init__.py @@ -29,7 +29,7 @@ assert len(lookup_any_location_name_to_id) == len(lookup_any_location_id_to_name network_data_package = {"lookup_any_location_id_to_name": lookup_any_location_id_to_name, "lookup_any_item_id_to_name": lookup_any_item_id_to_name, - "version": 6} + "version": 7} @enum.unique diff --git a/worlds/minecraft/__init__.py b/worlds/minecraft/__init__.py index 902c11b3..6cb12ad8 100644 --- a/worlds/minecraft/__init__.py +++ b/worlds/minecraft/__init__.py @@ -7,7 +7,7 @@ from BaseClasses import Region, Entrance from .Options import minecraft_options from ..AutoWorld import World -client_version = (0, 3) +client_version = (0, 4) class MinecraftWorld(World): game: str = "Minecraft"