Minecraft rewrite (#1493)
* Minecraft: rewrite to modern AP standards * Fix gitignore to not try to ignore the entire minecraft world * minecraft: clean up MC-specific tests * minecraft: use pkgutil instead of open * minecraft: ship as apworld * mc: update region to new api * Increase upper limit on advancement and egg shard goals * mc: reduce egg shard count by 5 for structure compasses * Minecraft: add more tests Ensures data loading works; tests beatability with various options at their max setting; new tests for 1.19 advancements * test improvements * mc: typing and imports cleanup * parens * mc: condense filler item code and override get_filler_item_name
This commit is contained in:
parent
a95e51deda
commit
5e1aa52373
|
@ -169,6 +169,7 @@ cython_debug/
|
|||
jdk*/
|
||||
minecraft*/
|
||||
minecraft_versions.json
|
||||
!worlds/minecraft/
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
|
1
setup.py
1
setup.py
|
@ -53,6 +53,7 @@ apworlds: set = {
|
|||
"Super Mario World",
|
||||
"Stardew Valley",
|
||||
"Timespinner",
|
||||
"Minecraft",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import os
|
||||
import json
|
||||
import pkgutil
|
||||
|
||||
def load_data_file(*args) -> dict:
|
||||
fname = os.path.join("data", *args)
|
||||
return json.loads(pkgutil.get_data(__name__, fname).decode())
|
||||
|
||||
# For historical reasons, these values are different.
|
||||
# They remain different to ensure datapackage consistency.
|
||||
# Do not separate other games' location and item IDs like this.
|
||||
item_id_offset: int = 45000
|
||||
location_id_offset: int = 42000
|
||||
|
||||
item_info = load_data_file("items.json")
|
||||
item_name_to_id = {name: item_id_offset + index \
|
||||
for index, name in enumerate(item_info["all_items"])}
|
||||
item_name_to_id["Bee Trap"] = item_id_offset + 100 # historical reasons
|
||||
|
||||
location_info = load_data_file("locations.json")
|
||||
location_name_to_id = {name: location_id_offset + index \
|
||||
for index, name in enumerate(location_info["all_locations"])}
|
||||
|
||||
exclusion_info = load_data_file("excluded_locations.json")
|
||||
|
||||
region_info = load_data_file("regions.json")
|
|
@ -0,0 +1,52 @@
|
|||
from math import ceil
|
||||
from typing import List
|
||||
|
||||
from BaseClasses import MultiWorld, Item
|
||||
from worlds.AutoWorld import World
|
||||
|
||||
from . import Constants
|
||||
|
||||
def get_junk_item_names(rand, k: int) -> str:
|
||||
junk_weights = Constants.item_info["junk_weights"]
|
||||
junk = rand.choices(
|
||||
list(junk_weights.keys()),
|
||||
weights=list(junk_weights.values()),
|
||||
k=k)
|
||||
return junk
|
||||
|
||||
def build_item_pool(mc_world: World) -> List[Item]:
|
||||
multiworld = mc_world.multiworld
|
||||
player = mc_world.player
|
||||
|
||||
itempool = []
|
||||
total_location_count = len(multiworld.get_unfilled_locations(player))
|
||||
|
||||
required_pool = Constants.item_info["required_pool"]
|
||||
junk_weights = Constants.item_info["junk_weights"]
|
||||
|
||||
# Add required progression items
|
||||
for item_name, num in required_pool.items():
|
||||
itempool += [mc_world.create_item(item_name) for _ in range(num)]
|
||||
|
||||
# Add structure compasses
|
||||
if multiworld.structure_compasses[player]:
|
||||
compasses = [name for name in mc_world.item_name_to_id if "Structure Compass" in name]
|
||||
for item_name in compasses:
|
||||
itempool.append(mc_world.create_item(item_name))
|
||||
|
||||
# Dragon egg shards
|
||||
if multiworld.egg_shards_required[player] > 0:
|
||||
num = multiworld.egg_shards_available[player]
|
||||
itempool += [mc_world.create_item("Dragon Egg Shard") for _ in range(num)]
|
||||
|
||||
# Bee traps
|
||||
bee_trap_percentage = multiworld.bee_traps[player] * 0.01
|
||||
if bee_trap_percentage > 0:
|
||||
bee_trap_qty = ceil(bee_trap_percentage * (total_location_count - len(itempool)))
|
||||
itempool += [mc_world.create_item("Bee Trap") for _ in range(bee_trap_qty)]
|
||||
|
||||
# Fill remaining itempool with randomly generated junk
|
||||
junk = get_junk_item_names(multiworld.random, total_location_count - len(itempool))
|
||||
itempool += [mc_world.create_item(name) for name in junk]
|
||||
|
||||
return itempool
|
|
@ -1,108 +0,0 @@
|
|||
from BaseClasses import Item
|
||||
import typing
|
||||
|
||||
|
||||
class ItemData(typing.NamedTuple):
|
||||
code: typing.Optional[int]
|
||||
progression: bool
|
||||
|
||||
|
||||
class MinecraftItem(Item):
|
||||
game: str = "Minecraft"
|
||||
|
||||
|
||||
item_table = {
|
||||
"Archery": ItemData(45000, True),
|
||||
"Progressive Resource Crafting": ItemData(45001, True),
|
||||
# "Resource Blocks": ItemData(45002, True),
|
||||
"Brewing": ItemData(45003, True),
|
||||
"Enchanting": ItemData(45004, True),
|
||||
"Bucket": ItemData(45005, True),
|
||||
"Flint and Steel": ItemData(45006, True),
|
||||
"Bed": ItemData(45007, True),
|
||||
"Bottles": ItemData(45008, True),
|
||||
"Shield": ItemData(45009, True),
|
||||
"Fishing Rod": ItemData(45010, True),
|
||||
"Campfire": ItemData(45011, True),
|
||||
"Progressive Weapons": ItemData(45012, True),
|
||||
"Progressive Tools": ItemData(45013, True),
|
||||
"Progressive Armor": ItemData(45014, True),
|
||||
"8 Netherite Scrap": ItemData(45015, True),
|
||||
"8 Emeralds": ItemData(45016, False),
|
||||
"4 Emeralds": ItemData(45017, False),
|
||||
"Channeling Book": ItemData(45018, True),
|
||||
"Silk Touch Book": ItemData(45019, True),
|
||||
"Sharpness III Book": ItemData(45020, False),
|
||||
"Piercing IV Book": ItemData(45021, True),
|
||||
"Looting III Book": ItemData(45022, False),
|
||||
"Infinity Book": ItemData(45023, False),
|
||||
"4 Diamond Ore": ItemData(45024, False),
|
||||
"16 Iron Ore": ItemData(45025, False),
|
||||
"500 XP": ItemData(45026, False),
|
||||
"100 XP": ItemData(45027, False),
|
||||
"50 XP": ItemData(45028, False),
|
||||
"3 Ender Pearls": ItemData(45029, True),
|
||||
"4 Lapis Lazuli": ItemData(45030, False),
|
||||
"16 Porkchops": ItemData(45031, False),
|
||||
"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),
|
||||
"Dragon Egg Shard": ItemData(45043, True),
|
||||
"Spyglass": ItemData(45044, True),
|
||||
"Lead": ItemData(45045, True),
|
||||
|
||||
"Bee Trap": ItemData(45100, False),
|
||||
"Blaze Rods": ItemData(None, True),
|
||||
"Defeat Ender Dragon": ItemData(None, True),
|
||||
"Defeat Wither": ItemData(None, True),
|
||||
}
|
||||
|
||||
# 33 required items
|
||||
required_items = {
|
||||
"Archery": 1,
|
||||
"Progressive Resource Crafting": 2,
|
||||
"Brewing": 1,
|
||||
"Enchanting": 1,
|
||||
"Bucket": 1,
|
||||
"Flint and Steel": 1,
|
||||
"Bed": 1,
|
||||
"Bottles": 1,
|
||||
"Shield": 1,
|
||||
"Fishing Rod": 1,
|
||||
"Campfire": 1,
|
||||
"Progressive Weapons": 3,
|
||||
"Progressive Tools": 3,
|
||||
"Progressive Armor": 2,
|
||||
"8 Netherite Scrap": 2,
|
||||
"Channeling Book": 1,
|
||||
"Silk Touch Book": 1,
|
||||
"Sharpness III Book": 1,
|
||||
"Piercing IV Book": 1,
|
||||
"Looting III Book": 1,
|
||||
"Infinity Book": 1,
|
||||
"3 Ender Pearls": 4,
|
||||
"Saddle": 1,
|
||||
"Spyglass": 1,
|
||||
"Lead": 1,
|
||||
}
|
||||
|
||||
junk_weights = {
|
||||
"4 Emeralds": 2,
|
||||
"4 Diamond Ore": 1,
|
||||
"16 Iron Ore": 1,
|
||||
"50 XP": 4,
|
||||
"16 Porkchops": 2,
|
||||
"8 Gold Ore": 1,
|
||||
"Rotten Flesh": 1,
|
||||
"32 Arrows": 1,
|
||||
}
|
||||
|
||||
lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code}
|
|
@ -1,192 +0,0 @@
|
|||
from BaseClasses import Location
|
||||
import typing
|
||||
|
||||
|
||||
class AdvData(typing.NamedTuple):
|
||||
id: typing.Optional[int]
|
||||
region: str
|
||||
|
||||
|
||||
class MinecraftAdvancement(Location):
|
||||
game: str = "Minecraft"
|
||||
|
||||
def __init__(self, player: int, name: str, address: typing.Optional[int], parent):
|
||||
super().__init__(player, name, address, parent)
|
||||
self.event = not address
|
||||
|
||||
|
||||
advancement_table = {
|
||||
"Who is Cutting Onions?": AdvData(42000, 'Overworld'),
|
||||
"Oh Shiny": AdvData(42001, 'Overworld'),
|
||||
"Suit Up": AdvData(42002, 'Overworld'),
|
||||
"Very Very Frightening": AdvData(42003, 'Overworld'),
|
||||
"Hot Stuff": AdvData(42004, 'Overworld'),
|
||||
"Free the End": AdvData(42005, 'The End'),
|
||||
"A Furious Cocktail": AdvData(42006, 'Nether Fortress'),
|
||||
"Best Friends Forever": AdvData(42007, 'Overworld'),
|
||||
"Bring Home the Beacon": AdvData(42008, 'Nether Fortress'),
|
||||
"Not Today, Thank You": AdvData(42009, 'Overworld'),
|
||||
"Isn't It Iron Pick": AdvData(42010, 'Overworld'),
|
||||
"Local Brewery": AdvData(42011, 'Nether Fortress'),
|
||||
"The Next Generation": AdvData(42012, 'The End'),
|
||||
"Fishy Business": AdvData(42013, 'Overworld'),
|
||||
"Hot Tourist Destinations": AdvData(42014, 'The Nether'),
|
||||
"This Boat Has Legs": AdvData(42015, 'The Nether'),
|
||||
"Sniper Duel": AdvData(42016, 'Overworld'),
|
||||
"Nether": AdvData(42017, 'The Nether'),
|
||||
"Great View From Up Here": AdvData(42018, 'End City'),
|
||||
"How Did We Get Here?": AdvData(42019, 'Nether Fortress'),
|
||||
"Bullseye": AdvData(42020, 'Overworld'),
|
||||
"Spooky Scary Skeleton": AdvData(42021, 'Nether Fortress'),
|
||||
"Two by Two": AdvData(42022, 'The Nether'),
|
||||
"Stone Age": AdvData(42023, 'Overworld'),
|
||||
"Two Birds, One Arrow": AdvData(42024, 'Overworld'),
|
||||
"We Need to Go Deeper": AdvData(42025, 'The Nether'),
|
||||
"Who's the Pillager Now?": AdvData(42026, 'Pillager Outpost'),
|
||||
"Getting an Upgrade": AdvData(42027, 'Overworld'),
|
||||
"Tactical Fishing": AdvData(42028, 'Overworld'),
|
||||
"Zombie Doctor": AdvData(42029, 'Overworld'),
|
||||
"The City at the End of the Game": AdvData(42030, 'End City'),
|
||||
"Ice Bucket Challenge": AdvData(42031, 'Overworld'),
|
||||
"Remote Getaway": AdvData(42032, 'The End'),
|
||||
"Into Fire": AdvData(42033, 'Nether Fortress'),
|
||||
"War Pigs": AdvData(42034, 'Bastion Remnant'),
|
||||
"Take Aim": AdvData(42035, 'Overworld'),
|
||||
"Total Beelocation": AdvData(42036, 'Overworld'),
|
||||
"Arbalistic": AdvData(42037, 'Overworld'),
|
||||
"The End... Again...": AdvData(42038, 'The End'),
|
||||
"Acquire Hardware": AdvData(42039, 'Overworld'),
|
||||
"Not Quite \"Nine\" Lives": AdvData(42040, 'The Nether'),
|
||||
"Cover Me With Diamonds": AdvData(42041, 'Overworld'),
|
||||
"Sky's the Limit": AdvData(42042, 'End City'),
|
||||
"Hired Help": AdvData(42043, 'Overworld'),
|
||||
"Return to Sender": AdvData(42044, 'The Nether'),
|
||||
"Sweet Dreams": AdvData(42045, 'Overworld'),
|
||||
"You Need a Mint": AdvData(42046, 'The End'),
|
||||
"Adventure": AdvData(42047, 'Overworld'),
|
||||
"Monsters Hunted": AdvData(42048, 'Overworld'),
|
||||
"Enchanter": AdvData(42049, 'Overworld'),
|
||||
"Voluntary Exile": AdvData(42050, 'Pillager Outpost'),
|
||||
"Eye Spy": AdvData(42051, 'Overworld'),
|
||||
"The End": AdvData(42052, 'The End'),
|
||||
"Serious Dedication": AdvData(42053, 'The Nether'),
|
||||
"Postmortal": AdvData(42054, 'Village'),
|
||||
"Monster Hunter": AdvData(42055, 'Overworld'),
|
||||
"Adventuring Time": AdvData(42056, 'Overworld'),
|
||||
"A Seedy Place": AdvData(42057, 'Overworld'),
|
||||
"Those Were the Days": AdvData(42058, 'Bastion Remnant'),
|
||||
"Hero of the Village": AdvData(42059, 'Village'),
|
||||
"Hidden in the Depths": AdvData(42060, 'The Nether'),
|
||||
"Beaconator": AdvData(42061, 'Nether Fortress'),
|
||||
"Withering Heights": AdvData(42062, 'Nether Fortress'),
|
||||
"A Balanced Diet": AdvData(42063, 'Village'),
|
||||
"Subspace Bubble": AdvData(42064, 'The Nether'),
|
||||
"Husbandry": AdvData(42065, 'Overworld'),
|
||||
"Country Lode, Take Me Home": AdvData(42066, 'The Nether'),
|
||||
"Bee Our Guest": AdvData(42067, 'Overworld'),
|
||||
"What a Deal!": AdvData(42068, 'Village'),
|
||||
"Uneasy Alliance": AdvData(42069, 'The Nether'),
|
||||
"Diamonds!": AdvData(42070, 'Overworld'),
|
||||
"A Terrible Fortress": AdvData(42071, 'Nether Fortress'),
|
||||
"A Throwaway Joke": AdvData(42072, 'Overworld'),
|
||||
"Minecraft": AdvData(42073, 'Overworld'),
|
||||
"Sticky Situation": AdvData(42074, 'Overworld'),
|
||||
"Ol' Betsy": AdvData(42075, 'Overworld'),
|
||||
"Cover Me in Debris": AdvData(42076, 'The Nether'),
|
||||
"The End?": AdvData(42077, 'The End'),
|
||||
"The Parrots and the Bats": AdvData(42078, 'Overworld'),
|
||||
"A Complete Catalogue": AdvData(42079, 'Village'),
|
||||
"Getting Wood": AdvData(42080, 'Overworld'),
|
||||
"Time to Mine!": AdvData(42081, 'Overworld'),
|
||||
"Hot Topic": AdvData(42082, 'Overworld'),
|
||||
"Bake Bread": AdvData(42083, 'Overworld'),
|
||||
"The Lie": AdvData(42084, 'Overworld'),
|
||||
"On a Rail": AdvData(42085, 'Overworld'),
|
||||
"Time to Strike!": AdvData(42086, 'Overworld'),
|
||||
"Cow Tipper": AdvData(42087, 'Overworld'),
|
||||
"When Pigs Fly": AdvData(42088, 'Overworld'),
|
||||
"Overkill": AdvData(42089, 'Nether Fortress'),
|
||||
"Librarian": AdvData(42090, 'Overworld'),
|
||||
"Overpowered": AdvData(42091, 'Bastion Remnant'),
|
||||
"Wax On": AdvData(42092, 'Overworld'),
|
||||
"Wax Off": AdvData(42093, 'Overworld'),
|
||||
"The Cutest Predator": AdvData(42094, 'Overworld'),
|
||||
"The Healing Power of Friendship": AdvData(42095, 'Overworld'),
|
||||
"Is It a Bird?": AdvData(42096, 'Overworld'),
|
||||
"Is It a Balloon?": AdvData(42097, 'The Nether'),
|
||||
"Is It a Plane?": AdvData(42098, 'The End'),
|
||||
"Surge Protector": AdvData(42099, 'Overworld'),
|
||||
"Light as a Rabbit": AdvData(42100, 'Overworld'),
|
||||
"Glow and Behold!": AdvData(42101, 'Overworld'),
|
||||
"Whatever Floats Your Goat!": AdvData(42102, 'Overworld'),
|
||||
"Caves & Cliffs": AdvData(42103, 'Overworld'),
|
||||
"Feels like home": AdvData(42104, 'The Nether'),
|
||||
"Sound of Music": AdvData(42105, 'Overworld'),
|
||||
"Star Trader": AdvData(42106, 'Village'),
|
||||
|
||||
# 1.19 advancements
|
||||
"Birthday Song": AdvData(42107, 'Pillager Outpost'),
|
||||
"Bukkit Bukkit": AdvData(42108, 'Overworld'),
|
||||
"It Spreads": AdvData(42109, 'Overworld'),
|
||||
"Sneak 100": AdvData(42110, 'Overworld'),
|
||||
"When the Squad Hops into Town": AdvData(42111, 'Overworld'),
|
||||
"With Our Powers Combined!": AdvData(42112, 'The Nether'),
|
||||
"You've Got a Friend in Me": AdvData(42113, 'Pillager Outpost'),
|
||||
|
||||
"Blaze Spawner": AdvData(None, 'Nether Fortress'),
|
||||
"Ender Dragon": AdvData(None, 'The End'),
|
||||
"Wither": AdvData(None, 'Nether Fortress'),
|
||||
}
|
||||
|
||||
exclusion_table = {
|
||||
"hard": {
|
||||
"Very Very Frightening",
|
||||
"A Furious Cocktail",
|
||||
"Two by Two",
|
||||
"Two Birds, One Arrow",
|
||||
"Arbalistic",
|
||||
"Monsters Hunted",
|
||||
"Beaconator",
|
||||
"A Balanced Diet",
|
||||
"Uneasy Alliance",
|
||||
"Cover Me in Debris",
|
||||
"A Complete Catalogue",
|
||||
"Surge Protector",
|
||||
"Sound of Music",
|
||||
"Star Trader",
|
||||
"When the Squad Hops into Town",
|
||||
"With Our Powers Combined!",
|
||||
},
|
||||
"unreasonable": {
|
||||
"How Did We Get Here?",
|
||||
"Adventuring Time",
|
||||
},
|
||||
}
|
||||
|
||||
def get_postgame_advancements(required_bosses):
|
||||
|
||||
postgame_advancements = {
|
||||
"ender_dragon": {
|
||||
"Free the End",
|
||||
"The Next Generation",
|
||||
"The End... Again...",
|
||||
"You Need a Mint",
|
||||
"Monsters Hunted",
|
||||
"Is It a Plane?",
|
||||
},
|
||||
"wither": {
|
||||
"Withering Heights",
|
||||
"Bring Home the Beacon",
|
||||
"Beaconator",
|
||||
"A Furious Cocktail",
|
||||
"How Did We Get Here?",
|
||||
"Monsters Hunted",
|
||||
}
|
||||
}
|
||||
|
||||
advancements = set()
|
||||
if required_bosses in {"ender_dragon", "both"}:
|
||||
advancements.update(postgame_advancements["ender_dragon"])
|
||||
if required_bosses in {"wither", "both"}:
|
||||
advancements.update(postgame_advancements["wither"])
|
||||
return advancements
|
|
@ -6,7 +6,7 @@ class AdvancementGoal(Range):
|
|||
"""Number of advancements required to spawn bosses."""
|
||||
display_name = "Advancement Goal"
|
||||
range_start = 0
|
||||
range_end = 95
|
||||
range_end = 114
|
||||
default = 40
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@ class EggShardsRequired(Range):
|
|||
"""Number of dragon egg shards to collect to spawn bosses."""
|
||||
display_name = "Egg Shards Required"
|
||||
range_start = 0
|
||||
range_end = 40
|
||||
range_end = 74
|
||||
default = 0
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@ class EggShardsAvailable(Range):
|
|||
"""Number of dragon egg shards available to collect."""
|
||||
display_name = "Egg Shards Available"
|
||||
range_start = 0
|
||||
range_end = 40
|
||||
range_end = 74
|
||||
default = 0
|
||||
|
||||
|
||||
|
@ -35,6 +35,14 @@ class BossGoal(Choice):
|
|||
option_both = 3
|
||||
default = 1
|
||||
|
||||
@property
|
||||
def dragon(self):
|
||||
return self.value % 2 == 1
|
||||
|
||||
@property
|
||||
def wither(self):
|
||||
return self.value > 1
|
||||
|
||||
|
||||
class ShuffleStructures(DefaultOnToggle):
|
||||
"""Enables shuffling of villages, outposts, fortresses, bastions, and end cities."""
|
||||
|
@ -94,14 +102,16 @@ minecraft_options: typing.Dict[str, type(Option)] = {
|
|||
"egg_shards_required": EggShardsRequired,
|
||||
"egg_shards_available": EggShardsAvailable,
|
||||
"required_bosses": BossGoal,
|
||||
|
||||
"shuffle_structures": ShuffleStructures,
|
||||
"structure_compasses": StructureCompasses,
|
||||
"bee_traps": BeeTraps,
|
||||
|
||||
"combat_difficulty": CombatDifficulty,
|
||||
"include_hard_advancements": HardAdvancements,
|
||||
"include_unreasonable_advancements": UnreasonableAdvancements,
|
||||
"include_postgame_advancements": PostgameAdvancements,
|
||||
"bee_traps": BeeTraps,
|
||||
"send_defeated_mobs": SendDefeatedMobs,
|
||||
"starting_items": StartingItems,
|
||||
"death_link": DeathLink,
|
||||
"starting_items": StartingItems,
|
||||
}
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
|
||||
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))
|
||||
|
||||
# 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} ({world.player_name[player]})")
|
||||
|
||||
pairs = {}
|
||||
|
||||
def set_pair(exit, 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_name[player]})")
|
||||
|
||||
# Connect plando structures first
|
||||
if world.plando_connections[player]:
|
||||
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]:
|
||||
structs.sort(reverse=True, key=lambda s: len(illegal_connections.get(s, [])))
|
||||
for struct in structs[:]:
|
||||
try:
|
||||
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_name[player]})")
|
||||
set_pair(exit, struct)
|
||||
else: # write remaining default connections
|
||||
for (exit, struct) in default_connections:
|
||||
if exit in exits:
|
||||
set_pair(exit, struct)
|
||||
|
||||
# Make sure we actually paired everything; might fail if plando
|
||||
try:
|
||||
assert len(exits) == len(structs) == 0
|
||||
except AssertionError:
|
||||
raise Exception(f"Failed to connect all Minecraft structures for player {player} ({world.player_name[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)
|
||||
|
||||
|
||||
|
||||
# (Region name, list of exits)
|
||||
mc_regions = [
|
||||
('Menu', ['New World']),
|
||||
('Overworld', ['Nether Portal', 'End Portal', 'Overworld Structure 1', 'Overworld Structure 2']),
|
||||
('The Nether', ['Nether Structure 1', 'Nether Structure 2']),
|
||||
('The End', ['The End Structure']),
|
||||
('Village', []),
|
||||
('Pillager Outpost', []),
|
||||
('Nether Fortress', []),
|
||||
('Bastion Remnant', []),
|
||||
('End City', [])
|
||||
]
|
||||
|
||||
# (Entrance, region pointed to)
|
||||
mandatory_connections = [
|
||||
('New World', 'Overworld'),
|
||||
('Nether Portal', 'The Nether'),
|
||||
('End Portal', 'The End')
|
||||
]
|
||||
|
||||
default_connections = [
|
||||
('Overworld Structure 1', 'Village'),
|
||||
('Overworld Structure 2', 'Pillager Outpost'),
|
||||
('Nether Structure 1', 'Nether Fortress'),
|
||||
('Nether Structure 2', 'Bastion Remnant'),
|
||||
('The End Structure', 'End City')
|
||||
]
|
||||
|
||||
# Structure: illegal locations
|
||||
illegal_connections = {
|
||||
'Nether Fortress': ['The End Structure']
|
||||
}
|
||||
|
|
@ -1,317 +1,313 @@
|
|||
from ..generic.Rules import set_rule, add_rule
|
||||
from .Locations import exclusion_table, get_postgame_advancements
|
||||
from BaseClasses import MultiWorld
|
||||
from ..AutoWorld import LogicMixin
|
||||
import typing
|
||||
from collections.abc import Callable
|
||||
|
||||
from BaseClasses import CollectionState
|
||||
from worlds.generic.Rules import exclusion_rules
|
||||
from worlds.AutoWorld import World
|
||||
|
||||
from . import Constants
|
||||
|
||||
# Helper functions
|
||||
# moved from logicmixin
|
||||
|
||||
def has_iron_ingots(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Progressive Tools', player) and state.has('Progressive Resource Crafting', player)
|
||||
|
||||
def has_copper_ingots(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Progressive Tools', player) and state.has('Progressive Resource Crafting', player)
|
||||
|
||||
def has_gold_ingots(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Progressive Resource Crafting', player) and (state.has('Progressive Tools', player, 2) or state.can_reach('The Nether', 'Region', player))
|
||||
|
||||
def has_diamond_pickaxe(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Progressive Tools', player, 3) and has_iron_ingots(state, player)
|
||||
|
||||
def craft_crossbow(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Archery', player) and has_iron_ingots(state, player)
|
||||
|
||||
def has_bottle(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Bottles', player) and state.has('Progressive Resource Crafting', player)
|
||||
|
||||
def has_spyglass(state: CollectionState, player: int) -> bool:
|
||||
return has_copper_ingots(state, player) and state.has('Spyglass', player) and can_adventure(state, player)
|
||||
|
||||
def can_enchant(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Enchanting', player) and has_diamond_pickaxe(state, player) # mine obsidian and lapis
|
||||
|
||||
def can_use_anvil(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Enchanting', player) and state.has('Progressive Resource Crafting', player, 2) and has_iron_ingots(state, player)
|
||||
|
||||
def fortress_loot(state: CollectionState, player: int) -> bool: # saddles, blaze rods, wither skulls
|
||||
return state.can_reach('Nether Fortress', 'Region', player) and basic_combat(state, player)
|
||||
|
||||
def can_brew_potions(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Blaze Rods', player) and state.has('Brewing', player) and has_bottle(state, player)
|
||||
|
||||
def can_piglin_trade(state: CollectionState, player: int) -> bool:
|
||||
return has_gold_ingots(state, player) and (
|
||||
state.can_reach('The Nether', 'Region', player) or
|
||||
state.can_reach('Bastion Remnant', 'Region', player))
|
||||
|
||||
def overworld_villager(state: CollectionState, player: int) -> bool:
|
||||
village_region = state.multiworld.get_region('Village', player).entrances[0].parent_region.name
|
||||
if village_region == 'The Nether': # 2 options: cure zombie villager or build portal in village
|
||||
return (state.can_reach('Zombie Doctor', 'Location', player) or
|
||||
(has_diamond_pickaxe(state, player) and state.can_reach('Village', 'Region', player)))
|
||||
elif village_region == 'The End':
|
||||
return state.can_reach('Zombie Doctor', 'Location', player)
|
||||
return state.can_reach('Village', 'Region', player)
|
||||
|
||||
def enter_stronghold(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Blaze Rods', player) and state.has('Brewing', player) and state.has('3 Ender Pearls', player)
|
||||
|
||||
# Difficulty-dependent functions
|
||||
def combat_difficulty(state: CollectionState, player: int) -> bool:
|
||||
return state.multiworld.combat_difficulty[player].current_key
|
||||
|
||||
def can_adventure(state: CollectionState, player: int) -> bool:
|
||||
death_link_check = not state.multiworld.death_link[player] or state.has('Bed', player)
|
||||
if combat_difficulty(state, player) == 'easy':
|
||||
return state.has('Progressive Weapons', player, 2) and has_iron_ingots(state, player) and death_link_check
|
||||
elif combat_difficulty(state, player) == 'hard':
|
||||
return True
|
||||
return (state.has('Progressive Weapons', player) and death_link_check and
|
||||
(state.has('Progressive Resource Crafting', player) or state.has('Campfire', player)))
|
||||
|
||||
def basic_combat(state: CollectionState, player: int) -> bool:
|
||||
if combat_difficulty(state, player) == 'easy':
|
||||
return state.has('Progressive Weapons', player, 2) and state.has('Progressive Armor', player) and \
|
||||
state.has('Shield', player) and has_iron_ingots(state, player)
|
||||
elif combat_difficulty(state, player) == 'hard':
|
||||
return True
|
||||
return state.has('Progressive Weapons', player) and (state.has('Progressive Armor', player) or state.has('Shield', player)) and has_iron_ingots(state, player)
|
||||
|
||||
def complete_raid(state: CollectionState, player: int) -> bool:
|
||||
reach_regions = state.can_reach('Village', 'Region', player) and state.can_reach('Pillager Outpost', 'Region', player)
|
||||
if combat_difficulty(state, player) == 'easy':
|
||||
return reach_regions and \
|
||||
state.has('Progressive Weapons', player, 3) and state.has('Progressive Armor', player, 2) and \
|
||||
state.has('Shield', player) and state.has('Archery', player) and \
|
||||
state.has('Progressive Tools', player, 2) and has_iron_ingots(state, player)
|
||||
elif combat_difficulty(state, player) == 'hard': # might be too hard?
|
||||
return reach_regions and state.has('Progressive Weapons', player, 2) and has_iron_ingots(state, player) and \
|
||||
(state.has('Progressive Armor', player) or state.has('Shield', player))
|
||||
return reach_regions and state.has('Progressive Weapons', player, 2) and has_iron_ingots(state, player) and \
|
||||
state.has('Progressive Armor', player) and state.has('Shield', player)
|
||||
|
||||
def can_kill_wither(state: CollectionState, player: int) -> bool:
|
||||
normal_kill = state.has("Progressive Weapons", player, 3) and state.has("Progressive Armor", player, 2) and can_brew_potions(state, player) and can_enchant(state, player)
|
||||
if combat_difficulty(state, player) == 'easy':
|
||||
return fortress_loot(state, player) and normal_kill and state.has('Archery', player)
|
||||
elif combat_difficulty(state, player) == 'hard': # cheese kill using bedrock ceilings
|
||||
return fortress_loot(state, player) and (normal_kill or state.can_reach('The Nether', 'Region', player) or state.can_reach('The End', 'Region', player))
|
||||
return fortress_loot(state, player) and normal_kill
|
||||
|
||||
def can_respawn_ender_dragon(state: CollectionState, player: int) -> bool:
|
||||
return state.can_reach('The Nether', 'Region', player) and state.can_reach('The End', 'Region', player) and \
|
||||
state.has('Progressive Resource Crafting', player) # smelt sand into glass
|
||||
|
||||
def can_kill_ender_dragon(state: CollectionState, player: int) -> bool:
|
||||
if combat_difficulty(state, player) == 'easy':
|
||||
return state.has("Progressive Weapons", player, 3) and state.has("Progressive Armor", player, 2) and \
|
||||
state.has('Archery', player) and can_brew_potions(state, player) and can_enchant(state, player)
|
||||
if combat_difficulty(state, player) == 'hard':
|
||||
return (state.has('Progressive Weapons', player, 2) and state.has('Progressive Armor', player)) or \
|
||||
(state.has('Progressive Weapons', player, 1) and state.has('Bed', player))
|
||||
return state.has('Progressive Weapons', player, 2) and state.has('Progressive Armor', player) and state.has('Archery', player)
|
||||
|
||||
def has_structure_compass(state: CollectionState, entrance_name: str, player: int) -> bool:
|
||||
if not state.multiworld.structure_compasses[player]:
|
||||
return True
|
||||
return state.has(f"Structure Compass ({state.multiworld.get_entrance(entrance_name, player).connected_region.name})", player)
|
||||
|
||||
|
||||
class MinecraftLogic(LogicMixin):
|
||||
def get_rules_lookup(player: int):
|
||||
rules_lookup: typing.Dict[str, typing.List[Callable[[CollectionState], bool]]] = {
|
||||
"entrances": {
|
||||
"Nether Portal": lambda state: (state.has('Flint and Steel', player) and
|
||||
(state.has('Bucket', player) or state.has('Progressive Tools', player, 3)) and
|
||||
has_iron_ingots(state, player)),
|
||||
"End Portal": lambda state: enter_stronghold(state, player) and state.has('3 Ender Pearls', player, 4),
|
||||
"Overworld Structure 1": lambda state: (can_adventure(state, player) and has_structure_compass(state, "Overworld Structure 1", player)),
|
||||
"Overworld Structure 2": lambda state: (can_adventure(state, player) and has_structure_compass(state, "Overworld Structure 2", player)),
|
||||
"Nether Structure 1": lambda state: (can_adventure(state, player) and has_structure_compass(state, "Nether Structure 1", player)),
|
||||
"Nether Structure 2": lambda state: (can_adventure(state, player) and has_structure_compass(state, "Nether Structure 2", player)),
|
||||
"The End Structure": lambda state: (can_adventure(state, player) and has_structure_compass(state, "The End Structure", player)),
|
||||
},
|
||||
"locations": {
|
||||
"Ender Dragon": lambda state: can_respawn_ender_dragon(state, player) and can_kill_ender_dragon(state, player),
|
||||
"Wither": lambda state: can_kill_wither(state, player),
|
||||
"Blaze Rods": lambda state: fortress_loot(state, player),
|
||||
|
||||
def _mc_has_iron_ingots(self, player: int):
|
||||
return self.has('Progressive Tools', player) and self.has('Progressive Resource Crafting', player)
|
||||
|
||||
def _mc_has_copper_ingots(self, player: int):
|
||||
return self.has('Progressive Tools', player) and self.has('Progressive Resource Crafting', player)
|
||||
|
||||
def _mc_has_gold_ingots(self, player: int):
|
||||
return self.has('Progressive Resource 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('Progressive Resource Crafting', player)
|
||||
|
||||
def _mc_has_spyglass(self, player: int):
|
||||
return self._mc_has_copper_ingots(player) and self.has('Spyglass', player) and self._mc_can_adventure(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('Progressive Resource Crafting', player, 2) 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.has('Blaze Rods', 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_overworld_villager(self, player: int):
|
||||
village_region = self.multiworld.get_region('Village', player).entrances[0].parent_region.name
|
||||
if village_region == 'The Nether': # 2 options: cure zombie villager or build portal in village
|
||||
return (self.can_reach('Zombie Doctor', 'Location', player) or
|
||||
(self._mc_has_diamond_pickaxe(player) and self.can_reach('Village', 'Region', player)))
|
||||
elif village_region == 'The End':
|
||||
return self.can_reach('Zombie Doctor', 'Location', player)
|
||||
return self.can_reach('Village', 'Region', player)
|
||||
|
||||
def _mc_enter_stronghold(self, player: int):
|
||||
return self.has('Blaze Rods', 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.multiworld.combat_difficulty[player].current_key
|
||||
|
||||
def _mc_can_adventure(self, player: int):
|
||||
death_link_check = not self.multiworld.death_link[player] or self.has('Bed', player)
|
||||
if self._mc_combat_difficulty(player) == 'easy':
|
||||
return self.has('Progressive Weapons', player, 2) and self._mc_has_iron_ingots(player) and death_link_check
|
||||
elif self._mc_combat_difficulty(player) == 'hard':
|
||||
return True
|
||||
return (self.has('Progressive Weapons', player) and death_link_check and
|
||||
(self.has('Progressive Resource 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_respawn_ender_dragon(self, player: int):
|
||||
return self.can_reach('The Nether', 'Region', player) and self.can_reach('The End', 'Region', player) and \
|
||||
self.has('Progressive Resource Crafting', player) # smelt sand into glass
|
||||
|
||||
def _mc_can_kill_ender_dragon(self, player: int):
|
||||
if self._mc_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._mc_can_brew_potions(player) and self._mc_can_enchant(player)
|
||||
if self._mc_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)
|
||||
|
||||
def _mc_has_structure_compass(self, entrance_name: str, player: int):
|
||||
if not self.multiworld.structure_compasses[player]:
|
||||
return True
|
||||
return self.has(f"Structure Compass ({self.multiworld.get_entrance(entrance_name, player).connected_region.name})", player)
|
||||
|
||||
# Sets rules on entrances and advancements that are always applied
|
||||
def set_advancement_rules(world: MultiWorld, player: int):
|
||||
|
||||
# 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})"
|
||||
|
||||
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._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: state._mc_can_kill_ender_dragon(player))
|
||||
set_rule(world.get_location("Wither", player), lambda state: state._mc_can_kill_wither(player))
|
||||
set_rule(world.get_location("Blaze Spawner", player), lambda state: state._mc_fortress_loot(player))
|
||||
|
||||
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 state._mc_overworld_villager(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: state._mc_can_respawn_ender_dragon(player) and state._mc_can_kill_ender_dragon(player))
|
||||
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._mc_can_kill_wither(player) and
|
||||
state._mc_has_diamond_pickaxe(player) and state.has("Progressive Resource Crafting", player, 2))
|
||||
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: state._mc_can_kill_ender_dragon(player))
|
||||
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._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._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._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.has("Bucket", player) and state._mc_can_adventure(player)) # shears > seagrass > turtles; buckets of tropical fish > axolotls; 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._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._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._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._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._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._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: state._mc_can_respawn_ender_dragon(player) and state._mc_can_kill_ender_dragon(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("Progressive Resource Crafting", player, 2))
|
||||
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._mc_basic_combat(player))
|
||||
set_rule(world.get_location("Hired Help", player), lambda state: state.has("Progressive Resource Crafting", player, 2) 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: state._mc_can_respawn_ender_dragon(player) 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: state._mc_can_respawn_ender_dragon(player) and state._mc_can_kill_ender_dragon(player) 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._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._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._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("Progressive Resource Crafting", player, 2))
|
||||
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("Progressive Resource Crafting", player, 2) and state.can_reach('The End', 'Region', player)) # notch apple, chorus fruit
|
||||
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._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._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: state._mc_can_adventure(player)) # 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._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("Progressive Resource Crafting", player) and
|
||||
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
|
||||
# set_rule(world.get_location("Getting Wood", player), lambda state: True)
|
||||
# set_rule(world.get_location("Time to Mine!", player), lambda state: True)
|
||||
set_rule(world.get_location("Hot Topic", player), lambda state: state.has("Progressive Resource Crafting", player))
|
||||
# set_rule(world.get_location("Bake Bread", player), lambda state: True)
|
||||
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._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._mc_has_iron_ingots(player) and
|
||||
state.has('Progressive Tools', player, 2) and state._mc_basic_combat(player)) # mine gold blocks w/ iron pick
|
||||
set_rule(world.get_location("Wax On", player), lambda state: state._mc_has_copper_ingots(player) and state.has('Campfire', player) and
|
||||
state.has('Progressive Resource Crafting', player, 2))
|
||||
set_rule(world.get_location("Wax Off", player), lambda state: state._mc_has_copper_ingots(player) and state.has('Campfire', player) and
|
||||
state.has('Progressive Resource Crafting', player, 2))
|
||||
set_rule(world.get_location("The Cutest Predator", player), lambda state: state._mc_has_iron_ingots(player) and state.has('Bucket', player))
|
||||
set_rule(world.get_location("The Healing Power of Friendship", player), lambda state: state._mc_has_iron_ingots(player) and state.has('Bucket', player))
|
||||
set_rule(world.get_location("Is It a Bird?", player), lambda state: state._mc_has_spyglass(player) and state._mc_can_adventure(player))
|
||||
set_rule(world.get_location("Is It a Balloon?", player), lambda state: state._mc_has_spyglass(player))
|
||||
set_rule(world.get_location("Is It a Plane?", player), lambda state: state._mc_has_spyglass(player) and state._mc_can_respawn_ender_dragon(player))
|
||||
set_rule(world.get_location("Surge Protector", player), lambda state: state.has("Channeling Book", player) and
|
||||
state._mc_can_use_anvil(player) and state._mc_can_enchant(player) and state._mc_overworld_villager(player))
|
||||
set_rule(world.get_location("Light as a Rabbit", player), lambda state: state._mc_can_adventure(player) and state._mc_has_iron_ingots(player) and state.has('Bucket', player))
|
||||
set_rule(world.get_location("Glow and Behold!", player), lambda state: state._mc_can_adventure(player))
|
||||
set_rule(world.get_location("Whatever Floats Your Goat!", player), lambda state: state._mc_can_adventure(player))
|
||||
set_rule(world.get_location("Caves & Cliffs", player), lambda state: state._mc_has_iron_ingots(player) and state.has('Bucket', player) and state.has('Progressive Tools', player, 2))
|
||||
set_rule(world.get_location("Feels like home", player), lambda state: state._mc_has_iron_ingots(player) and state.has('Bucket', player) and state.has('Fishing Rod', player) and
|
||||
(state._mc_fortress_loot(player) or state._mc_complete_raid(player)) and state.has("Saddle", player))
|
||||
set_rule(world.get_location("Sound of Music", player), lambda state: state.can_reach("Diamonds!", "Location", player) and state._mc_basic_combat(player))
|
||||
set_rule(world.get_location("Star Trader", player), lambda state: state._mc_has_iron_ingots(player) and state.has('Bucket', player) and
|
||||
(state.can_reach("The Nether", 'Region', player) or state.can_reach("Nether Fortress", 'Region', player) or state._mc_can_piglin_trade(player)) and # soul sand for water elevator
|
||||
state._mc_overworld_villager(player))
|
||||
|
||||
# 1.19 advancements
|
||||
|
||||
# can make a cake, and a noteblock, and can reach a pillager outposts for allays
|
||||
set_rule(world.get_location("Birthday Song", player), lambda state: state.can_reach("The Lie", "Location", player) and state.has("Progressive Tools", player, 2) and state._mc_has_iron_ingots(player))
|
||||
# can get to outposts.
|
||||
# set_rule(world.get_location("You've Got a Friend in Me", player), lambda state: True)
|
||||
# craft bucket and adventure to find frog spawning biome
|
||||
set_rule(world.get_location("Bukkit Bukkit", player), lambda state: state.has("Bucket", player) and state._mc_has_iron_ingots(player) and state._mc_can_adventure(player))
|
||||
# I don't like this one its way to easy to get. just a pain to find.
|
||||
set_rule(world.get_location("It Spreads", player), lambda state: state._mc_can_adventure(player) and state._mc_has_iron_ingots(player) and state.has("Progressive Tools", player, 2))
|
||||
# literally just a duplicate of It spreads.
|
||||
set_rule(world.get_location("Sneak 100", player), lambda state: state._mc_can_adventure(player) and state._mc_has_iron_ingots(player) and state.has("Progressive Tools", player, 2))
|
||||
set_rule(world.get_location("When the Squad Hops into Town", player), lambda state: state._mc_can_adventure(player) and state.has("Lead", player))
|
||||
# lead frogs to the nether and a basalt delta's biomes to find magma cubes.
|
||||
set_rule(world.get_location("With Our Powers Combined!", player), lambda state: state._mc_can_adventure(player) and state.has("Lead", player))
|
||||
"Who is Cutting Onions?": lambda state: can_piglin_trade(state, player),
|
||||
"Oh Shiny": lambda state: can_piglin_trade(state, player),
|
||||
"Suit Up": lambda state: state.has("Progressive Armor", player) and has_iron_ingots(state, player),
|
||||
"Very Very Frightening": lambda state: (state.has("Channeling Book", player) and
|
||||
can_use_anvil(state, player) and can_enchant(state, player) and overworld_villager(state, player)),
|
||||
"Hot Stuff": lambda state: state.has("Bucket", player) and has_iron_ingots(state, player),
|
||||
"Free the End": lambda state: can_respawn_ender_dragon(state, player) and can_kill_ender_dragon(state, player),
|
||||
"A Furious Cocktail": lambda state: (can_brew_potions(state, 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
|
||||
"Bring Home the Beacon": lambda state: (can_kill_wither(state, player) and
|
||||
has_diamond_pickaxe(state, player) and state.has("Progressive Resource Crafting", player, 2)),
|
||||
"Not Today, Thank You": lambda state: state.has("Shield", player) and has_iron_ingots(state, player),
|
||||
"Isn't It Iron Pick": lambda state: state.has("Progressive Tools", player, 2) and has_iron_ingots(state, player),
|
||||
"Local Brewery": lambda state: can_brew_potions(state, player),
|
||||
"The Next Generation": lambda state: can_respawn_ender_dragon(state, player) and can_kill_ender_dragon(state, player),
|
||||
"Fishy Business": lambda state: state.has("Fishing Rod", player),
|
||||
"This Boat Has Legs": lambda state: ((fortress_loot(state, player) or complete_raid(state, player)) and
|
||||
state.has("Saddle", player) and state.has("Fishing Rod", player)),
|
||||
"Sniper Duel": lambda state: state.has("Archery", player),
|
||||
"Great View From Up Here": lambda state: basic_combat(state, player),
|
||||
"How Did We Get Here?": lambda state: (can_brew_potions(state, player) and
|
||||
has_gold_ingots(state, 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
|
||||
"Bullseye": lambda state: (state.has("Archery", player) and state.has("Progressive Tools", player, 2) and
|
||||
has_iron_ingots(state, player)),
|
||||
"Spooky Scary Skeleton": lambda state: basic_combat(state, player),
|
||||
"Two by Two": lambda state: has_iron_ingots(state, player) and state.has("Bucket", player) and can_adventure(state, player),
|
||||
"Two Birds, One Arrow": lambda state: craft_crossbow(state, player) and can_enchant(state, player),
|
||||
"Who's the Pillager Now?": lambda state: craft_crossbow(state, player),
|
||||
"Getting an Upgrade": lambda state: state.has("Progressive Tools", player),
|
||||
"Tactical Fishing": lambda state: state.has("Bucket", player) and has_iron_ingots(state, player),
|
||||
"Zombie Doctor": lambda state: can_brew_potions(state, player) and has_gold_ingots(state, player),
|
||||
"Ice Bucket Challenge": lambda state: has_diamond_pickaxe(state, player),
|
||||
"Into Fire": lambda state: basic_combat(state, player),
|
||||
"War Pigs": lambda state: basic_combat(state, player),
|
||||
"Take Aim": lambda state: state.has("Archery", player),
|
||||
"Total Beelocation": lambda state: state.has("Silk Touch Book", player) and can_use_anvil(state, player) and can_enchant(state, player),
|
||||
"Arbalistic": lambda state: (craft_crossbow(state, player) and state.has("Piercing IV Book", player) and
|
||||
can_use_anvil(state, player) and can_enchant(state, player)),
|
||||
"The End... Again...": lambda state: can_respawn_ender_dragon(state, player) and can_kill_ender_dragon(state, player),
|
||||
"Acquire Hardware": lambda state: has_iron_ingots(state, player),
|
||||
"Not Quite \"Nine\" Lives": lambda state: can_piglin_trade(state, player) and state.has("Progressive Resource Crafting", player, 2),
|
||||
"Cover Me With Diamonds": lambda state: (state.has("Progressive Armor", player, 2) and
|
||||
state.has("Progressive Tools", player, 2) and has_iron_ingots(state, player)),
|
||||
"Sky's the Limit": lambda state: basic_combat(state, player),
|
||||
"Hired Help": lambda state: state.has("Progressive Resource Crafting", player, 2) and has_iron_ingots(state, player),
|
||||
"Sweet Dreams": lambda state: state.has("Bed", player) or state.can_reach('Village', 'Region', player),
|
||||
"You Need a Mint": lambda state: can_respawn_ender_dragon(state, player) and has_bottle(state, player),
|
||||
"Monsters Hunted": lambda state: (can_respawn_ender_dragon(state, player) and can_kill_ender_dragon(state, player) and
|
||||
can_kill_wither(state, player) and state.has("Fishing Rod", player)),
|
||||
"Enchanter": lambda state: can_enchant(state, player),
|
||||
"Voluntary Exile": lambda state: basic_combat(state, player),
|
||||
"Eye Spy": lambda state: enter_stronghold(state, player),
|
||||
"Serious Dedication": lambda state: (can_brew_potions(state, player) and state.has("Bed", player) and
|
||||
has_diamond_pickaxe(state, player) and has_gold_ingots(state, player)),
|
||||
"Postmortal": lambda state: complete_raid(state, player),
|
||||
"Adventuring Time": lambda state: can_adventure(state, player),
|
||||
"Hero of the Village": lambda state: complete_raid(state, player),
|
||||
"Hidden in the Depths": lambda state: can_brew_potions(state, player) and state.has("Bed", player) and has_diamond_pickaxe(state, player),
|
||||
"Beaconator": lambda state: (can_kill_wither(state, player) and has_diamond_pickaxe(state, player) and
|
||||
state.has("Progressive Resource Crafting", player, 2)),
|
||||
"Withering Heights": lambda state: can_kill_wither(state, player),
|
||||
"A Balanced Diet": lambda state: (has_bottle(state, player) and has_gold_ingots(state, player) and # honey bottle; gapple
|
||||
state.has("Progressive Resource Crafting", player, 2) and state.can_reach('The End', 'Region', player)), # notch apple, chorus fruit
|
||||
"Subspace Bubble": lambda state: has_diamond_pickaxe(state, player),
|
||||
"Country Lode, Take Me Home": lambda state: state.can_reach("Hidden in the Depths", "Location", player) and has_gold_ingots(state, player),
|
||||
"Bee Our Guest": lambda state: state.has("Campfire", player) and has_bottle(state, player),
|
||||
"Uneasy Alliance": lambda state: has_diamond_pickaxe(state, player) and state.has('Fishing Rod', player),
|
||||
"Diamonds!": lambda state: state.has("Progressive Tools", player, 2) and has_iron_ingots(state, player),
|
||||
"A Throwaway Joke": lambda state: can_adventure(state, player),
|
||||
"Sticky Situation": lambda state: state.has("Campfire", player) and has_bottle(state, player),
|
||||
"Ol' Betsy": lambda state: craft_crossbow(state, player),
|
||||
"Cover Me in Debris": lambda state: (state.has("Progressive Armor", player, 2) and
|
||||
state.has("8 Netherite Scrap", player, 2) and state.has("Progressive Resource Crafting", player) and
|
||||
has_diamond_pickaxe(state, player) and has_iron_ingots(state, player) and
|
||||
can_brew_potions(state, player) and state.has("Bed", player)),
|
||||
"Hot Topic": lambda state: state.has("Progressive Resource Crafting", player),
|
||||
"The Lie": lambda state: has_iron_ingots(state, player) and state.has("Bucket", player),
|
||||
"On a Rail": lambda state: has_iron_ingots(state, player) and state.has('Progressive Tools', player, 2),
|
||||
"When Pigs Fly": lambda state: ((fortress_loot(state, player) or complete_raid(state, player)) and
|
||||
state.has("Saddle", player) and state.has("Fishing Rod", player) and can_adventure(state, player)),
|
||||
"Overkill": lambda state: (can_brew_potions(state, player) and
|
||||
(state.has("Progressive Weapons", player) or state.can_reach('The Nether', 'Region', player))),
|
||||
"Librarian": lambda state: state.has("Enchanting", player),
|
||||
"Overpowered": lambda state: (has_iron_ingots(state, player) and
|
||||
state.has('Progressive Tools', player, 2) and basic_combat(state, player)),
|
||||
"Wax On": lambda state: (has_copper_ingots(state, player) and state.has('Campfire', player) and
|
||||
state.has('Progressive Resource Crafting', player, 2)),
|
||||
"Wax Off": lambda state: (has_copper_ingots(state, player) and state.has('Campfire', player) and
|
||||
state.has('Progressive Resource Crafting', player, 2)),
|
||||
"The Cutest Predator": lambda state: has_iron_ingots(state, player) and state.has('Bucket', player),
|
||||
"The Healing Power of Friendship": lambda state: has_iron_ingots(state, player) and state.has('Bucket', player),
|
||||
"Is It a Bird?": lambda state: has_spyglass(state, player) and can_adventure(state, player),
|
||||
"Is It a Balloon?": lambda state: has_spyglass(state, player),
|
||||
"Is It a Plane?": lambda state: has_spyglass(state, player) and can_respawn_ender_dragon(state, player),
|
||||
"Surge Protector": lambda state: (state.has("Channeling Book", player) and
|
||||
can_use_anvil(state, player) and can_enchant(state, player) and overworld_villager(state, player)),
|
||||
"Light as a Rabbit": lambda state: can_adventure(state, player) and has_iron_ingots(state, player) and state.has('Bucket', player),
|
||||
"Glow and Behold!": lambda state: can_adventure(state, player),
|
||||
"Whatever Floats Your Goat!": lambda state: can_adventure(state, player),
|
||||
"Caves & Cliffs": lambda state: has_iron_ingots(state, player) and state.has('Bucket', player) and state.has('Progressive Tools', player, 2),
|
||||
"Feels like home": lambda state: (has_iron_ingots(state, player) and state.has('Bucket', player) and state.has('Fishing Rod', player) and
|
||||
(fortress_loot(state, player) or complete_raid(state, player)) and state.has("Saddle", player)),
|
||||
"Sound of Music": lambda state: state.has("Progressive Tools", player, 2) and has_iron_ingots(state, player) and basic_combat(state, player),
|
||||
"Star Trader": lambda state: (has_iron_ingots(state, player) and state.has('Bucket', player) and
|
||||
(state.can_reach("The Nether", 'Region', player) or
|
||||
state.can_reach("Nether Fortress", 'Region', player) or # soul sand for water elevator
|
||||
can_piglin_trade(state, player)) and
|
||||
overworld_villager(state, player)),
|
||||
"Birthday Song": lambda state: state.can_reach("The Lie", "Location", player) and state.has("Progressive Tools", player, 2) and has_iron_ingots(state, player),
|
||||
"Bukkit Bukkit": lambda state: state.has("Bucket", player) and has_iron_ingots(state, player) and can_adventure(state, player),
|
||||
"It Spreads": lambda state: can_adventure(state, player) and has_iron_ingots(state, player) and state.has("Progressive Tools", player, 2),
|
||||
"Sneak 100": lambda state: can_adventure(state, player) and has_iron_ingots(state, player) and state.has("Progressive Tools", player, 2),
|
||||
"When the Squad Hops into Town": lambda state: can_adventure(state, player) and state.has("Lead", player),
|
||||
"With Our Powers Combined!": lambda state: can_adventure(state, player) and state.has("Lead", player),
|
||||
}
|
||||
}
|
||||
return rules_lookup
|
||||
|
||||
|
||||
# Sets rules on completion condition and postgame advancements
|
||||
def set_completion_rules(world: MultiWorld, player: int):
|
||||
def reachable_locations(state):
|
||||
postgame_advancements = get_postgame_advancements(world.required_bosses[player].current_key)
|
||||
return [location for location in world.get_locations() if
|
||||
location.player == player and
|
||||
location.name not in postgame_advancements and
|
||||
location.address != None and
|
||||
location.can_reach(state)]
|
||||
def set_rules(mc_world: World) -> None:
|
||||
multiworld = mc_world.multiworld
|
||||
player = mc_world.player
|
||||
|
||||
def defeated_required_bosses(state):
|
||||
return (world.required_bosses[player].current_key not in {"ender_dragon", "both"} or state.has("Defeat Ender Dragon", player)) and \
|
||||
(world.required_bosses[player].current_key not in {"wither", "both"} or state.has("Defeat Wither", player))
|
||||
rules_lookup = get_rules_lookup(player)
|
||||
|
||||
# 103 total advancements. Goal is to complete X advancements and then defeat the dragon.
|
||||
# There are 11 possible postgame advancements; 5 for dragon, 5 for wither, 1 shared between them
|
||||
# Hence the max for completion is 92
|
||||
egg_shards = min(world.egg_shards_required[player], world.egg_shards_available[player])
|
||||
completion_requirements = lambda state: len(reachable_locations(state)) >= world.advancement_goal[player] and \
|
||||
state.has("Dragon Egg Shard", player, egg_shards)
|
||||
world.completion_condition[player] = lambda state: completion_requirements(state) and defeated_required_bosses(state)
|
||||
# Set rules on postgame advancements
|
||||
for adv_name in get_postgame_advancements(world.required_bosses[player].current_key):
|
||||
add_rule(world.get_location(adv_name, player), completion_requirements)
|
||||
# Set entrance rules
|
||||
for entrance_name, rule in rules_lookup["entrances"].items():
|
||||
multiworld.get_entrance(entrance_name, player).access_rule = rule
|
||||
|
||||
# Set location rules
|
||||
for location_name, rule in rules_lookup["locations"].items():
|
||||
multiworld.get_location(location_name, player).access_rule = rule
|
||||
|
||||
# Set rules surrounding completion
|
||||
bosses = multiworld.required_bosses[player]
|
||||
postgame_advancements = set()
|
||||
if bosses.dragon:
|
||||
postgame_advancements.update(Constants.exclusion_info["ender_dragon"])
|
||||
if bosses.wither:
|
||||
postgame_advancements.update(Constants.exclusion_info["wither"])
|
||||
|
||||
def location_count(state: CollectionState) -> bool:
|
||||
return len([location for location in multiworld.get_locations(player) if
|
||||
location.address != None and
|
||||
location.can_reach(state)])
|
||||
|
||||
def defeated_bosses(state: CollectionState) -> bool:
|
||||
return ((not bosses.dragon or state.has("Ender Dragon", player))
|
||||
and (not bosses.wither or state.has("Wither", player)))
|
||||
|
||||
egg_shards = min(multiworld.egg_shards_required[player], multiworld.egg_shards_available[player])
|
||||
completion_requirements = lambda state: (location_count(state) >= multiworld.advancement_goal[player]
|
||||
and state.has("Dragon Egg Shard", player, egg_shards))
|
||||
multiworld.completion_condition[player] = lambda state: completion_requirements(state) and defeated_bosses(state)
|
||||
|
||||
# Set exclusions on hard/unreasonable/postgame
|
||||
excluded_advancements = set()
|
||||
if not multiworld.include_hard_advancements[player]:
|
||||
excluded_advancements.update(Constants.exclusion_info["hard"])
|
||||
if not multiworld.include_unreasonable_advancements[player]:
|
||||
excluded_advancements.update(Constants.exclusion_info["unreasonable"])
|
||||
if not multiworld.include_postgame_advancements[player]:
|
||||
excluded_advancements.update(postgame_advancements)
|
||||
exclusion_rules(multiworld, player, excluded_advancements)
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
from worlds.AutoWorld import World
|
||||
|
||||
from . import Constants
|
||||
|
||||
def shuffle_structures(mc_world: World) -> None:
|
||||
multiworld = mc_world.multiworld
|
||||
player = mc_world.player
|
||||
|
||||
default_connections = Constants.region_info["default_connections"]
|
||||
illegal_connections = Constants.region_info["illegal_connections"]
|
||||
|
||||
# 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 multiworld.regions if r.player == player for exit in r.exits if exit.connected_region == None]
|
||||
structs = [r.name for r in multiworld.regions if r.player == player and r.entrances == [] and r.name != 'Menu']
|
||||
exits_spoiler = exits[:] # copy the original order for the spoiler log
|
||||
|
||||
pairs = {}
|
||||
|
||||
def set_pair(exit, 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} ({multiworld.player_name[player]})")
|
||||
|
||||
# Connect plando structures first
|
||||
if multiworld.plando_connections[player]:
|
||||
for conn in multiworld.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 multiworld.shuffle_structures[player]:
|
||||
structs.sort(reverse=True, key=lambda s: len(illegal_connections.get(s, [])))
|
||||
for struct in structs[:]:
|
||||
try:
|
||||
exit = multiworld.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} ({multiworld.player_name[player]})")
|
||||
set_pair(exit, struct)
|
||||
else: # write remaining default connections
|
||||
for (exit, struct) in default_connections:
|
||||
if exit in exits:
|
||||
set_pair(exit, struct)
|
||||
|
||||
# Make sure we actually paired everything; might fail if plando
|
||||
try:
|
||||
assert len(exits) == len(structs) == 0
|
||||
except AssertionError:
|
||||
raise Exception(f"Failed to connect all Minecraft structures for player {player} ({multiworld.player_name[player]})")
|
||||
|
||||
for exit in exits_spoiler:
|
||||
multiworld.get_entrance(exit, player).connect(multiworld.get_region(pairs[exit], player))
|
||||
if multiworld.shuffle_structures[player] or multiworld.plando_connections[player]:
|
||||
multiworld.spoiler.set_entrance(exit, pairs[exit], 'entrance', player)
|
|
@ -1,17 +1,16 @@
|
|||
import os
|
||||
import json
|
||||
from base64 import b64encode, b64decode
|
||||
from math import ceil
|
||||
from typing import Dict, Any
|
||||
|
||||
from .Items import MinecraftItem, item_table, required_items, junk_weights
|
||||
from .Locations import MinecraftAdvancement, advancement_table, exclusion_table, get_postgame_advancements
|
||||
from .Regions import mc_regions, link_minecraft_structures, default_connections
|
||||
from .Rules import set_advancement_rules, set_completion_rules
|
||||
from worlds.generic.Rules import exclusion_rules
|
||||
from BaseClasses import Region, Entrance, Item, Tutorial, ItemClassification, Location
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
|
||||
from BaseClasses import Region, Entrance, Item, Tutorial, ItemClassification
|
||||
from . import Constants
|
||||
from .Options import minecraft_options
|
||||
from ..AutoWorld import World, WebWorld
|
||||
from .Structures import shuffle_structures
|
||||
from .ItemPool import build_item_pool, get_junk_item_names
|
||||
from .Rules import set_rules
|
||||
|
||||
client_version = 9
|
||||
|
||||
|
@ -71,13 +70,13 @@ class MinecraftWorld(World):
|
|||
topology_present = True
|
||||
web = MinecraftWebWorld()
|
||||
|
||||
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()}
|
||||
item_name_to_id = Constants.item_name_to_id
|
||||
location_name_to_id = Constants.location_name_to_id
|
||||
|
||||
data_version = 7
|
||||
|
||||
def _get_mc_data(self):
|
||||
exits = [connection[0] for connection in default_connections]
|
||||
def _get_mc_data(self) -> Dict[str, Any]:
|
||||
exits = [connection[0] for connection in Constants.region_info["default_connections"]]
|
||||
return {
|
||||
'world_seed': self.multiworld.per_slot_randoms[self.player].getrandbits(32),
|
||||
'seed_name': self.multiworld.seed_name,
|
||||
|
@ -96,74 +95,70 @@ class MinecraftWorld(World):
|
|||
'race': self.multiworld.is_race,
|
||||
}
|
||||
|
||||
def generate_basic(self):
|
||||
def create_item(self, name: str) -> Item:
|
||||
item_class = ItemClassification.filler
|
||||
if name in Constants.item_info["progression_items"]:
|
||||
item_class = ItemClassification.progression
|
||||
elif name in Constants.item_info["useful_items"]:
|
||||
item_class = ItemClassification.useful
|
||||
elif name in Constants.item_info["trap_items"]:
|
||||
item_class = ItemClassification.trap
|
||||
|
||||
# Generate item pool
|
||||
itempool = []
|
||||
junk_pool = junk_weights.copy()
|
||||
# Add all required progression items
|
||||
for (name, num) in required_items.items():
|
||||
itempool += [name] * num
|
||||
# Add structure compasses if desired
|
||||
if self.multiworld.structure_compasses[self.player]:
|
||||
structures = [connection[1] for connection in default_connections]
|
||||
for struct_name in structures:
|
||||
itempool.append(f"Structure Compass ({struct_name})")
|
||||
# Add dragon egg shards
|
||||
if self.multiworld.egg_shards_required[self.player] > 0:
|
||||
itempool += ["Dragon Egg Shard"] * self.multiworld.egg_shards_available[self.player]
|
||||
# Add bee traps if desired
|
||||
bee_trap_quantity = ceil(self.multiworld.bee_traps[self.player] * (len(self.location_names) - len(itempool)) * 0.01)
|
||||
itempool += ["Bee Trap"] * bee_trap_quantity
|
||||
# Fill remaining items with randomly generated junk
|
||||
itempool += self.multiworld.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()), k=len(self.location_names) - len(itempool))
|
||||
# Convert itempool into real items
|
||||
itempool = [item for item in map(lambda name: self.create_item(name), itempool)]
|
||||
return MinecraftItem(name, item_class, self.item_name_to_id.get(name, None), self.player)
|
||||
|
||||
# Choose locations to automatically exclude based on settings
|
||||
exclusion_pool = set()
|
||||
exclusion_types = ['hard', 'unreasonable']
|
||||
for key in exclusion_types:
|
||||
if not getattr(self.multiworld, f"include_{key}_advancements")[self.player]:
|
||||
exclusion_pool.update(exclusion_table[key])
|
||||
# For postgame advancements, check with the boss goal
|
||||
exclusion_pool.update(get_postgame_advancements(self.multiworld.required_bosses[self.player].current_key))
|
||||
exclusion_rules(self.multiworld, self.player, exclusion_pool)
|
||||
def create_event(self, region_name: str, event_name: str) -> None:
|
||||
region = self.multiworld.get_region(region_name, self.player)
|
||||
loc = MinecraftLocation(self.player, event_name, None, region)
|
||||
loc.place_locked_item(self.create_event_item(event_name))
|
||||
region.locations.append(loc)
|
||||
|
||||
# Prefill event locations with their events
|
||||
self.multiworld.get_location("Blaze Spawner", self.player).place_locked_item(self.create_item("Blaze Rods"))
|
||||
self.multiworld.get_location("Ender Dragon", self.player).place_locked_item(self.create_item("Defeat Ender Dragon"))
|
||||
self.multiworld.get_location("Wither", self.player).place_locked_item(self.create_item("Defeat Wither"))
|
||||
def create_event_item(self, name: str) -> None:
|
||||
item = self.create_item(name)
|
||||
item.classification = ItemClassification.progression
|
||||
return item
|
||||
|
||||
self.multiworld.itempool += itempool
|
||||
def create_regions(self) -> None:
|
||||
# Create regions
|
||||
for region_name, exits in Constants.region_info["regions"]:
|
||||
r = Region(region_name, self.player, self.multiworld)
|
||||
for exit_name in exits:
|
||||
r.exits.append(Entrance(self.player, exit_name, r))
|
||||
self.multiworld.regions.append(r)
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return self.multiworld.random.choices(list(junk_weights.keys()), weights=list(junk_weights.values()))[0]
|
||||
# Bind mandatory connections
|
||||
for entr_name, region_name in Constants.region_info["mandatory_connections"]:
|
||||
e = self.multiworld.get_entrance(entr_name, self.player)
|
||||
r = self.multiworld.get_region(region_name, self.player)
|
||||
e.connect(r)
|
||||
|
||||
def set_rules(self):
|
||||
set_advancement_rules(self.multiworld, self.player)
|
||||
set_completion_rules(self.multiworld, self.player)
|
||||
# Add locations
|
||||
for region_name, locations in Constants.location_info["locations_by_region"].items():
|
||||
region = self.multiworld.get_region(region_name, self.player)
|
||||
for loc_name in locations:
|
||||
loc = MinecraftLocation(self.player, loc_name,
|
||||
self.location_name_to_id.get(loc_name, None), region)
|
||||
region.locations.append(loc)
|
||||
|
||||
def create_regions(self):
|
||||
def MCRegion(region_name: str, exits=[]):
|
||||
ret = Region(region_name, self.player, self.multiworld)
|
||||
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
|
||||
# Add events
|
||||
self.create_event("Nether Fortress", "Blaze Rods")
|
||||
self.create_event("The End", "Ender Dragon")
|
||||
self.create_event("Nether Fortress", "Wither")
|
||||
|
||||
self.multiworld.regions += [MCRegion(*r) for r in mc_regions]
|
||||
link_minecraft_structures(self.multiworld, self.player)
|
||||
# Shuffle the connections
|
||||
shuffle_structures(self)
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
def create_items(self) -> None:
|
||||
self.multiworld.itempool += build_item_pool(self)
|
||||
|
||||
set_rules = set_rules
|
||||
|
||||
def generate_output(self, output_directory: str) -> None:
|
||||
data = self._get_mc_data()
|
||||
filename = f"AP_{self.multiworld.get_out_file_name_base(self.player)}.apmc"
|
||||
with open(os.path.join(output_directory, filename), 'wb') as f:
|
||||
f.write(b64encode(bytes(json.dumps(data), 'utf-8')))
|
||||
|
||||
def fill_slot_data(self):
|
||||
def fill_slot_data(self) -> dict:
|
||||
slot_data = self._get_mc_data()
|
||||
for option_name in minecraft_options:
|
||||
option = getattr(self.multiworld, option_name)[self.player]
|
||||
|
@ -171,20 +166,16 @@ class MinecraftWorld(World):
|
|||
slot_data[option_name] = int(option.value)
|
||||
return slot_data
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
item_data = item_table[name]
|
||||
if name == "Bee Trap":
|
||||
classification = ItemClassification.trap
|
||||
# prevent books from going on excluded locations
|
||||
elif name in ("Sharpness III Book", "Infinity Book", "Looting III Book"):
|
||||
classification = ItemClassification.useful
|
||||
elif item_data.progression:
|
||||
classification = ItemClassification.progression
|
||||
else:
|
||||
classification = ItemClassification.filler
|
||||
item = MinecraftItem(name, classification, item_data.code, self.player)
|
||||
def get_filler_item_name(self) -> str:
|
||||
return get_junk_item_names(self.multiworld.random, 1)[0]
|
||||
|
||||
|
||||
class MinecraftLocation(Location):
|
||||
game = "Minecraft"
|
||||
|
||||
class MinecraftItem(Item):
|
||||
game = "Minecraft"
|
||||
|
||||
return item
|
||||
|
||||
def mc_update_output(raw_data, server, port):
|
||||
data = json.loads(b64decode(raw_data))
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"hard": [
|
||||
"Very Very Frightening",
|
||||
"A Furious Cocktail",
|
||||
"Two by Two",
|
||||
"Two Birds, One Arrow",
|
||||
"Arbalistic",
|
||||
"Monsters Hunted",
|
||||
"Beaconator",
|
||||
"A Balanced Diet",
|
||||
"Uneasy Alliance",
|
||||
"Cover Me in Debris",
|
||||
"A Complete Catalogue",
|
||||
"Surge Protector",
|
||||
"Sound of Music",
|
||||
"Star Trader",
|
||||
"When the Squad Hops into Town",
|
||||
"With Our Powers Combined!"
|
||||
],
|
||||
"unreasonable": [
|
||||
"How Did We Get Here?",
|
||||
"Adventuring Time"
|
||||
],
|
||||
"ender_dragon": [
|
||||
"Free the End",
|
||||
"The Next Generation",
|
||||
"The End... Again...",
|
||||
"You Need a Mint",
|
||||
"Monsters Hunted",
|
||||
"Is It a Plane?"
|
||||
],
|
||||
"wither": [
|
||||
"Withering Heights",
|
||||
"Bring Home the Beacon",
|
||||
"Beaconator",
|
||||
"A Furious Cocktail",
|
||||
"How Did We Get Here?",
|
||||
"Monsters Hunted"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
{
|
||||
"all_items": [
|
||||
"Archery",
|
||||
"Progressive Resource Crafting",
|
||||
"Resource Blocks",
|
||||
"Brewing",
|
||||
"Enchanting",
|
||||
"Bucket",
|
||||
"Flint and Steel",
|
||||
"Bed",
|
||||
"Bottles",
|
||||
"Shield",
|
||||
"Fishing Rod",
|
||||
"Campfire",
|
||||
"Progressive Weapons",
|
||||
"Progressive Tools",
|
||||
"Progressive Armor",
|
||||
"8 Netherite Scrap",
|
||||
"8 Emeralds",
|
||||
"4 Emeralds",
|
||||
"Channeling Book",
|
||||
"Silk Touch Book",
|
||||
"Sharpness III Book",
|
||||
"Piercing IV Book",
|
||||
"Looting III Book",
|
||||
"Infinity Book",
|
||||
"4 Diamond Ore",
|
||||
"16 Iron Ore",
|
||||
"500 XP",
|
||||
"100 XP",
|
||||
"50 XP",
|
||||
"3 Ender Pearls",
|
||||
"4 Lapis Lazuli",
|
||||
"16 Porkchops",
|
||||
"8 Gold Ore",
|
||||
"Rotten Flesh",
|
||||
"Single Arrow",
|
||||
"32 Arrows",
|
||||
"Saddle",
|
||||
"Structure Compass (Village)",
|
||||
"Structure Compass (Pillager Outpost)",
|
||||
"Structure Compass (Nether Fortress)",
|
||||
"Structure Compass (Bastion Remnant)",
|
||||
"Structure Compass (End City)",
|
||||
"Shulker Box",
|
||||
"Dragon Egg Shard",
|
||||
"Spyglass",
|
||||
"Lead",
|
||||
"Bee Trap"
|
||||
],
|
||||
"progression_items": [
|
||||
"Archery",
|
||||
"Progressive Resource Crafting",
|
||||
"Resource Blocks",
|
||||
"Brewing",
|
||||
"Enchanting",
|
||||
"Bucket",
|
||||
"Flint and Steel",
|
||||
"Bed",
|
||||
"Bottles",
|
||||
"Shield",
|
||||
"Fishing Rod",
|
||||
"Campfire",
|
||||
"Progressive Weapons",
|
||||
"Progressive Tools",
|
||||
"Progressive Armor",
|
||||
"8 Netherite Scrap",
|
||||
"Channeling Book",
|
||||
"Silk Touch Book",
|
||||
"Piercing IV Book",
|
||||
"3 Ender Pearls",
|
||||
"Saddle",
|
||||
"Structure Compass (Village)",
|
||||
"Structure Compass (Pillager Outpost)",
|
||||
"Structure Compass (Nether Fortress)",
|
||||
"Structure Compass (Bastion Remnant)",
|
||||
"Structure Compass (End City)",
|
||||
"Dragon Egg Shard",
|
||||
"Spyglass",
|
||||
"Lead"
|
||||
],
|
||||
"useful_items": [
|
||||
"Sharpness III Book",
|
||||
"Looting III Book",
|
||||
"Infinity Book"
|
||||
],
|
||||
"trap_items": [
|
||||
"Bee Trap"
|
||||
],
|
||||
|
||||
"required_pool": {
|
||||
"Archery": 1,
|
||||
"Progressive Resource Crafting": 2,
|
||||
"Brewing": 1,
|
||||
"Enchanting": 1,
|
||||
"Bucket": 1,
|
||||
"Flint and Steel": 1,
|
||||
"Bed": 1,
|
||||
"Bottles": 1,
|
||||
"Shield": 1,
|
||||
"Fishing Rod": 1,
|
||||
"Campfire": 1,
|
||||
"Progressive Weapons": 3,
|
||||
"Progressive Tools": 3,
|
||||
"Progressive Armor": 2,
|
||||
"8 Netherite Scrap": 2,
|
||||
"Channeling Book": 1,
|
||||
"Silk Touch Book": 1,
|
||||
"Sharpness III Book": 1,
|
||||
"Piercing IV Book": 1,
|
||||
"Looting III Book": 1,
|
||||
"Infinity Book": 1,
|
||||
"3 Ender Pearls": 4,
|
||||
"Saddle": 1,
|
||||
"Spyglass": 1,
|
||||
"Lead": 1
|
||||
},
|
||||
"junk_weights": {
|
||||
"4 Emeralds": 2,
|
||||
"4 Diamond Ore": 1,
|
||||
"16 Iron Ore": 1,
|
||||
"50 XP": 4,
|
||||
"16 Porkchops": 2,
|
||||
"8 Gold Ore": 1,
|
||||
"Rotten Flesh": 1,
|
||||
"32 Arrows": 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
{
|
||||
"all_locations": [
|
||||
"Who is Cutting Onions?",
|
||||
"Oh Shiny",
|
||||
"Suit Up",
|
||||
"Very Very Frightening",
|
||||
"Hot Stuff",
|
||||
"Free the End",
|
||||
"A Furious Cocktail",
|
||||
"Best Friends Forever",
|
||||
"Bring Home the Beacon",
|
||||
"Not Today, Thank You",
|
||||
"Isn't It Iron Pick",
|
||||
"Local Brewery",
|
||||
"The Next Generation",
|
||||
"Fishy Business",
|
||||
"Hot Tourist Destinations",
|
||||
"This Boat Has Legs",
|
||||
"Sniper Duel",
|
||||
"Nether",
|
||||
"Great View From Up Here",
|
||||
"How Did We Get Here?",
|
||||
"Bullseye",
|
||||
"Spooky Scary Skeleton",
|
||||
"Two by Two",
|
||||
"Stone Age",
|
||||
"Two Birds, One Arrow",
|
||||
"We Need to Go Deeper",
|
||||
"Who's the Pillager Now?",
|
||||
"Getting an Upgrade",
|
||||
"Tactical Fishing",
|
||||
"Zombie Doctor",
|
||||
"The City at the End of the Game",
|
||||
"Ice Bucket Challenge",
|
||||
"Remote Getaway",
|
||||
"Into Fire",
|
||||
"War Pigs",
|
||||
"Take Aim",
|
||||
"Total Beelocation",
|
||||
"Arbalistic",
|
||||
"The End... Again...",
|
||||
"Acquire Hardware",
|
||||
"Not Quite \"Nine\" Lives",
|
||||
"Cover Me With Diamonds",
|
||||
"Sky's the Limit",
|
||||
"Hired Help",
|
||||
"Return to Sender",
|
||||
"Sweet Dreams",
|
||||
"You Need a Mint",
|
||||
"Adventure",
|
||||
"Monsters Hunted",
|
||||
"Enchanter",
|
||||
"Voluntary Exile",
|
||||
"Eye Spy",
|
||||
"The End",
|
||||
"Serious Dedication",
|
||||
"Postmortal",
|
||||
"Monster Hunter",
|
||||
"Adventuring Time",
|
||||
"A Seedy Place",
|
||||
"Those Were the Days",
|
||||
"Hero of the Village",
|
||||
"Hidden in the Depths",
|
||||
"Beaconator",
|
||||
"Withering Heights",
|
||||
"A Balanced Diet",
|
||||
"Subspace Bubble",
|
||||
"Husbandry",
|
||||
"Country Lode, Take Me Home",
|
||||
"Bee Our Guest",
|
||||
"What a Deal!",
|
||||
"Uneasy Alliance",
|
||||
"Diamonds!",
|
||||
"A Terrible Fortress",
|
||||
"A Throwaway Joke",
|
||||
"Minecraft",
|
||||
"Sticky Situation",
|
||||
"Ol' Betsy",
|
||||
"Cover Me in Debris",
|
||||
"The End?",
|
||||
"The Parrots and the Bats",
|
||||
"A Complete Catalogue",
|
||||
"Getting Wood",
|
||||
"Time to Mine!",
|
||||
"Hot Topic",
|
||||
"Bake Bread",
|
||||
"The Lie",
|
||||
"On a Rail",
|
||||
"Time to Strike!",
|
||||
"Cow Tipper",
|
||||
"When Pigs Fly",
|
||||
"Overkill",
|
||||
"Librarian",
|
||||
"Overpowered",
|
||||
"Wax On",
|
||||
"Wax Off",
|
||||
"The Cutest Predator",
|
||||
"The Healing Power of Friendship",
|
||||
"Is It a Bird?",
|
||||
"Is It a Balloon?",
|
||||
"Is It a Plane?",
|
||||
"Surge Protector",
|
||||
"Light as a Rabbit",
|
||||
"Glow and Behold!",
|
||||
"Whatever Floats Your Goat!",
|
||||
"Caves & Cliffs",
|
||||
"Feels like home",
|
||||
"Sound of Music",
|
||||
"Star Trader",
|
||||
"Birthday Song",
|
||||
"Bukkit Bukkit",
|
||||
"It Spreads",
|
||||
"Sneak 100",
|
||||
"When the Squad Hops into Town",
|
||||
"With Our Powers Combined!",
|
||||
"You've Got a Friend in Me"
|
||||
],
|
||||
"locations_by_region": {
|
||||
"Overworld": [
|
||||
"Who is Cutting Onions?",
|
||||
"Oh Shiny",
|
||||
"Suit Up",
|
||||
"Very Very Frightening",
|
||||
"Hot Stuff",
|
||||
"Best Friends Forever",
|
||||
"Not Today, Thank You",
|
||||
"Isn't It Iron Pick",
|
||||
"Fishy Business",
|
||||
"Sniper Duel",
|
||||
"Bullseye",
|
||||
"Stone Age",
|
||||
"Two Birds, One Arrow",
|
||||
"Getting an Upgrade",
|
||||
"Tactical Fishing",
|
||||
"Zombie Doctor",
|
||||
"Ice Bucket Challenge",
|
||||
"Take Aim",
|
||||
"Total Beelocation",
|
||||
"Arbalistic",
|
||||
"Acquire Hardware",
|
||||
"Cover Me With Diamonds",
|
||||
"Hired Help",
|
||||
"Sweet Dreams",
|
||||
"Adventure",
|
||||
"Monsters Hunted",
|
||||
"Enchanter",
|
||||
"Eye Spy",
|
||||
"Monster Hunter",
|
||||
"Adventuring Time",
|
||||
"A Seedy Place",
|
||||
"Husbandry",
|
||||
"Bee Our Guest",
|
||||
"Diamonds!",
|
||||
"A Throwaway Joke",
|
||||
"Minecraft",
|
||||
"Sticky Situation",
|
||||
"Ol' Betsy",
|
||||
"The Parrots and the Bats",
|
||||
"Getting Wood",
|
||||
"Time to Mine!",
|
||||
"Hot Topic",
|
||||
"Bake Bread",
|
||||
"The Lie",
|
||||
"On a Rail",
|
||||
"Time to Strike!",
|
||||
"Cow Tipper",
|
||||
"When Pigs Fly",
|
||||
"Librarian",
|
||||
"Wax On",
|
||||
"Wax Off",
|
||||
"The Cutest Predator",
|
||||
"The Healing Power of Friendship",
|
||||
"Is It a Bird?",
|
||||
"Surge Protector",
|
||||
"Light as a Rabbit",
|
||||
"Glow and Behold!",
|
||||
"Whatever Floats Your Goat!",
|
||||
"Caves & Cliffs",
|
||||
"Sound of Music",
|
||||
"Bukkit Bukkit",
|
||||
"It Spreads",
|
||||
"Sneak 100",
|
||||
"When the Squad Hops into Town"
|
||||
],
|
||||
"The Nether": [
|
||||
"Hot Tourist Destinations",
|
||||
"This Boat Has Legs",
|
||||
"Nether",
|
||||
"Two by Two",
|
||||
"We Need to Go Deeper",
|
||||
"Not Quite \"Nine\" Lives",
|
||||
"Return to Sender",
|
||||
"Serious Dedication",
|
||||
"Hidden in the Depths",
|
||||
"Subspace Bubble",
|
||||
"Country Lode, Take Me Home",
|
||||
"Uneasy Alliance",
|
||||
"Cover Me in Debris",
|
||||
"Is It a Balloon?",
|
||||
"Feels like home",
|
||||
"With Our Powers Combined!"
|
||||
],
|
||||
"The End": [
|
||||
"Free the End",
|
||||
"The Next Generation",
|
||||
"Remote Getaway",
|
||||
"The End... Again...",
|
||||
"You Need a Mint",
|
||||
"The End",
|
||||
"The End?",
|
||||
"Is It a Plane?"
|
||||
],
|
||||
"Village": [
|
||||
"Postmortal",
|
||||
"Hero of the Village",
|
||||
"A Balanced Diet",
|
||||
"What a Deal!",
|
||||
"A Complete Catalogue",
|
||||
"Star Trader"
|
||||
],
|
||||
"Nether Fortress": [
|
||||
"A Furious Cocktail",
|
||||
"Bring Home the Beacon",
|
||||
"Local Brewery",
|
||||
"How Did We Get Here?",
|
||||
"Spooky Scary Skeleton",
|
||||
"Into Fire",
|
||||
"Beaconator",
|
||||
"Withering Heights",
|
||||
"A Terrible Fortress",
|
||||
"Overkill"
|
||||
],
|
||||
"Pillager Outpost": [
|
||||
"Who's the Pillager Now?",
|
||||
"Voluntary Exile",
|
||||
"Birthday Song",
|
||||
"You've Got a Friend in Me"
|
||||
],
|
||||
"Bastion Remnant": [
|
||||
"War Pigs",
|
||||
"Those Were the Days",
|
||||
"Overpowered"
|
||||
],
|
||||
"End City": [
|
||||
"Great View From Up Here",
|
||||
"The City at the End of the Game",
|
||||
"Sky's the Limit"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"regions": [
|
||||
["Menu", ["New World"]],
|
||||
["Overworld", ["Nether Portal", "End Portal", "Overworld Structure 1", "Overworld Structure 2"]],
|
||||
["The Nether", ["Nether Structure 1", "Nether Structure 2"]],
|
||||
["The End", ["The End Structure"]],
|
||||
["Village", []],
|
||||
["Pillager Outpost", []],
|
||||
["Nether Fortress", []],
|
||||
["Bastion Remnant", []],
|
||||
["End City", []]
|
||||
],
|
||||
"mandatory_connections": [
|
||||
["New World", "Overworld"],
|
||||
["Nether Portal", "The Nether"],
|
||||
["End Portal", "The End"]
|
||||
],
|
||||
"default_connections": [
|
||||
["Overworld Structure 1", "Village"],
|
||||
["Overworld Structure 2", "Pillager Outpost"],
|
||||
["Nether Structure 1", "Nether Fortress"],
|
||||
["Nether Structure 2", "Bastion Remnant"],
|
||||
["The End Structure", "End City"]
|
||||
],
|
||||
"illegal_connections": {
|
||||
"Nether Fortress": ["The End Structure"]
|
||||
}
|
||||
}
|
|
@ -1,10 +1,14 @@
|
|||
from .TestMinecraft import TestMinecraft
|
||||
from . import MCTestBase
|
||||
|
||||
|
||||
# Format:
|
||||
# [location, expected_result, given_items, [excluded_items]]
|
||||
# Every advancement has its own test, named by its internal ID number.
|
||||
class TestAdvancements(TestMinecraft):
|
||||
class TestAdvancements(MCTestBase):
|
||||
options = {
|
||||
"shuffle_structures": False,
|
||||
"structure_compasses": False
|
||||
}
|
||||
|
||||
def test_42000(self):
|
||||
self.run_location_tests([
|
||||
|
@ -1278,3 +1282,129 @@ class TestAdvancements(TestMinecraft):
|
|||
["Whatever Floats Your Goat!", True, ["Progressive Weapons", "Progressive Resource Crafting"]],
|
||||
["Whatever Floats Your Goat!", True, ["Progressive Weapons", "Campfire"]],
|
||||
])
|
||||
|
||||
# bucket, iron pick
|
||||
def test_42103(self):
|
||||
self.run_location_tests([
|
||||
["Caves & Cliffs", False, []],
|
||||
["Caves & Cliffs", False, [], ["Bucket"]],
|
||||
["Caves & Cliffs", False, [], ["Progressive Tools"]],
|
||||
["Caves & Cliffs", False, [], ["Progressive Resource Crafting"]],
|
||||
["Caves & Cliffs", True, ["Progressive Resource Crafting", "Progressive Tools", "Progressive Tools", "Bucket"]],
|
||||
])
|
||||
|
||||
# bucket, fishing rod, saddle, combat
|
||||
def test_42104(self):
|
||||
self.run_location_tests([
|
||||
["Feels like home", False, []],
|
||||
["Feels like home", False, [], ['Progressive Resource Crafting']],
|
||||
["Feels like home", False, [], ['Progressive Tools']],
|
||||
["Feels like home", False, [], ['Progressive Weapons']],
|
||||
["Feels like home", False, [], ['Progressive Armor', 'Shield']],
|
||||
["Feels like home", False, [], ['Fishing Rod']],
|
||||
["Feels like home", False, [], ['Saddle']],
|
||||
["Feels like home", False, [], ['Bucket']],
|
||||
["Feels like home", False, [], ['Flint and Steel']],
|
||||
["Feels like home", True, ['Saddle', 'Progressive Resource Crafting', 'Progressive Tools', 'Flint and Steel', 'Bucket', 'Progressive Weapons', 'Progressive Armor', 'Fishing Rod']],
|
||||
["Feels like home", True, ['Saddle', 'Progressive Resource Crafting', 'Progressive Tools', 'Flint and Steel', 'Bucket', 'Progressive Weapons', 'Shield', 'Fishing Rod']],
|
||||
])
|
||||
|
||||
# iron pick, combat
|
||||
def test_42105(self):
|
||||
self.run_location_tests([
|
||||
["Sound of Music", False, []],
|
||||
["Sound of Music", False, [], ["Progressive Tools"]],
|
||||
["Sound of Music", False, [], ["Progressive Resource Crafting"]],
|
||||
["Sound of Music", False, [], ["Progressive Weapons"]],
|
||||
["Sound of Music", False, [], ["Progressive Armor", "Shield"]],
|
||||
["Sound of Music", True, ["Progressive Tools", "Progressive Tools", "Progressive Resource Crafting", "Progressive Weapons", "Progressive Armor"]],
|
||||
["Sound of Music", True, ["Progressive Tools", "Progressive Tools", "Progressive Resource Crafting", "Progressive Weapons", "Shield"]],
|
||||
])
|
||||
|
||||
# bucket, nether, villager
|
||||
def test_42106(self):
|
||||
self.run_location_tests([
|
||||
["Star Trader", False, []],
|
||||
["Star Trader", False, [], ["Bucket"]],
|
||||
["Star Trader", False, [], ["Flint and Steel"]],
|
||||
["Star Trader", False, [], ["Progressive Tools"]],
|
||||
["Star Trader", False, [], ["Progressive Resource Crafting"]],
|
||||
["Star Trader", False, [], ["Progressive Weapons"]],
|
||||
["Star Trader", True, ["Bucket", "Flint and Steel", "Progressive Tools", "Progressive Resource Crafting", "Progressive Weapons"]],
|
||||
])
|
||||
|
||||
# bucket, redstone -> iron pick, pillager outpost -> adventure
|
||||
def test_42107(self):
|
||||
self.run_location_tests([
|
||||
["Birthday Song", False, []],
|
||||
["Birthday Song", False, [], ["Bucket"]],
|
||||
["Birthday Song", False, [], ["Progressive Tools"]],
|
||||
["Birthday Song", False, [], ["Progressive Weapons"]],
|
||||
["Birthday Song", False, [], ["Progressive Resource Crafting"]],
|
||||
["Birthday Song", True, ["Progressive Resource Crafting", "Progressive Tools", "Progressive Tools", "Progressive Weapons", "Bucket"]],
|
||||
])
|
||||
|
||||
# bucket, adventure
|
||||
def test_42108(self):
|
||||
self.run_location_tests([
|
||||
["Bukkit Bukkit", False, []],
|
||||
["Bukkit Bukkit", False, [], ["Bucket"]],
|
||||
["Bukkit Bukkit", False, [], ["Progressive Tools"]],
|
||||
["Bukkit Bukkit", False, [], ["Progressive Weapons"]],
|
||||
["Bukkit Bukkit", False, [], ["Progressive Resource Crafting"]],
|
||||
["Bukkit Bukkit", True, ["Bucket", "Progressive Tools", "Progressive Weapons", "Progressive Resource Crafting"]],
|
||||
])
|
||||
|
||||
# iron pick, adventure
|
||||
def test_42109(self):
|
||||
self.run_location_tests([
|
||||
["It Spreads", False, []],
|
||||
["It Spreads", False, [], ["Progressive Tools"]],
|
||||
["It Spreads", False, [], ["Progressive Weapons"]],
|
||||
["It Spreads", False, [], ["Progressive Resource Crafting"]],
|
||||
["It Spreads", True, ["Progressive Tools", "Progressive Tools", "Progressive Weapons", "Progressive Resource Crafting"]],
|
||||
])
|
||||
|
||||
# iron pick, adventure
|
||||
def test_42110(self):
|
||||
self.run_location_tests([
|
||||
["Sneak 100", False, []],
|
||||
["Sneak 100", False, [], ["Progressive Tools"]],
|
||||
["Sneak 100", False, [], ["Progressive Weapons"]],
|
||||
["Sneak 100", False, [], ["Progressive Resource Crafting"]],
|
||||
["Sneak 100", True, ["Progressive Tools", "Progressive Tools", "Progressive Weapons", "Progressive Resource Crafting"]],
|
||||
])
|
||||
|
||||
# adventure, lead
|
||||
def test_42111(self):
|
||||
self.run_location_tests([
|
||||
["When the Squad Hops into Town", False, []],
|
||||
["When the Squad Hops into Town", False, [], ["Progressive Weapons"]],
|
||||
["When the Squad Hops into Town", False, [], ["Campfire", "Progressive Resource Crafting"]],
|
||||
["When the Squad Hops into Town", False, [], ["Lead"]],
|
||||
["When the Squad Hops into Town", True, ["Progressive Weapons", "Lead", "Campfire"]],
|
||||
["When the Squad Hops into Town", True, ["Progressive Weapons", "Lead", "Progressive Resource Crafting"]],
|
||||
])
|
||||
|
||||
# adventure, lead, nether
|
||||
def test_42112(self):
|
||||
self.run_location_tests([
|
||||
["With Our Powers Combined!", False, []],
|
||||
["With Our Powers Combined!", False, [], ["Lead"]],
|
||||
["With Our Powers Combined!", False, [], ["Bucket", "Progressive Tools"]],
|
||||
["With Our Powers Combined!", False, [], ["Flint and Steel"]],
|
||||
["With Our Powers Combined!", False, [], ["Progressive Weapons"]],
|
||||
["With Our Powers Combined!", False, [], ["Progressive Resource Crafting"]],
|
||||
["With Our Powers Combined!", True, ["Lead", "Progressive Weapons", "Progressive Resource Crafting", "Flint and Steel", "Progressive Tools", "Bucket"]],
|
||||
["With Our Powers Combined!", True, ["Lead", "Progressive Weapons", "Progressive Resource Crafting", "Flint and Steel", "Progressive Tools", "Progressive Tools", "Progressive Tools"]],
|
||||
])
|
||||
|
||||
# pillager outpost -> adventure
|
||||
def test_42113(self):
|
||||
self.run_location_tests([
|
||||
["You've Got a Friend in Me", False, []],
|
||||
["You've Got a Friend in Me", False, [], ["Progressive Weapons"]],
|
||||
["You've Got a Friend in Me", False, [], ["Campfire", "Progressive Resource Crafting"]],
|
||||
["You've Got a Friend in Me", True, ["Progressive Weapons", "Campfire"]],
|
||||
["You've Got a Friend in Me", True, ["Progressive Weapons", "Progressive Resource Crafting"]],
|
||||
])
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import unittest
|
||||
|
||||
from .. import Constants
|
||||
|
||||
class TestDataLoad(unittest.TestCase):
|
||||
|
||||
def test_item_data(self):
|
||||
item_info = Constants.item_info
|
||||
|
||||
# All items in sub-tables are in all_items
|
||||
all_items: set = set(item_info['all_items'])
|
||||
assert set(item_info['progression_items']) <= all_items
|
||||
assert set(item_info['useful_items']) <= all_items
|
||||
assert set(item_info['trap_items']) <= all_items
|
||||
assert set(item_info['required_pool'].keys()) <= all_items
|
||||
assert set(item_info['junk_weights'].keys()) <= all_items
|
||||
|
||||
# No overlapping ids (because of bee trap stuff)
|
||||
all_ids: set = set(Constants.item_name_to_id.values())
|
||||
assert len(all_items) == len(all_ids)
|
||||
|
||||
def test_location_data(self):
|
||||
location_info = Constants.location_info
|
||||
exclusion_info = Constants.exclusion_info
|
||||
|
||||
# Every location has a region and every region's locations are in all_locations
|
||||
all_locations: set = set(location_info['all_locations'])
|
||||
all_locs_2: set = set()
|
||||
for v in location_info['locations_by_region'].values():
|
||||
all_locs_2.update(v)
|
||||
assert all_locations == all_locs_2
|
||||
|
||||
# All exclusions are locations
|
||||
for v in exclusion_info.values():
|
||||
assert set(v) <= all_locations
|
||||
|
||||
def test_region_data(self):
|
||||
region_info = Constants.region_info
|
||||
|
||||
# Every entrance and region in mandatory/default/illegal connections is a real entrance and region
|
||||
all_regions = set()
|
||||
all_entrances = set()
|
||||
for v in region_info['regions']:
|
||||
assert isinstance(v[0], str)
|
||||
assert isinstance(v[1], list)
|
||||
all_regions.add(v[0])
|
||||
all_entrances.update(v[1])
|
||||
|
||||
for v in region_info['mandatory_connections']:
|
||||
assert v[0] in all_entrances
|
||||
assert v[1] in all_regions
|
||||
|
||||
for v in region_info['default_connections']:
|
||||
assert v[0] in all_entrances
|
||||
assert v[1] in all_regions
|
||||
|
||||
for k, v in region_info['illegal_connections'].items():
|
||||
assert k in all_regions
|
||||
assert set(v) <= all_entrances
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
from .TestMinecraft import TestMinecraft
|
||||
from . import MCTestBase
|
||||
|
||||
|
||||
class TestEntrances(TestMinecraft):
|
||||
class TestEntrances(MCTestBase):
|
||||
options = {
|
||||
"shuffle_structures": False,
|
||||
"structure_compasses": False
|
||||
}
|
||||
|
||||
def testPortals(self):
|
||||
self.run_entrance_tests([
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
from test.TestBase import TestBase
|
||||
from BaseClasses import MultiWorld, ItemClassification
|
||||
from worlds import AutoWorld
|
||||
from worlds.minecraft import MinecraftWorld
|
||||
from worlds.minecraft.Items import MinecraftItem, item_table
|
||||
from Options import Toggle
|
||||
from worlds.minecraft.Options import AdvancementGoal, EggShardsRequired, EggShardsAvailable, BossGoal, BeeTraps, \
|
||||
ShuffleStructures, CombatDifficulty
|
||||
|
||||
|
||||
# Converts the name of an item into an item object
|
||||
def MCItemFactory(items, player: int):
|
||||
ret = []
|
||||
singleton = False
|
||||
if isinstance(items, str):
|
||||
items = [items]
|
||||
singleton = True
|
||||
for item in items:
|
||||
if item in item_table:
|
||||
ret.append(MinecraftItem(
|
||||
item, ItemClassification.progression if item_table[item].progression else ItemClassification.filler,
|
||||
item_table[item].code, player
|
||||
))
|
||||
else:
|
||||
raise Exception(f"Unknown item {item}")
|
||||
|
||||
if singleton:
|
||||
return ret[0]
|
||||
return ret
|
||||
|
||||
|
||||
class TestMinecraft(TestBase):
|
||||
|
||||
def setUp(self):
|
||||
self.multiworld = MultiWorld(1)
|
||||
self.multiworld.game[1] = "Minecraft"
|
||||
self.multiworld.worlds[1] = MinecraftWorld(self.multiworld, 1)
|
||||
exclusion_pools = ['hard', 'unreasonable', 'postgame']
|
||||
for pool in exclusion_pools:
|
||||
setattr(self.multiworld, f"include_{pool}_advancements", {1: False})
|
||||
setattr(self.multiworld, "advancement_goal", {1: AdvancementGoal(30)})
|
||||
setattr(self.multiworld, "egg_shards_required", {1: EggShardsRequired(0)})
|
||||
setattr(self.multiworld, "egg_shards_available", {1: EggShardsAvailable(0)})
|
||||
setattr(self.multiworld, "required_bosses", {1: BossGoal(1)}) # ender dragon
|
||||
setattr(self.multiworld, "shuffle_structures", {1: ShuffleStructures(False)})
|
||||
setattr(self.multiworld, "bee_traps", {1: BeeTraps(0)})
|
||||
setattr(self.multiworld, "combat_difficulty", {1: CombatDifficulty(1)}) # normal
|
||||
setattr(self.multiworld, "structure_compasses", {1: Toggle(False)})
|
||||
setattr(self.multiworld, "death_link", {1: Toggle(False)})
|
||||
AutoWorld.call_single(self.multiworld, "create_regions", 1)
|
||||
AutoWorld.call_single(self.multiworld, "generate_basic", 1)
|
||||
AutoWorld.call_single(self.multiworld, "set_rules", 1)
|
||||
|
||||
def _get_items(self, item_pool, all_except):
|
||||
if all_except and len(all_except) > 0:
|
||||
items = self.multiworld.itempool[:]
|
||||
items = [item for item in items if
|
||||
item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)]
|
||||
items.extend(MCItemFactory(item_pool[0], 1))
|
||||
else:
|
||||
items = MCItemFactory(item_pool[0], 1)
|
||||
return self.get_state(items)
|
||||
|
||||
def _get_items_partial(self, item_pool, missing_item):
|
||||
new_items = item_pool[0].copy()
|
||||
new_items.remove(missing_item)
|
||||
items = MCItemFactory(new_items, 1)
|
||||
return self.get_state(items)
|
|
@ -0,0 +1,49 @@
|
|||
from . import MCTestBase
|
||||
from ..Constants import region_info
|
||||
from ..Options import minecraft_options
|
||||
|
||||
from BaseClasses import ItemClassification
|
||||
|
||||
class AdvancementTestBase(MCTestBase):
|
||||
options = {
|
||||
"advancement_goal": minecraft_options["advancement_goal"].range_end
|
||||
}
|
||||
# beatability test implicit
|
||||
|
||||
class ShardTestBase(MCTestBase):
|
||||
options = {
|
||||
"egg_shards_required": minecraft_options["egg_shards_required"].range_end,
|
||||
"egg_shards_available": minecraft_options["egg_shards_available"].range_end
|
||||
}
|
||||
|
||||
# check that itempool is not overfilled with shards
|
||||
def test_itempool(self):
|
||||
assert len(self.multiworld.get_unfilled_locations()) == len(self.multiworld.itempool)
|
||||
|
||||
class CompassTestBase(MCTestBase):
|
||||
def test_compasses_in_pool(self):
|
||||
structures = [x[1] for x in region_info["default_connections"]]
|
||||
itempool_str = {item.name for item in self.multiworld.itempool}
|
||||
for struct in structures:
|
||||
assert f"Structure Compass ({struct})" in itempool_str
|
||||
|
||||
class NoBeeTestBase(MCTestBase):
|
||||
options = {
|
||||
"bee_traps": 0
|
||||
}
|
||||
|
||||
# With no bees, there are no traps in the pool
|
||||
def test_bees(self):
|
||||
for item in self.multiworld.itempool:
|
||||
assert item.classification != ItemClassification.trap
|
||||
|
||||
|
||||
class AllBeeTestBase(MCTestBase):
|
||||
options = {
|
||||
"bee_traps": 100
|
||||
}
|
||||
|
||||
# With max bees, there are no filler items, only bee traps
|
||||
def test_bees(self):
|
||||
for item in self.multiworld.itempool:
|
||||
assert item.classification != ItemClassification.filler
|
|
@ -0,0 +1,33 @@
|
|||
from test.TestBase import TestBase, WorldTestBase
|
||||
from .. import MinecraftWorld
|
||||
|
||||
|
||||
class MCTestBase(WorldTestBase, TestBase):
|
||||
game = "Minecraft"
|
||||
player: int = 1
|
||||
|
||||
def _create_items(self, items, player):
|
||||
singleton = False
|
||||
if isinstance(items, str):
|
||||
items = [items]
|
||||
singleton = True
|
||||
ret = [self.multiworld.worlds[player].create_item(item) for item in items]
|
||||
if singleton:
|
||||
return ret[0]
|
||||
return ret
|
||||
|
||||
def _get_items(self, item_pool, all_except):
|
||||
if all_except and len(all_except) > 0:
|
||||
items = self.multiworld.itempool[:]
|
||||
items = [item for item in items if item.name not in all_except]
|
||||
items.extend(self._create_items(item_pool[0], 1))
|
||||
else:
|
||||
items = self._create_items(item_pool[0], 1)
|
||||
return self.get_state(items)
|
||||
|
||||
def _get_items_partial(self, item_pool, missing_item):
|
||||
new_items = item_pool[0].copy()
|
||||
new_items.remove(missing_item)
|
||||
items = self._create_items(new_items, 1)
|
||||
return self.get_state(items)
|
||||
|
Loading…
Reference in New Issue