Merge remote-tracking branch 'Espeon/minecraft' into Archipelago_Main

This commit is contained in:
Fabian Dill 2021-07-02 20:14:34 +02:00
commit 8030db03ad
12 changed files with 203 additions and 294 deletions

View File

@ -894,13 +894,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:

12
Main.py
View File

@ -21,8 +21,6 @@ from Fill import distribute_items_restrictive, flood_items, balance_multiworld_p
from worlds.alttp.Shops import create_shops, ShopSlotFill, SHOP_ID_START, total_shop_slots, FillDisabledShopSlots
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.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
@ -196,9 +194,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',
@ -260,9 +255,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:
@ -511,7 +503,7 @@ def main(args, seed=None):
for slot in world.hk_player_ids:
slot_data[slot] = AutoWorld.call_single(world, "fill_slot_data", slot)
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():
@ -563,8 +555,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)

View File

@ -175,6 +175,9 @@ class Range(Option, int):
return cls(data)
return cls.from_text(str(data))
def get_option_name(self):
return str(self.value)
def __str__(self):
return str(self.value)

View File

@ -102,10 +102,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
@ -119,7 +116,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:

View File

@ -1,11 +1,10 @@
import worlds.minecraft.Options
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
from worlds.minecraft.Options import AdvancementGoal, CombatDifficulty
# Converts the name of an item into an item object
def MCItemFactory(items, player: int):
@ -29,16 +28,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, worlds.minecraft.Options.AdvancementGoal(value=0)])
setattr(self.world, "shuffle_structures", [False, False])
setattr(self.world, "combat_difficulty", [0, worlds.minecraft.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: AdvancementGoal(30)})
setattr(self.world, "shuffle_structures", {1: False})
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)
def _get_items(self, item_pool, all_except):
if all_except and len(all_except) > 0:

View File

@ -32,7 +32,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

View File

@ -1,4 +1,4 @@
from BaseClasses import Region, Entrance, Location, MultiWorld, Item
from BaseClasses import Item
import typing
class ItemData(typing.NamedTuple):
@ -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}

View File

@ -1,4 +1,4 @@
from BaseClasses import Region, Entrance, Location, MultiWorld, Item
from BaseClasses import Location
import typing
class AdvData(typing.NamedTuple):
@ -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",

View File

@ -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):

View File

@ -1,73 +1,44 @@
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):
# 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]
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:
@ -77,13 +48,15 @@ def link_minecraft_structures(world: MultiWorld, player: int):
# 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 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, pairs[exit], 'entrance', player)
for exit, struct in pairs.items():
world.get_entrance(exit, player).connect(world.get_region(struct, player))
if world.shuffle_structures[player]:
world.spoiler.set_entrance(exit, struct, 'entrance', player)
# (Region name, list of exits)
mc_regions = [
@ -112,3 +85,9 @@ default_connections = {
('Nether Structure 2', 'Bastion Remnant'),
('The End Structure', 'End City')
}
# Structure: illegal locations
illegal_connections = {
'Nether Fortress': ['The End Structure']
}

View File

@ -14,26 +14,19 @@ 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()]
can_complete = lambda state: len(reachable_locations(state)) >= goal and state.can_reach('The End', 'Region',
player) and state.can_kill_ender_dragon(
player)
# 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':
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))
@ -44,158 +37,98 @@ def set_rules(world: MultiWorld, player: int):
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 \
((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("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
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("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 \
((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))
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
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)
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("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("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("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))
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
@ -203,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))

View File

@ -1,81 +1,92 @@
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, 4)
class MinecraftWorld(World):
game: str = "Minecraft"
options = minecraft_options
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 json
from base64 import b64encode
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(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