diff --git a/BaseClasses.py b/BaseClasses.py index c96df3f4..9496785e 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -741,91 +741,6 @@ class CollectionState(object): def can_bomb_clip(self, region: Region, player: int) -> bool: return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player) - # Minecraft logic functions - def has_iron_ingots(self, player: int): - return self.has('Progressive Tools', player) and self.has('Ingot Crafting', player) - - def has_gold_ingots(self, player: int): - return self.has('Ingot Crafting', player) and (self.has('Progressive Tools', player, 2) or self.can_reach('The Nether', 'Region', player)) - - def has_diamond_pickaxe(self, player: int): - return self.has('Progressive Tools', player, 3) and self.has_iron_ingots(player) - - def craft_crossbow(self, player: int): - return self.has('Archery', player) and self.has_iron_ingots(player) - - def has_bottle_mc(self, player: int): - return self.has('Bottles', player) and self.has('Ingot Crafting', player) - - def can_enchant(self, player: int): - return self.has('Enchanting', player) and self.has_diamond_pickaxe(player) # mine obsidian and lapis - - def can_use_anvil(self, player: int): - return self.has('Enchanting', player) and self.has('Resource Blocks', player) and self.has_iron_ingots(player) - - def fortress_loot(self, player: int): # saddles, blaze rods, wither skulls - return self.can_reach('Nether Fortress', 'Region', player) and self.basic_combat(player) - - def can_brew_potions(self, player: int): - return self.fortress_loot(player) and self.has('Brewing', player) and self.has_bottle_mc(player) - - def can_piglin_trade(self, player: int): - return self.has_gold_ingots(player) and (self.can_reach('The Nether', 'Region', player) or self.can_reach('Bastion Remnant', 'Region', player)) - - def enter_stronghold(self, player: int): - return self.fortress_loot(player) and self.has('Brewing', player) and self.has('3 Ender Pearls', player) - - # Difficulty-dependent functions - def combat_difficulty(self, player: int): - return self.world.combat_difficulty[player].get_option_name() - - def can_adventure(self, player: int): - if self.combat_difficulty(player) == 'easy': - return self.has('Progressive Weapons', player, 2) and self.has_iron_ingots(player) - elif self.combat_difficulty(player) == 'hard': - return True - return self.has('Progressive Weapons', player) and (self.has('Ingot Crafting', player) or self.has('Campfire', player)) - - def basic_combat(self, player: int): - if self.combat_difficulty(player) == 'easy': - return self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player) and \ - self.has('Shield', player) and self.has_iron_ingots(player) - elif self.combat_difficulty(player) == 'hard': - return True - return self.has('Progressive Weapons', player) and (self.has('Progressive Armor', player) or self.has('Shield', player)) and self.has_iron_ingots(player) - - def complete_raid(self, player: int): - reach_regions = self.can_reach('Village', 'Region', player) and self.can_reach('Pillager Outpost', 'Region', player) - if self.combat_difficulty(player) == 'easy': - return reach_regions and \ - self.has('Progressive Weapons', player, 3) and self.has('Progressive Armor', player, 2) and \ - self.has('Shield', player) and self.has('Archery', player) and \ - self.has('Progressive Tools', player, 2) and self.has_iron_ingots(player) - elif self.combat_difficulty(player) == 'hard': # might be too hard? - return reach_regions and self.has('Progressive Weapons', player, 2) and self.has_iron_ingots(player) and \ - (self.has('Progressive Armor', player) or self.has('Shield', player)) - return reach_regions and self.has('Progressive Weapons', player, 2) and self.has_iron_ingots(player) and \ - self.has('Progressive Armor', player) and self.has('Shield', player) - - def can_kill_wither(self, player: int): - normal_kill = self.has("Progressive Weapons", player, 3) and self.has("Progressive Armor", player, 2) and self.can_brew_potions(player) and self.can_enchant(player) - if self.combat_difficulty(player) == 'easy': - return self.fortress_loot(player) and normal_kill and self.has('Archery', player) - elif self.combat_difficulty(player) == 'hard': # cheese kill using bedrock ceilings - return self.fortress_loot(player) and (normal_kill or self.can_reach('The Nether', 'Region', player) or self.can_reach('The End', 'Region', player)) - 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 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 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: if location: self.locations_checked.add(location) diff --git a/playerSettings.yaml b/playerSettings.yaml index dd9416c7..56d43f35 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -199,6 +199,12 @@ Minecraft: shuffle_structures: # Enables shuffling of villages, outposts, fortresses, bastions, and end cities. on: 0 off: 1 + structure_compasses: # Adds structure compasses to the item pool, which point to the nearest indicated structure. + on: 0 + off: 1 + bee_traps: # Adds bee traps to the item pool, which spawn multiple angered bees around every player when received. + on: 0 + off: 1 A Link to the Past: ### Logic Section ### glitches_required: # Determine the logic required to complete the seed diff --git a/test/minecraft/TestAdvancements.py b/test/minecraft/TestAdvancements.py index 96c8db01..baacb259 100644 --- a/test/minecraft/TestAdvancements.py +++ b/test/minecraft/TestAdvancements.py @@ -215,11 +215,12 @@ class TestAdvancements(TestMinecraft): ["This Boat Has Legs", False, [], ['Progressive Weapons']], ["This Boat Has Legs", False, [], ['Progressive Armor', 'Shield']], ["This Boat Has Legs", False, [], ['Fishing Rod']], + ["This Boat Has Legs", False, [], ['Saddle']], ["This Boat Has Legs", False, ['Progressive Tools', 'Progressive Tools'], ['Bucket', 'Progressive Tools']], - ["This Boat Has Legs", True, ['Ingot Crafting', 'Progressive Tools', 'Progressive Weapons', 'Progressive Armor', 'Flint and Steel', 'Bucket', 'Fishing Rod']], - ["This Boat Has Legs", True, ['Ingot Crafting', 'Progressive Tools', 'Progressive Weapons', 'Progressive Armor', 'Flint and Steel', 'Progressive Tools', 'Progressive Tools', 'Fishing Rod']], - ["This Boat Has Legs", True, ['Ingot Crafting', 'Progressive Tools', 'Progressive Weapons', 'Shield', 'Flint and Steel', 'Bucket', 'Fishing Rod']], - ["This Boat Has Legs", True, ['Ingot Crafting', 'Progressive Tools', 'Progressive Weapons', 'Shield', 'Flint and Steel', 'Progressive Tools', 'Progressive Tools', 'Fishing Rod']], + ["This Boat Has Legs", True, ['Saddle', 'Ingot Crafting', 'Progressive Tools', 'Progressive Weapons', 'Progressive Armor', 'Flint and Steel', 'Bucket', 'Fishing Rod']], + ["This Boat Has Legs", True, ['Saddle', 'Ingot Crafting', 'Progressive Tools', 'Progressive Weapons', 'Progressive Armor', 'Flint and Steel', 'Progressive Tools', 'Progressive Tools', 'Fishing Rod']], + ["This Boat Has Legs", True, ['Saddle', 'Ingot Crafting', 'Progressive Tools', 'Progressive Weapons', 'Shield', 'Flint and Steel', 'Bucket', 'Fishing Rod']], + ["This Boat Has Legs", True, ['Saddle', 'Ingot Crafting', 'Progressive Tools', 'Progressive Weapons', 'Shield', 'Flint and Steel', 'Progressive Tools', 'Progressive Tools', 'Fishing Rod']], ]) def test_42016(self): @@ -1099,13 +1100,14 @@ class TestAdvancements(TestMinecraft): ["When Pigs Fly", False, [], ['Progressive Weapons']], ["When Pigs Fly", False, [], ['Progressive Armor', 'Shield']], ["When Pigs Fly", False, [], ['Fishing Rod']], + ["When Pigs Fly", False, [], ['Saddle']], ["When Pigs Fly", False, ['Progressive Weapons'], ['Flint and Steel', 'Progressive Weapons', 'Progressive Weapons']], ["When Pigs Fly", False, ['Progressive Tools', 'Progressive Tools', 'Progressive Weapons'], ['Bucket', 'Progressive Tools', 'Progressive Weapons', 'Progressive Weapons']], - ["When Pigs Fly", True, ['Ingot Crafting', 'Progressive Tools', 'Flint and Steel', 'Bucket', 'Progressive Weapons', 'Progressive Armor', 'Fishing Rod']], - ["When Pigs Fly", True, ['Ingot Crafting', 'Progressive Tools', 'Flint and Steel', 'Progressive Tools', 'Progressive Tools', 'Progressive Weapons', 'Progressive Armor', 'Fishing Rod']], - ["When Pigs Fly", True, ['Ingot Crafting', 'Progressive Tools', 'Flint and Steel', 'Bucket', 'Progressive Weapons', 'Shield', 'Fishing Rod']], - ["When Pigs Fly", True, ['Ingot Crafting', 'Progressive Tools', 'Flint and Steel', 'Progressive Tools', 'Progressive Tools', 'Progressive Weapons', 'Shield', 'Fishing Rod']], - ["When Pigs Fly", True, ['Progressive Weapons', 'Progressive Weapons', 'Progressive Armor', 'Shield', 'Ingot Crafting', 'Progressive Tools', 'Fishing Rod']], + ["When Pigs Fly", True, ['Saddle', 'Ingot Crafting', 'Progressive Tools', 'Flint and Steel', 'Bucket', 'Progressive Weapons', 'Progressive Armor', 'Fishing Rod']], + ["When Pigs Fly", True, ['Saddle', 'Ingot Crafting', 'Progressive Tools', 'Flint and Steel', 'Progressive Tools', 'Progressive Tools', 'Progressive Weapons', 'Progressive Armor', 'Fishing Rod']], + ["When Pigs Fly", True, ['Saddle', 'Ingot Crafting', 'Progressive Tools', 'Flint and Steel', 'Bucket', 'Progressive Weapons', 'Shield', 'Fishing Rod']], + ["When Pigs Fly", True, ['Saddle', 'Ingot Crafting', 'Progressive Tools', 'Flint and Steel', 'Progressive Tools', 'Progressive Tools', 'Progressive Weapons', 'Shield', 'Fishing Rod']], + ["When Pigs Fly", True, ['Saddle', 'Progressive Weapons', 'Progressive Weapons', 'Progressive Armor', 'Shield', 'Ingot Crafting', 'Progressive Tools', 'Fishing Rod']], ]) def test_42089(self): diff --git a/test/minecraft/TestMinecraft.py b/test/minecraft/TestMinecraft.py index 06ed6fae..f5220a64 100644 --- a/test/minecraft/TestMinecraft.py +++ b/test/minecraft/TestMinecraft.py @@ -37,6 +37,7 @@ class TestMinecraft(TestBase): setattr(self.world, "shuffle_structures", {1: Toggle(False)}) setattr(self.world, "combat_difficulty", {1: CombatDifficulty(1)}) # normal setattr(self.world, "bee_traps", {1: Toggle(False)}) + setattr(self.world, "structure_compasses", {1: Toggle(False)}) AutoWorld.call_single(self.world, "create_regions", 1) AutoWorld.call_single(self.world, "generate_basic", 1) AutoWorld.call_single(self.world, "set_rules", 1) diff --git a/worlds/minecraft/Items.py b/worlds/minecraft/Items.py index 881b8050..22cb0cc1 100644 --- a/worlds/minecraft/Items.py +++ b/worlds/minecraft/Items.py @@ -47,6 +47,14 @@ item_table = { "8 Gold Ore": ItemData(45032, False), "Rotten Flesh": ItemData(45033, False), "Single Arrow": ItemData(45034, False), + "32 Arrows": ItemData(45035, False), + "Saddle": ItemData(45036, True), + "Structure Compass (Village)": ItemData(45037, True), + "Structure Compass (Pillager Outpost)": ItemData(45038, True), + "Structure Compass (Nether Fortress)": ItemData(45039, True), + "Structure Compass (Bastion Remnant)": ItemData(45040, True), + "Structure Compass (End City)": ItemData(45041, True), + "Shulker Box": ItemData(45042, False), "Bee Trap (Minecraft)": ItemData(45100, False), "Victory": ItemData(None, True) @@ -62,16 +70,23 @@ item_frequencies = { "4 Emeralds": 8, "4 Diamond Ore": 4, "16 Iron Ore": 4, - "500 XP": 4, # 2 after exclusions - "100 XP": 10, # 4 after exclusions - "50 XP": 12, # 4 after exclusions + "500 XP": 0, + "100 XP": 0, + "50 XP": 21, "3 Ender Pearls": 4, "4 Lapis Lazuli": 2, "16 Porkchops": 8, "8 Gold Ore": 4, "Rotten Flesh": 4, "Single Arrow": 0, - "Bee Trap (Minecraft)": 0 + "32 Arrows": 4, + "Structure Compass (Village)": 0, + "Structure Compass (Pillager Outpost)": 0, + "Structure Compass (Nether Fortress)": 0, + "Structure Compass (Bastion Remnant)": 0, + "Structure Compass (End City)": 0, + "Shulker Box": 0, + "Bee Trap (Minecraft)": 0, } lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code} diff --git a/worlds/minecraft/Options.py b/worlds/minecraft/Options.py index efa4625f..9e10ac9e 100644 --- a/worlds/minecraft/Options.py +++ b/worlds/minecraft/Options.py @@ -21,6 +21,7 @@ minecraft_options: typing.Dict[str, type(Option)] = { "include_hard_advancements": Toggle, "include_insane_advancements": Toggle, "include_postgame_advancements": Toggle, - "shuffle_structures": Toggle, + "shuffle_structures": Toggle, + "structure_compasses": Toggle, "bee_traps": Toggle } \ No newline at end of file diff --git a/worlds/minecraft/Rules.py b/worlds/minecraft/Rules.py index 21ea3f05..965353e0 100644 --- a/worlds/minecraft/Rules.py +++ b/worlds/minecraft/Rules.py @@ -1,6 +1,99 @@ from ..generic.Rules import set_rule from .Locations import exclusion_table, events_table from BaseClasses import MultiWorld +from ..AutoWorld import LogicMixin + + +class MinecraftLogic(LogicMixin): + + def _mc_has_iron_ingots(self, player: int): + return self.has('Progressive Tools', player) and self.has('Ingot Crafting', player) + + def _mc_has_gold_ingots(self, player: int): + return self.has('Ingot Crafting', player) and (self.has('Progressive Tools', player, 2) or self.can_reach('The Nether', 'Region', player)) + + def _mc_has_diamond_pickaxe(self, player: int): + return self.has('Progressive Tools', player, 3) and self._mc_has_iron_ingots(player) + + def _mc_craft_crossbow(self, player: int): + return self.has('Archery', player) and self._mc_has_iron_ingots(player) + + def _mc_has_bottle(self, player: int): + return self.has('Bottles', player) and self.has('Ingot Crafting', player) + + def _mc_can_enchant(self, player: int): + return self.has('Enchanting', player) and self._mc_has_diamond_pickaxe(player) # mine obsidian and lapis + + def _mc_can_use_anvil(self, player: int): + return self.has('Enchanting', player) and self.has('Resource Blocks', player) and self._mc_has_iron_ingots(player) + + def _mc_fortress_loot(self, player: int): # saddles, blaze rods, wither skulls + return self.can_reach('Nether Fortress', 'Region', player) and self._mc_basic_combat(player) + + def _mc_can_brew_potions(self, player: int): + return self._mc_fortress_loot(player) and self.has('Brewing', player) and self._mc_has_bottle(player) + + def _mc_can_piglin_trade(self, player: int): + return self._mc_has_gold_ingots(player) and (self.can_reach('The Nether', 'Region', player) or self.can_reach('Bastion Remnant', 'Region', player)) + + def _mc_enter_stronghold(self, player: int): + return self._mc_fortress_loot(player) and self.has('Brewing', player) and self.has('3 Ender Pearls', player) + + # Difficulty-dependent functions + def _mc_combat_difficulty(self, player: int): + return self.world.combat_difficulty[player].get_option_name() + + def _mc_can_adventure(self, player: int): + if self._mc_combat_difficulty(player) == 'easy': + return self.has('Progressive Weapons', player, 2) and self._mc_has_iron_ingots(player) + elif self._mc_combat_difficulty(player) == 'hard': + return True + return self.has('Progressive Weapons', player) and (self.has('Ingot Crafting', player) or self.has('Campfire', player)) + + def _mc_basic_combat(self, player: int): + if self._mc_combat_difficulty(player) == 'easy': + return self.has('Progressive Weapons', player, 2) and self.has('Progressive Armor', player) and \ + self.has('Shield', player) and self._mc_has_iron_ingots(player) + elif self._mc_combat_difficulty(player) == 'hard': + return True + return self.has('Progressive Weapons', player) and (self.has('Progressive Armor', player) or self.has('Shield', player)) and self._mc_has_iron_ingots(player) + + def _mc_complete_raid(self, player: int): + reach_regions = self.can_reach('Village', 'Region', player) and self.can_reach('Pillager Outpost', 'Region', player) + if self._mc_combat_difficulty(player) == 'easy': + return reach_regions and \ + self.has('Progressive Weapons', player, 3) and self.has('Progressive Armor', player, 2) and \ + self.has('Shield', player) and self.has('Archery', player) and \ + self.has('Progressive Tools', player, 2) and self._mc_has_iron_ingots(player) + elif self._mc_combat_difficulty(player) == 'hard': # might be too hard? + return reach_regions and self.has('Progressive Weapons', player, 2) and self._mc_has_iron_ingots(player) and \ + (self.has('Progressive Armor', player) or self.has('Shield', player)) + return reach_regions and self.has('Progressive Weapons', player, 2) and self._mc_has_iron_ingots(player) and \ + self.has('Progressive Armor', player) and self.has('Shield', player) + + def _mc_can_kill_wither(self, player: int): + normal_kill = self.has("Progressive Weapons", player, 3) and self.has("Progressive Armor", player, 2) and self._mc_can_brew_potions(player) and self._mc_can_enchant(player) + if self._mc_combat_difficulty(player) == 'easy': + return self._mc_fortress_loot(player) and normal_kill and self.has('Archery', player) + elif self._mc_combat_difficulty(player) == 'hard': # cheese kill using bedrock ceilings + return self._mc_fortress_loot(player) and (normal_kill or self.can_reach('The Nether', 'Region', player) or self.can_reach('The End', 'Region', player)) + return self._mc_fortress_loot(player) and normal_kill + + def _mc_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._mc_combat_difficulty(player) == 'easy': + return respawn_dragon and self.has("Progressive Weapons", player, 3) and self.has("Progressive Armor", player, 2) and \ + self.has('Archery', player) and self._mc_can_brew_potions(player) and self._mc_can_enchant(player) + if self._mc_combat_difficulty(player) == 'hard': + 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 _mc_has_structure_compass(self, entrance_name: str, player: int): + if not self.world.structure_compasses[player]: + return True + return self.has(f"Structure Compass ({self.world.get_entrance(entrance_name, player).connected_region.name})", player) def set_rules(world: MultiWorld, player: int): @@ -14,118 +107,124 @@ def set_rules(world: MultiWorld, player: int): location.name not in postgame_advancements and location.can_reach(state)] + # Retrieves the appropriate structure compass for the given entrance + def get_struct_compass(entrance_name): + struct = world.get_entrance(entrance_name, player).connected_region.name + return f"Structure Compass ({struct})" + # 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) + can_complete = lambda state: len(reachable_locations(state)) >= goal and state.can_reach('The End', 'Region', player) and state._mc_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("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)) - set_rule(world.get_entrance("Nether Structure 2", player), lambda state: state.can_adventure(player)) - set_rule(world.get_entrance("The End Structure", player), lambda state: state.can_adventure(player)) + state._mc_has_iron_ingots(player)) + set_rule(world.get_entrance("End Portal", player), lambda state: state._mc_enter_stronghold(player) and state.has('3 Ender Pearls', player, 4)) + set_rule(world.get_entrance("Overworld Structure 1", player), lambda state: state._mc_can_adventure(player) and state._mc_has_structure_compass("Overworld Structure 1", player)) + set_rule(world.get_entrance("Overworld Structure 2", player), lambda state: state._mc_can_adventure(player) and state._mc_has_structure_compass("Overworld Structure 2", player)) + set_rule(world.get_entrance("Nether Structure 1", player), lambda state: state._mc_can_adventure(player) and state._mc_has_structure_compass("Nether Structure 1", player)) + set_rule(world.get_entrance("Nether Structure 2", player), lambda state: state._mc_can_adventure(player) and state._mc_has_structure_compass("Nether Structure 2", player)) + set_rule(world.get_entrance("The End Structure", player), lambda state: state._mc_can_adventure(player) and state._mc_has_structure_compass("The End Structure", player)) set_rule(world.get_location("Ender Dragon", player), lambda state: can_complete(state)) - set_rule(world.get_location("Who is Cutting Onions?", player), lambda state: state.can_piglin_trade(player)) - set_rule(world.get_location("Oh Shiny", player), lambda state: state.can_piglin_trade(player)) - set_rule(world.get_location("Suit Up", player), lambda state: state.has("Progressive Armor", player) and state.has_iron_ingots(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 \ + set_rule(world.get_location("Who is Cutting Onions?", player), lambda state: state._mc_can_piglin_trade(player)) + set_rule(world.get_location("Oh Shiny", player), lambda state: state._mc_can_piglin_trade(player)) + set_rule(world.get_location("Suit Up", player), lambda state: state.has("Progressive Armor", player) and state._mc_has_iron_ingots(player)) + set_rule(world.get_location("Very Very Frightening", player), lambda state: state.has("Channeling Book", player) and state._mc_can_use_anvil(player) and state._mc_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("Hot Stuff", player), lambda state: state.has("Bucket", player) and state._mc_has_iron_ingots(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 + set_rule(world.get_location("A Furious Cocktail", player), lambda state: state._mc_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 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("Local Brewery", player), lambda state: state.can_brew_potions(player)) + set_rule(world.get_location("Bring Home the Beacon", player), lambda state: state._mc_can_kill_wither(player) and + state._mc_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._mc_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._mc_has_iron_ingots(player)) + set_rule(world.get_location("Local Brewery", player), lambda state: state._mc_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._mc_fortress_loot(player) or state._mc_complete_raid(player)) and + state.has("Saddle", 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 # Absorption + set_rule(world.get_location("Great View From Up Here", player), lambda state: state._mc_basic_combat(player)) + set_rule(world.get_location("How Did We Get Here?", player), lambda state: state._mc_can_brew_potions(player) and + state._mc_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("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("Bullseye", player), lambda state: state.has("Archery", player) and state.has("Progressive Tools", player, 2) and state._mc_has_iron_ingots(player)) + set_rule(world.get_location("Spooky Scary Skeleton", player), lambda state: state._mc_basic_combat(player)) + set_rule(world.get_location("Two by Two", player), lambda state: state._mc_has_iron_ingots(player) and state._mc_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._mc_craft_crossbow(player) and state._mc_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("Who's the Pillager Now?", player), lambda state: state._mc_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._mc_has_iron_ingots(player)) + set_rule(world.get_location("Zombie Doctor", player), lambda state: state._mc_can_brew_potions(player) and state._mc_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("Ice Bucket Challenge", player), lambda state: state._mc_has_diamond_pickaxe(player)) set_rule(world.get_location("Remote Getaway", player), lambda state: True) - set_rule(world.get_location("Into Fire", player), lambda state: state.basic_combat(player)) - set_rule(world.get_location("War Pigs", player), lambda state: state.basic_combat(player)) + set_rule(world.get_location("Into Fire", player), lambda state: state._mc_basic_combat(player)) + set_rule(world.get_location("War Pigs", player), lambda state: state._mc_basic_combat(player)) set_rule(world.get_location("Take Aim", player), lambda state: state.has("Archery", 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("Total Beelocation", player), lambda state: state.has("Silk Touch Book", player) and state._mc_can_use_anvil(player) and state._mc_can_enchant(player)) + set_rule(world.get_location("Arbalistic", player), lambda state: state._mc_craft_crossbow(player) and state.has("Piercing IV Book", player) and + state._mc_can_use_anvil(player) and state._mc_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("Acquire Hardware", player), lambda state: state._mc_has_iron_ingots(player)) + set_rule(world.get_location("Not Quite \"Nine\" Lives", player), lambda state: state._mc_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("Sky's the Limit", player), lambda state: state._mc_basic_combat(player)) + set_rule(world.get_location("Hired Help", player), lambda state: state.has("Resource Blocks", player) and state._mc_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("You Need a Mint", player), lambda state: can_complete(state) and state._mc_has_bottle(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("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("Monsters Hunted", player), lambda state: can_complete(state) and state._mc_can_kill_wither(player) and state.has("Fishing Rod", player)) # pufferfish for Water Breathing + set_rule(world.get_location("Enchanter", player), lambda state: state._mc_can_enchant(player)) + set_rule(world.get_location("Voluntary Exile", player), lambda state: state._mc_basic_combat(player)) + set_rule(world.get_location("Eye Spy", player), lambda state: state._mc_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("Postmortal", player), lambda state: state.complete_raid(player)) + set_rule(world.get_location("Serious Dedication", player), lambda state: state.can_reach("Hidden in the Depths", "Location", player) and state._mc_has_gold_ingots(player)) + set_rule(world.get_location("Postmortal", player), lambda state: state._mc_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("Adventuring Time", player), lambda state: state._mc_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("Hero of the Village", player), lambda state: state._mc_complete_raid(player)) + set_rule(world.get_location("Hidden in the Depths", player), lambda state: state._mc_can_brew_potions(player) and state.has("Bed", player) and state._mc_has_diamond_pickaxe(player)) # bed mining :) + set_rule(world.get_location("Beaconator", player), lambda state: state._mc_can_kill_wither(player) and state._mc_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 + set_rule(world.get_location("Withering Heights", player), lambda state: state._mc_can_kill_wither(player)) + set_rule(world.get_location("A Balanced Diet", player), lambda state: state._mc_has_bottle(player) and state._mc_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("Subspace Bubble", player), lambda state: state._mc_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._mc_has_gold_ingots(player)) + set_rule(world.get_location("Bee Our Guest", player), lambda state: state.has("Campfire", player) and state._mc_has_bottle(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("Uneasy Alliance", player), lambda state: state._mc_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._mc_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("Ol' Betsy", player), lambda state: state.craft_crossbow(player)) + set_rule(world.get_location("Sticky Situation", player), lambda state: state.has("Campfire", player) and state._mc_has_bottle(player)) + set_rule(world.get_location("Ol' Betsy", player), lambda state: state._mc_craft_crossbow(player)) 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)) @@ -136,13 +235,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._mc_has_iron_ingots(player) and state.has("Bucket", player)) + set_rule(world.get_location("On a Rail", player), lambda state: state._mc_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 + set_rule(world.get_location("When Pigs Fly", player), lambda state: (state._mc_fortress_loot(player) or state._mc_complete_raid(player)) and + state.has("Saddle", player) and state.has("Fishing Rod", player) and state._mc_can_adventure(player)) + set_rule(world.get_location("Overkill", player), lambda state: state._mc_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._mc_has_gold_ingots(player)) diff --git a/worlds/minecraft/__init__.py b/worlds/minecraft/__init__.py index 30804b77..7744a0a4 100644 --- a/worlds/minecraft/__init__.py +++ b/worlds/minecraft/__init__.py @@ -3,7 +3,7 @@ import os from .Items import MinecraftItem, item_table, item_frequencies from .Locations import MinecraftAdvancement, advancement_table, exclusion_table, events_table -from .Regions import mc_regions, link_minecraft_structures +from .Regions import mc_regions, link_minecraft_structures, default_connections from .Rules import set_rules from worlds.generic.Rules import exclusion_rules @@ -11,7 +11,7 @@ from BaseClasses import Region, Entrance, Item from .Options import minecraft_options from ..AutoWorld import World -client_version = (0, 4) +client_version = 5 class MinecraftWorld(World): game: str = "Minecraft" @@ -23,9 +23,10 @@ class MinecraftWorld(World): item_name_to_id = {name: data.code for name, data in item_table.items()} location_name_to_id = {name: data.id for name, data in advancement_table.items()} + data_version = 2 + def _get_mc_data(self): - exits = ["Overworld Structure 1", "Overworld Structure 2", "Nether Structure 1", "Nether Structure 2", - "The End Structure"] + exits = [connection[0] for connection in default_connections] return { 'world_seed': self.world.slot_seeds[self.player].getrandbits(32), # consistent and doesn't interfere with other generation @@ -42,8 +43,15 @@ class MinecraftWorld(World): # Generate item pool itempool = [] pool_counts = item_frequencies.copy() - if getattr(self.world, "bee_traps")[self.player]: # replace Rotten Flesh by bee traps + # Replace Rotten Flesh with bee traps + if self.world.bee_traps[self.player]: pool_counts.update({"Rotten Flesh": 0, "Bee Trap (Minecraft)": 4}) + # Add structure compasses to the pool, replacing 50 XP + if self.world.structure_compasses[self.player]: + structures = [connection[1] for connection in default_connections] + for struct_name in structures: + pool_counts[f"Structure Compass ({struct_name})"] = 1 + pool_counts["50 XP"] -= 1 for item_name in item_table: for count in range(pool_counts.get(item_name, 1)): itempool.append(self.create_item(item_name)) @@ -97,6 +105,7 @@ class MinecraftWorld(World): def create_item(self, name: str) -> Item: item_data = item_table[name] item = MinecraftItem(name, item_data.progression, item_data.code, self.player) - if "Book" in name: # prevent enchanted books from being excluded + nonexcluded_items = ["Sharpness III Book", "Infinity Book", "Looting III Book"] + if name in nonexcluded_items: # prevent books from going on excluded locations item.never_exclude = True return item