mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 04:01:32 -06:00
Remove Minecraft (#4672)
* Remove Minecraft * remove minecraft * remove minecraft * elif -> if --------- Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -56,7 +56,6 @@ success.txt
|
||||
output/
|
||||
Output Logs/
|
||||
/factorio/
|
||||
/Minecraft Forge Server/
|
||||
/WebHostLib/static/generated
|
||||
/freeze_requirements.txt
|
||||
/Archipelago.zip
|
||||
@@ -184,12 +183,6 @@ _speedups.c
|
||||
_speedups.cpp
|
||||
_speedups.html
|
||||
|
||||
# minecraft server stuff
|
||||
jdk*/
|
||||
minecraft*/
|
||||
minecraft_versions.json
|
||||
!worlds/minecraft/
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
|
@@ -1,347 +0,0 @@
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import atexit
|
||||
import shutil
|
||||
from subprocess import Popen
|
||||
from shutil import copyfile
|
||||
from time import strftime
|
||||
import logging
|
||||
|
||||
import requests
|
||||
|
||||
import Utils
|
||||
from Utils import is_windows
|
||||
from settings import get_settings
|
||||
|
||||
atexit.register(input, "Press enter to exit.")
|
||||
|
||||
# 1 or more digits followed by m or g, then optional b
|
||||
max_heap_re = re.compile(r"^\d+[mMgG][bB]?$")
|
||||
|
||||
|
||||
def prompt_yes_no(prompt):
|
||||
yes_inputs = {'yes', 'ye', 'y'}
|
||||
no_inputs = {'no', 'n'}
|
||||
while True:
|
||||
choice = input(prompt + " [y/n] ").lower()
|
||||
if choice in yes_inputs:
|
||||
return True
|
||||
elif choice in no_inputs:
|
||||
return False
|
||||
else:
|
||||
print('Please respond with "y" or "n".')
|
||||
|
||||
|
||||
def find_ap_randomizer_jar(forge_dir):
|
||||
"""Create mods folder if needed; find AP randomizer jar; return None if not found."""
|
||||
mods_dir = os.path.join(forge_dir, 'mods')
|
||||
if os.path.isdir(mods_dir):
|
||||
for entry in os.scandir(mods_dir):
|
||||
if entry.name.startswith("aprandomizer") and entry.name.endswith(".jar"):
|
||||
logging.info(f"Found AP randomizer mod: {entry.name}")
|
||||
return entry.name
|
||||
return None
|
||||
else:
|
||||
os.mkdir(mods_dir)
|
||||
logging.info(f"Created mods folder in {forge_dir}")
|
||||
return None
|
||||
|
||||
|
||||
def replace_apmc_files(forge_dir, apmc_file):
|
||||
"""Create APData folder if needed; clean .apmc files from APData; copy given .apmc into directory."""
|
||||
if apmc_file is None:
|
||||
return
|
||||
apdata_dir = os.path.join(forge_dir, 'APData')
|
||||
copy_apmc = True
|
||||
if not os.path.isdir(apdata_dir):
|
||||
os.mkdir(apdata_dir)
|
||||
logging.info(f"Created APData folder in {forge_dir}")
|
||||
for entry in os.scandir(apdata_dir):
|
||||
if entry.name.endswith(".apmc") and entry.is_file():
|
||||
if not os.path.samefile(apmc_file, entry.path):
|
||||
os.remove(entry.path)
|
||||
logging.info(f"Removed {entry.name} in {apdata_dir}")
|
||||
else: # apmc already in apdata
|
||||
copy_apmc = False
|
||||
if copy_apmc:
|
||||
copyfile(apmc_file, os.path.join(apdata_dir, os.path.basename(apmc_file)))
|
||||
logging.info(f"Copied {os.path.basename(apmc_file)} to {apdata_dir}")
|
||||
|
||||
|
||||
def read_apmc_file(apmc_file):
|
||||
from base64 import b64decode
|
||||
|
||||
with open(apmc_file, 'r') as f:
|
||||
return json.loads(b64decode(f.read()))
|
||||
|
||||
|
||||
def update_mod(forge_dir, url: str):
|
||||
"""Check mod version, download new mod from GitHub releases page if needed. """
|
||||
ap_randomizer = find_ap_randomizer_jar(forge_dir)
|
||||
os.path.basename(url)
|
||||
if ap_randomizer is not None:
|
||||
logging.info(f"Your current mod is {ap_randomizer}.")
|
||||
else:
|
||||
logging.info(f"You do not have the AP randomizer mod installed.")
|
||||
|
||||
if ap_randomizer != os.path.basename(url):
|
||||
logging.info(f"A new release of the Minecraft AP randomizer mod was found: "
|
||||
f"{os.path.basename(url)}")
|
||||
if prompt_yes_no("Would you like to update?"):
|
||||
old_ap_mod = os.path.join(forge_dir, 'mods', ap_randomizer) if ap_randomizer is not None else None
|
||||
new_ap_mod = os.path.join(forge_dir, 'mods', os.path.basename(url))
|
||||
logging.info("Downloading AP randomizer mod. This may take a moment...")
|
||||
apmod_resp = requests.get(url)
|
||||
if apmod_resp.status_code == 200:
|
||||
with open(new_ap_mod, 'wb') as f:
|
||||
f.write(apmod_resp.content)
|
||||
logging.info(f"Wrote new mod file to {new_ap_mod}")
|
||||
if old_ap_mod is not None:
|
||||
os.remove(old_ap_mod)
|
||||
logging.info(f"Removed old mod file from {old_ap_mod}")
|
||||
else:
|
||||
logging.error(f"Error retrieving the randomizer mod (status code {apmod_resp.status_code}).")
|
||||
logging.error(f"Please report this issue on the Archipelago Discord server.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def check_eula(forge_dir):
|
||||
"""Check if the EULA is agreed to, and prompt the user to read and agree if necessary."""
|
||||
eula_path = os.path.join(forge_dir, "eula.txt")
|
||||
if not os.path.isfile(eula_path):
|
||||
# Create eula.txt
|
||||
with open(eula_path, 'w') as f:
|
||||
f.write("#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/documents/minecraft_eula).\n")
|
||||
f.write(f"#{strftime('%a %b %d %X %Z %Y')}\n")
|
||||
f.write("eula=false\n")
|
||||
with open(eula_path, 'r+') as f:
|
||||
text = f.read()
|
||||
if 'false' in text:
|
||||
# Prompt user to agree to the EULA
|
||||
logging.info("You need to agree to the Minecraft EULA in order to run the server.")
|
||||
logging.info("The EULA can be found at https://account.mojang.com/documents/minecraft_eula")
|
||||
if prompt_yes_no("Do you agree to the EULA?"):
|
||||
f.seek(0)
|
||||
f.write(text.replace('false', 'true'))
|
||||
f.truncate()
|
||||
logging.info(f"Set {eula_path} to true")
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def find_jdk_dir(version: str) -> str:
|
||||
"""get the specified versions jdk directory"""
|
||||
for entry in os.listdir():
|
||||
if os.path.isdir(entry) and entry.startswith(f"jdk{version}"):
|
||||
return os.path.abspath(entry)
|
||||
|
||||
|
||||
def find_jdk(version: str) -> str:
|
||||
"""get the java exe location"""
|
||||
|
||||
if is_windows:
|
||||
jdk = find_jdk_dir(version)
|
||||
jdk_exe = os.path.join(jdk, "bin", "java.exe")
|
||||
if os.path.isfile(jdk_exe):
|
||||
return jdk_exe
|
||||
else:
|
||||
jdk_exe = shutil.which(options.java)
|
||||
if not jdk_exe:
|
||||
jdk_exe = shutil.which("java") # try to fall back to system java
|
||||
if not jdk_exe:
|
||||
raise Exception("Could not find Java. Is Java installed on the system?")
|
||||
return jdk_exe
|
||||
|
||||
|
||||
def download_java(java: str):
|
||||
"""Download Corretto (Amazon JDK)"""
|
||||
|
||||
jdk = find_jdk_dir(java)
|
||||
if jdk is not None:
|
||||
print(f"Removing old JDK...")
|
||||
from shutil import rmtree
|
||||
rmtree(jdk)
|
||||
|
||||
print(f"Downloading Java...")
|
||||
jdk_url = f"https://corretto.aws/downloads/latest/amazon-corretto-{java}-x64-windows-jdk.zip"
|
||||
resp = requests.get(jdk_url)
|
||||
if resp.status_code == 200: # OK
|
||||
print(f"Extracting...")
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
with zipfile.ZipFile(BytesIO(resp.content)) as zf:
|
||||
zf.extractall()
|
||||
else:
|
||||
print(f"Error downloading Java (status code {resp.status_code}).")
|
||||
print(f"If this was not expected, please report this issue on the Archipelago Discord server.")
|
||||
if not prompt_yes_no("Continue anyways?"):
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def install_forge(directory: str, forge_version: str, java_version: str):
|
||||
"""download and install forge"""
|
||||
|
||||
java_exe = find_jdk(java_version)
|
||||
if java_exe is not None:
|
||||
print(f"Downloading Forge {forge_version}...")
|
||||
forge_url = f"https://maven.minecraftforge.net/net/minecraftforge/forge/{forge_version}/forge-{forge_version}-installer.jar"
|
||||
resp = requests.get(forge_url)
|
||||
if resp.status_code == 200: # OK
|
||||
forge_install_jar = os.path.join(directory, "forge_install.jar")
|
||||
if not os.path.exists(directory):
|
||||
os.mkdir(directory)
|
||||
with open(forge_install_jar, 'wb') as f:
|
||||
f.write(resp.content)
|
||||
print(f"Installing Forge...")
|
||||
install_process = Popen([java_exe, "-jar", forge_install_jar, "--installServer", directory])
|
||||
install_process.wait()
|
||||
os.remove(forge_install_jar)
|
||||
|
||||
|
||||
def run_forge_server(forge_dir: str, java_version: str, heap_arg: str) -> Popen:
|
||||
"""Run the Forge server."""
|
||||
|
||||
java_exe = find_jdk(java_version)
|
||||
if not os.path.isfile(java_exe):
|
||||
java_exe = "java" # try to fall back on java in the PATH
|
||||
|
||||
heap_arg = max_heap_re.match(heap_arg).group()
|
||||
if heap_arg[-1] in ['b', 'B']:
|
||||
heap_arg = heap_arg[:-1]
|
||||
heap_arg = "-Xmx" + heap_arg
|
||||
|
||||
os_args = "win_args.txt" if is_windows else "unix_args.txt"
|
||||
args_file = os.path.join(forge_dir, "libraries", "net", "minecraftforge", "forge", forge_version, os_args)
|
||||
forge_args = []
|
||||
with open(args_file) as argfile:
|
||||
for line in argfile:
|
||||
forge_args.extend(line.strip().split(" "))
|
||||
|
||||
args = [java_exe, heap_arg, *forge_args, "-nogui"]
|
||||
logging.info(f"Running Forge server: {args}")
|
||||
os.chdir(forge_dir)
|
||||
return Popen(args)
|
||||
|
||||
|
||||
def get_minecraft_versions(version, release_channel="release"):
|
||||
version_file_endpoint = "https://raw.githubusercontent.com/KonoTyran/Minecraft_AP_Randomizer/master/versions/minecraft_versions.json"
|
||||
resp = requests.get(version_file_endpoint)
|
||||
local = False
|
||||
if resp.status_code == 200: # OK
|
||||
try:
|
||||
data = resp.json()
|
||||
except requests.exceptions.JSONDecodeError:
|
||||
logging.warning(f"Unable to fetch version update file, using local version. (status code {resp.status_code}).")
|
||||
local = True
|
||||
else:
|
||||
logging.warning(f"Unable to fetch version update file, using local version. (status code {resp.status_code}).")
|
||||
local = True
|
||||
|
||||
if local:
|
||||
with open(Utils.user_path("minecraft_versions.json"), 'r') as f:
|
||||
data = json.load(f)
|
||||
else:
|
||||
with open(Utils.user_path("minecraft_versions.json"), 'w') as f:
|
||||
json.dump(data, f)
|
||||
|
||||
try:
|
||||
if version:
|
||||
return next(filter(lambda entry: entry["version"] == version, data[release_channel]))
|
||||
else:
|
||||
return resp.json()[release_channel][0]
|
||||
except (StopIteration, KeyError):
|
||||
logging.error(f"No compatible mod version found for client version {version} on \"{release_channel}\" channel.")
|
||||
if release_channel != "release":
|
||||
logging.error("Consider switching \"release_channel\" to \"release\" in your Host.yaml file")
|
||||
else:
|
||||
logging.error("No suitable mod found on the \"release\" channel. Please Contact us on discord to report this error.")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def is_correct_forge(forge_dir) -> bool:
|
||||
if os.path.isdir(os.path.join(forge_dir, "libraries", "net", "minecraftforge", "forge", forge_version)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
Utils.init_logging("MinecraftClient")
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("apmc_file", default=None, nargs='?', help="Path to an Archipelago Minecraft data file (.apmc)")
|
||||
parser.add_argument('--install', '-i', dest='install', default=False, action='store_true',
|
||||
help="Download and install Java and the Forge server. Does not launch the client afterwards.")
|
||||
parser.add_argument('--release_channel', '-r', dest="channel", type=str, action='store',
|
||||
help="Specify release channel to use.")
|
||||
parser.add_argument('--java', '-j', metavar='17', dest='java', type=str, default=False, action='store',
|
||||
help="specify java version.")
|
||||
parser.add_argument('--forge', '-f', metavar='1.18.2-40.1.0', dest='forge', type=str, default=False, action='store',
|
||||
help="specify forge version. (Minecraft Version-Forge Version)")
|
||||
parser.add_argument('--version', '-v', metavar='9', dest='data_version', type=int, action='store',
|
||||
help="specify Mod data version to download.")
|
||||
|
||||
args = parser.parse_args()
|
||||
apmc_file = os.path.abspath(args.apmc_file) if args.apmc_file else None
|
||||
|
||||
# Change to executable's working directory
|
||||
os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))
|
||||
|
||||
options = get_settings().minecraft_options
|
||||
channel = args.channel or options.release_channel
|
||||
apmc_data = None
|
||||
data_version = args.data_version or None
|
||||
|
||||
if apmc_file is None and not args.install:
|
||||
apmc_file = Utils.open_filename('Select APMC file', (('APMC File', ('.apmc',)),))
|
||||
|
||||
if apmc_file is not None and data_version is None:
|
||||
apmc_data = read_apmc_file(apmc_file)
|
||||
data_version = apmc_data.get('client_version', '')
|
||||
|
||||
versions = get_minecraft_versions(data_version, channel)
|
||||
|
||||
forge_dir = options.forge_directory
|
||||
max_heap = options.max_heap_size
|
||||
forge_version = args.forge or versions["forge"]
|
||||
java_version = args.java or versions["java"]
|
||||
mod_url = versions["url"]
|
||||
java_dir = find_jdk_dir(java_version)
|
||||
|
||||
if args.install:
|
||||
if is_windows:
|
||||
print("Installing Java")
|
||||
download_java(java_version)
|
||||
if not is_correct_forge(forge_dir):
|
||||
print("Installing Minecraft Forge")
|
||||
install_forge(forge_dir, forge_version, java_version)
|
||||
else:
|
||||
print("Correct Forge version already found, skipping install.")
|
||||
sys.exit(0)
|
||||
|
||||
if apmc_data is None:
|
||||
raise FileNotFoundError(f"APMC file does not exist or is inaccessible at the given location ({apmc_file})")
|
||||
|
||||
if is_windows:
|
||||
if java_dir is None or not os.path.isdir(java_dir):
|
||||
if prompt_yes_no("Did not find java directory. Download and install java now?"):
|
||||
download_java(java_version)
|
||||
java_dir = find_jdk_dir(java_version)
|
||||
if java_dir is None or not os.path.isdir(java_dir):
|
||||
raise NotADirectoryError(f"Path {java_dir} does not exist or could not be accessed.")
|
||||
|
||||
if not is_correct_forge(forge_dir):
|
||||
if prompt_yes_no(f"Did not find forge version {forge_version} download and install it now?"):
|
||||
install_forge(forge_dir, forge_version, java_version)
|
||||
if not os.path.isdir(forge_dir):
|
||||
raise NotADirectoryError(f"Path {forge_dir} does not exist or could not be accessed.")
|
||||
|
||||
if not max_heap_re.match(max_heap):
|
||||
raise Exception(f"Max heap size {max_heap} in incorrect format. Use a number followed by M or G, e.g. 512M or 2G.")
|
||||
|
||||
update_mod(forge_dir, mod_url)
|
||||
replace_apmc_files(forge_dir, apmc_file)
|
||||
check_eula(forge_dir)
|
||||
server_process = run_forge_server(forge_dir, java_version, max_heap)
|
||||
server_process.wait()
|
@@ -7,7 +7,6 @@ Currently, the following games are supported:
|
||||
|
||||
* The Legend of Zelda: A Link to the Past
|
||||
* Factorio
|
||||
* Minecraft
|
||||
* Subnautica
|
||||
* Risk of Rain 2
|
||||
* The Legend of Zelda: Ocarina of Time
|
||||
|
@@ -61,12 +61,7 @@ def download_slot_file(room_id, player_id: int):
|
||||
else:
|
||||
import io
|
||||
|
||||
if slot_data.game == "Minecraft":
|
||||
from worlds.minecraft import mc_update_output
|
||||
fname = f"AP_{app.jinja_env.filters['suuid'](room_id)}_P{slot_data.player_id}_{slot_data.player_name}.apmc"
|
||||
data = mc_update_output(slot_data.data, server=app.config['HOST_ADDRESS'], port=room.last_port)
|
||||
return send_file(io.BytesIO(data), as_attachment=True, download_name=fname)
|
||||
elif slot_data.game == "Factorio":
|
||||
if slot_data.game == "Factorio":
|
||||
with zipfile.ZipFile(io.BytesIO(slot_data.data)) as zf:
|
||||
for name in zf.namelist():
|
||||
if name.endswith("info.json"):
|
||||
|
@@ -1,102 +0,0 @@
|
||||
#player-tracker-wrapper{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#inventory-table{
|
||||
border-top: 2px solid #000000;
|
||||
border-left: 2px solid #000000;
|
||||
border-right: 2px solid #000000;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
padding: 3px 3px 10px;
|
||||
width: 384px;
|
||||
background-color: #42b149;
|
||||
}
|
||||
|
||||
#inventory-table td{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#inventory-table img{
|
||||
height: 100%;
|
||||
max-width: 40px;
|
||||
max-height: 40px;
|
||||
filter: grayscale(100%) contrast(75%) brightness(30%);
|
||||
}
|
||||
|
||||
#inventory-table img.acquired{
|
||||
filter: none;
|
||||
}
|
||||
|
||||
#inventory-table div.counted-item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#inventory-table div.item-count {
|
||||
position: absolute;
|
||||
color: white;
|
||||
font-family: "Minecraftia", monospace;
|
||||
font-weight: bold;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#location-table{
|
||||
width: 384px;
|
||||
border-left: 2px solid #000000;
|
||||
border-right: 2px solid #000000;
|
||||
border-bottom: 2px solid #000000;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
background-color: #42b149;
|
||||
padding: 0 3px 3px;
|
||||
font-family: "Minecraftia", monospace;
|
||||
font-size: 14px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#location-table th{
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#location-table td{
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
#location-table td.counter {
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#location-table td.toggle-arrow {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#location-table tr#Total-header {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#location-table img{
|
||||
height: 100%;
|
||||
max-width: 30px;
|
||||
max-height: 30px;
|
||||
}
|
||||
|
||||
#location-table tbody.locations {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#location-table td.location-name {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
@@ -26,10 +26,7 @@
|
||||
<td>{{ patch.game }}</td>
|
||||
<td>
|
||||
{% if patch.data %}
|
||||
{% if patch.game == "Minecraft" %}
|
||||
<a href="{{ url_for("download_slot_file", room_id=room.id, player_id=patch.player_id) }}" download>
|
||||
Download APMC File...</a>
|
||||
{% elif patch.game == "VVVVVV" and room.seed.slots|length == 1 %}
|
||||
{% if patch.game == "VVVVVV" and room.seed.slots|length == 1 %}
|
||||
<a href="{{ url_for("download_slot_file", room_id=room.id, player_id=patch.player_id) }}" download>
|
||||
Download APV6 File...</a>
|
||||
{% elif patch.game == "Super Mario 64" and room.seed.slots|length == 1 %}
|
||||
|
@@ -1,84 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{ player_name }}'s Tracker</title>
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles/minecraftTracker.css') }}"/>
|
||||
<script type="application/ecmascript" src="{{ url_for('static', filename='assets/minecraftTracker.js') }}"></script>
|
||||
<link rel="stylesheet" media="screen" href="https://fontlibrary.org//face/minecraftia" type="text/css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{# TODO: Replace this with a proper wrapper for each tracker when developing TrackerAPI. #}
|
||||
<div style="margin-bottom: 0.5rem">
|
||||
<a href="{{ url_for("get_generic_game_tracker", tracker=room.tracker, tracked_team=team, tracked_player=player) }}">Switch To Generic Tracker</a>
|
||||
</div>
|
||||
|
||||
<div id="player-tracker-wrapper" data-tracker="{{ room.tracker|suuid }}">
|
||||
<table id="inventory-table">
|
||||
<tr>
|
||||
<td><img src="{{ tools_url }}" class="{{ 'acquired' }}" title="Progressive Tools" /></td>
|
||||
<td><img src="{{ weapons_url }}" class="{{ 'acquired' }}" title="Progressive Weapons" /></td>
|
||||
<td><img src="{{ armor_url }}" class="{{ 'acquired' }}" title="Progressive Armor" /></td>
|
||||
<td><img src="{{ resource_crafting_url }}" class="{{ 'acquired' if 'Progressive Resource Crafting' in acquired_items }}"
|
||||
title="Progressive Resource Crafting" /></td>
|
||||
<td><img src="{{ icons['Brewing Stand'] }}" class="{{ 'acquired' if 'Brewing' in acquired_items }}" title="Brewing" /></td>
|
||||
<td>
|
||||
<div class="counted-item">
|
||||
<img src="{{ icons['Ender Pearl'] }}" class="{{ 'acquired' if '3 Ender Pearls' in acquired_items }}" title="Ender Pearls" />
|
||||
<div class="item-count">{{ pearls_count }}</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="{{ icons['Bucket'] }}" class="{{ 'acquired' if 'Bucket' in acquired_items }}" title="Bucket" /></td>
|
||||
<td><img src="{{ icons['Bow'] }}" class="{{ 'acquired' if 'Archery' in acquired_items }}" title="Archery" /></td>
|
||||
<td><img src="{{ icons['Shield'] }}" class="{{ 'acquired' if 'Shield' in acquired_items }}" title="Shield" /></td>
|
||||
<td><img src="{{ icons['Red Bed'] }}" class="{{ 'acquired' if 'Bed' in acquired_items }}" title="Bed" /></td>
|
||||
<td><img src="{{ icons['Water Bottle'] }}" class="{{ 'acquired' if 'Bottles' in acquired_items }}" title="Bottles" /></td>
|
||||
<td>
|
||||
<div class="counted-item">
|
||||
<img src="{{ icons['Netherite Scrap'] }}" class="{{ 'acquired' if '8 Netherite Scrap' in acquired_items }}" title="Netherite Scrap" />
|
||||
<div class="item-count">{{ scrap_count }}</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="{{ icons['Flint and Steel'] }}" class="{{ 'acquired' if 'Flint and Steel' in acquired_items }}" title="Flint and Steel" /></td>
|
||||
<td><img src="{{ icons['Enchanting Table'] }}" class="{{ 'acquired' if 'Enchanting' in acquired_items }}" title="Enchanting" /></td>
|
||||
<td><img src="{{ icons['Fishing Rod'] }}" class="{{ 'acquired' if 'Fishing Rod' in acquired_items }}" title="Fishing Rod" /></td>
|
||||
<td><img src="{{ icons['Campfire'] }}" class="{{ 'acquired' if 'Campfire' in acquired_items }}" title="Campfire" /></td>
|
||||
<td><img src="{{ icons['Spyglass'] }}" class="{{ 'acquired' if 'Spyglass' in acquired_items }}" title="Spyglass" /></td>
|
||||
<td>
|
||||
<div class="counted-item">
|
||||
<img src="{{ icons['Dragon Egg Shard'] }}" class="{{ 'acquired' if 'Dragon Egg Shard' in acquired_items }}" title="Dragon Egg Shard" />
|
||||
<div class="item-count">{{ shard_count }}</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="{{ icons['Lead'] }}" class="{{ 'acquired' if 'Lead' in acquired_items }}" title="Lead" /></td>
|
||||
<td><img src="{{ icons['Saddle'] }}" class="{{ 'acquired' if 'Saddle' in acquired_items }}" title="Saddle" /></td>
|
||||
<td><img src="{{ icons['Channeling Book'] }}" class="{{ 'acquired' if 'Channeling Book' in acquired_items }}" title="Channeling Book" /></td>
|
||||
<td><img src="{{ icons['Silk Touch Book'] }}" class="{{ 'acquired' if 'Silk Touch Book' in acquired_items }}" title="Silk Touch Book" /></td>
|
||||
<td><img src="{{ icons['Piercing IV Book'] }}" class="{{ 'acquired' if 'Piercing IV Book' in acquired_items }}" title="Piercing IV Book" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table id="location-table">
|
||||
{% for area in checks_done %}
|
||||
<tr class="location-category" id="{{area}}-header">
|
||||
<td>{{ area }} {{'▼' if area != 'Total'}}</td>
|
||||
<td class="counter">{{ checks_done[area] }} / {{ checks_in_area[area] }}</td>
|
||||
</tr>
|
||||
<tbody class="locations hide" id="{{area}}">
|
||||
{% for location in location_info[area] %}
|
||||
<tr>
|
||||
<td class="location-name">{{ location }}</td>
|
||||
<td class="counter">{{ '✔' if location_info[area][location] else '' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@@ -706,127 +706,6 @@ if "A Link to the Past" in network_data_package["games"]:
|
||||
_multiworld_trackers["A Link to the Past"] = render_ALinkToThePast_multiworld_tracker
|
||||
_player_trackers["A Link to the Past"] = render_ALinkToThePast_tracker
|
||||
|
||||
if "Minecraft" in network_data_package["games"]:
|
||||
def render_Minecraft_tracker(tracker_data: TrackerData, team: int, player: int) -> str:
|
||||
icons = {
|
||||
"Wooden Pickaxe": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/d/d2/Wooden_Pickaxe_JE3_BE3.png",
|
||||
"Stone Pickaxe": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/c/c4/Stone_Pickaxe_JE2_BE2.png",
|
||||
"Iron Pickaxe": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/d/d1/Iron_Pickaxe_JE3_BE2.png",
|
||||
"Diamond Pickaxe": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/e/e7/Diamond_Pickaxe_JE3_BE3.png",
|
||||
"Wooden Sword": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/d/d5/Wooden_Sword_JE2_BE2.png",
|
||||
"Stone Sword": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/b/b1/Stone_Sword_JE2_BE2.png",
|
||||
"Iron Sword": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/8/8e/Iron_Sword_JE2_BE2.png",
|
||||
"Diamond Sword": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/4/44/Diamond_Sword_JE3_BE3.png",
|
||||
"Leather Tunic": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/b/b7/Leather_Tunic_JE4_BE2.png",
|
||||
"Iron Chestplate": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/3/31/Iron_Chestplate_JE2_BE2.png",
|
||||
"Diamond Chestplate": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/e/e0/Diamond_Chestplate_JE3_BE2.png",
|
||||
"Iron Ingot": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/f/fc/Iron_Ingot_JE3_BE2.png",
|
||||
"Block of Iron": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/7/7e/Block_of_Iron_JE4_BE3.png",
|
||||
"Brewing Stand": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/b/b3/Brewing_Stand_%28empty%29_JE10.png",
|
||||
"Ender Pearl": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/f/f6/Ender_Pearl_JE3_BE2.png",
|
||||
"Bucket": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/f/fc/Bucket_JE2_BE2.png",
|
||||
"Bow": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/a/ab/Bow_%28Pull_2%29_JE1_BE1.png",
|
||||
"Shield": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/c/c6/Shield_JE2_BE1.png",
|
||||
"Red Bed": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/6/6a/Red_Bed_%28N%29.png",
|
||||
"Netherite Scrap": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/3/33/Netherite_Scrap_JE2_BE1.png",
|
||||
"Flint and Steel": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/9/94/Flint_and_Steel_JE4_BE2.png",
|
||||
"Enchanting Table": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/3/31/Enchanting_Table.gif",
|
||||
"Fishing Rod": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/7/7f/Fishing_Rod_JE2_BE2.png",
|
||||
"Campfire": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/9/91/Campfire_JE2_BE2.gif",
|
||||
"Water Bottle": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/7/75/Water_Bottle_JE2_BE2.png",
|
||||
"Spyglass": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/c/c1/Spyglass_JE2_BE1.png",
|
||||
"Dragon Egg Shard": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/3/38/Dragon_Egg_JE4.png",
|
||||
"Lead": "https://static.wikia.nocookie.net/minecraft_gamepedia/images/1/1f/Lead_JE2_BE2.png",
|
||||
"Saddle": "https://i.imgur.com/2QtDyR0.png",
|
||||
"Channeling Book": "https://i.imgur.com/J3WsYZw.png",
|
||||
"Silk Touch Book": "https://i.imgur.com/iqERxHQ.png",
|
||||
"Piercing IV Book": "https://i.imgur.com/OzJptGz.png",
|
||||
}
|
||||
|
||||
minecraft_location_ids = {
|
||||
"Story": [42073, 42023, 42027, 42039, 42002, 42009, 42010, 42070,
|
||||
42041, 42049, 42004, 42031, 42025, 42029, 42051, 42077],
|
||||
"Nether": [42017, 42044, 42069, 42058, 42034, 42060, 42066, 42076, 42064, 42071, 42021,
|
||||
42062, 42008, 42061, 42033, 42011, 42006, 42019, 42000, 42040, 42001, 42015, 42104, 42014],
|
||||
"The End": [42052, 42005, 42012, 42032, 42030, 42042, 42018, 42038, 42046],
|
||||
"Adventure": [42047, 42050, 42096, 42097, 42098, 42059, 42055, 42072, 42003, 42109, 42035, 42016, 42020,
|
||||
42048, 42054, 42068, 42043, 42106, 42074, 42075, 42024, 42026, 42037, 42045, 42056, 42105,
|
||||
42099, 42103, 42110, 42100],
|
||||
"Husbandry": [42065, 42067, 42078, 42022, 42113, 42107, 42007, 42079, 42013, 42028, 42036, 42108, 42111,
|
||||
42112,
|
||||
42057, 42063, 42053, 42102, 42101, 42092, 42093, 42094, 42095],
|
||||
"Archipelago": [42080, 42081, 42082, 42083, 42084, 42085, 42086, 42087, 42088, 42089, 42090, 42091],
|
||||
}
|
||||
|
||||
display_data = {}
|
||||
|
||||
# Determine display for progressive items
|
||||
progressive_items = {
|
||||
"Progressive Tools": 45013,
|
||||
"Progressive Weapons": 45012,
|
||||
"Progressive Armor": 45014,
|
||||
"Progressive Resource Crafting": 45001
|
||||
}
|
||||
progressive_names = {
|
||||
"Progressive Tools": ["Wooden Pickaxe", "Stone Pickaxe", "Iron Pickaxe", "Diamond Pickaxe"],
|
||||
"Progressive Weapons": ["Wooden Sword", "Stone Sword", "Iron Sword", "Diamond Sword"],
|
||||
"Progressive Armor": ["Leather Tunic", "Iron Chestplate", "Diamond Chestplate"],
|
||||
"Progressive Resource Crafting": ["Iron Ingot", "Iron Ingot", "Block of Iron"]
|
||||
}
|
||||
|
||||
inventory = tracker_data.get_player_inventory_counts(team, player)
|
||||
for item_name, item_id in progressive_items.items():
|
||||
level = min(inventory[item_id], len(progressive_names[item_name]) - 1)
|
||||
display_name = progressive_names[item_name][level]
|
||||
base_name = item_name.split(maxsplit=1)[1].lower().replace(" ", "_")
|
||||
display_data[base_name + "_url"] = icons[display_name]
|
||||
|
||||
# Multi-items
|
||||
multi_items = {
|
||||
"3 Ender Pearls": 45029,
|
||||
"8 Netherite Scrap": 45015,
|
||||
"Dragon Egg Shard": 45043
|
||||
}
|
||||
for item_name, item_id in multi_items.items():
|
||||
base_name = item_name.split()[-1].lower()
|
||||
count = inventory[item_id]
|
||||
if count >= 0:
|
||||
display_data[base_name + "_count"] = count
|
||||
|
||||
# Victory condition
|
||||
game_state = tracker_data.get_player_client_status(team, player)
|
||||
display_data["game_finished"] = game_state == 30
|
||||
|
||||
# Turn location IDs into advancement tab counts
|
||||
checked_locations = tracker_data.get_player_checked_locations(team, player)
|
||||
lookup_name = lambda id: tracker_data.location_id_to_name["Minecraft"][id]
|
||||
location_info = {tab_name: {lookup_name(id): (id in checked_locations) for id in tab_locations}
|
||||
for tab_name, tab_locations in minecraft_location_ids.items()}
|
||||
checks_done = {tab_name: len([id for id in tab_locations if id in checked_locations])
|
||||
for tab_name, tab_locations in minecraft_location_ids.items()}
|
||||
checks_done["Total"] = len(checked_locations)
|
||||
checks_in_area = {tab_name: len(tab_locations) for tab_name, tab_locations in minecraft_location_ids.items()}
|
||||
checks_in_area["Total"] = sum(checks_in_area.values())
|
||||
|
||||
lookup_any_item_id_to_name = tracker_data.item_id_to_name["Minecraft"]
|
||||
return render_template(
|
||||
"tracker__Minecraft.html",
|
||||
inventory=inventory,
|
||||
icons=icons,
|
||||
acquired_items={lookup_any_item_id_to_name[id] for id, count in inventory.items() if count > 0},
|
||||
player=player,
|
||||
team=team,
|
||||
room=tracker_data.room,
|
||||
player_name=tracker_data.get_player_name(team, player),
|
||||
saving_second=tracker_data.get_room_saving_second(),
|
||||
checks_done=checks_done,
|
||||
checks_in_area=checks_in_area,
|
||||
location_info=location_info,
|
||||
**display_data,
|
||||
)
|
||||
|
||||
_player_trackers["Minecraft"] = render_Minecraft_tracker
|
||||
|
||||
if "Ocarina of Time" in network_data_package["games"]:
|
||||
def render_OcarinaOfTime_tracker(tracker_data: TrackerData, team: int, player: int) -> str:
|
||||
icons = {
|
||||
|
@@ -135,11 +135,6 @@ def upload_zip_to_db(zfile: zipfile.ZipFile, owner=None, meta={"race": False}, s
|
||||
flash("Could not load multidata. File may be corrupted or incompatible.")
|
||||
multidata = None
|
||||
|
||||
# Minecraft
|
||||
elif file.filename.endswith(".apmc"):
|
||||
data = zfile.open(file, "r").read()
|
||||
metadata = json.loads(base64.b64decode(data).decode("utf-8"))
|
||||
files[metadata["player_id"]] = data
|
||||
|
||||
# Factorio
|
||||
elif file.filename.endswith(".zip"):
|
||||
|
BIN
data/mcicon.ico
BIN
data/mcicon.ico
Binary file not shown.
Before Width: | Height: | Size: 2.6 KiB |
@@ -121,9 +121,6 @@
|
||||
# The Messenger
|
||||
/worlds/messenger/ @alwaysintreble
|
||||
|
||||
# Minecraft
|
||||
/worlds/minecraft/ @KonoTyran @espeon65536
|
||||
|
||||
# Mega Man 2
|
||||
/worlds/mm2/ @Silvris
|
||||
|
||||
|
@@ -117,12 +117,6 @@ flowchart LR
|
||||
%% Java Based Games
|
||||
subgraph Java
|
||||
JM[Mod with Archipelago.MultiClient.Java]
|
||||
subgraph Minecraft
|
||||
MCS[Minecraft Forge Server]
|
||||
JMC[Any Java Minecraft Clients]
|
||||
MCS <-- TCP --> JMC
|
||||
end
|
||||
JM <-- Forge Mod Loader --> MCS
|
||||
end
|
||||
AS <-- WebSockets --> JM
|
||||
|
||||
|
@@ -138,11 +138,6 @@ Root: HKCR; Subkey: "{#MyAppName}kdl3patch"; ValueData: "Arc
|
||||
Root: HKCR; Subkey: "{#MyAppName}kdl3patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: "";
|
||||
Root: HKCR; Subkey: "{#MyAppName}kdl3patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: "";
|
||||
|
||||
Root: HKCR; Subkey: ".apmc"; ValueData: "{#MyAppName}mcdata"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
|
||||
Root: HKCR; Subkey: "{#MyAppName}mcdata"; ValueData: "Archipelago Minecraft Data"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
|
||||
Root: HKCR; Subkey: "{#MyAppName}mcdata\DefaultIcon"; ValueData: "{app}\ArchipelagoMinecraftClient.exe,0"; ValueType: string; ValueName: "";
|
||||
Root: HKCR; Subkey: "{#MyAppName}mcdata\shell\open\command"; ValueData: """{app}\ArchipelagoMinecraftClient.exe"" ""%1"""; ValueType: string; ValueName: "";
|
||||
|
||||
Root: HKCR; Subkey: ".apz5"; ValueData: "{#MyAppName}n64zpf"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
|
||||
Root: HKCR; Subkey: "{#MyAppName}n64zpf"; ValueData: "Archipelago Ocarina of Time Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
|
||||
Root: HKCR; Subkey: "{#MyAppName}n64zpf\DefaultIcon"; ValueData: "{app}\ArchipelagoOoTClient.exe,0"; ValueType: string; ValueName: "";
|
||||
|
@@ -221,9 +221,6 @@ components: List[Component] = [
|
||||
Component('Links Awakening DX Client', 'LinksAwakeningClient',
|
||||
file_identifier=SuffixIdentifier('.apladx')),
|
||||
Component('LttP Adjuster', 'LttPAdjuster'),
|
||||
# Minecraft
|
||||
Component('Minecraft Client', 'MinecraftClient', icon='mcicon', cli=True,
|
||||
file_identifier=SuffixIdentifier('.apmc')),
|
||||
# Ocarina of Time
|
||||
Component('OoT Client', 'OoTClient',
|
||||
file_identifier=SuffixIdentifier('.apz5')),
|
||||
@@ -246,6 +243,5 @@ components: List[Component] = [
|
||||
# if registering an icon from within an apworld, the format "ap:module.name/path/to/file.png" can be used
|
||||
icon_paths = {
|
||||
'icon': local_path('data', 'icon.png'),
|
||||
'mcicon': local_path('data', 'mcicon.png'),
|
||||
'discord': local_path('data', 'discord-mark-blue.png'),
|
||||
}
|
||||
|
@@ -288,7 +288,7 @@ world and the beginning of another world. You can also combine multiple files by
|
||||
### Example
|
||||
|
||||
```yaml
|
||||
description: Example of generating multiple worlds. World 1 of 3
|
||||
description: Example of generating multiple worlds. World 1 of 2
|
||||
name: Mario
|
||||
game: Super Mario 64
|
||||
requires:
|
||||
@@ -310,31 +310,6 @@ Super Mario 64:
|
||||
|
||||
---
|
||||
|
||||
description: Example of generating multiple worlds. World 2 of 3
|
||||
name: Minecraft
|
||||
game: Minecraft
|
||||
Minecraft:
|
||||
progression_balancing: 50
|
||||
accessibility: items
|
||||
advancement_goal: 40
|
||||
combat_difficulty: hard
|
||||
include_hard_advancements: false
|
||||
include_unreasonable_advancements: false
|
||||
include_postgame_advancements: false
|
||||
shuffle_structures: true
|
||||
structure_compasses: true
|
||||
send_defeated_mobs: true
|
||||
bee_traps: 15
|
||||
egg_shards_required: 7
|
||||
egg_shards_available: 10
|
||||
required_bosses:
|
||||
none: 0
|
||||
ender_dragon: 1
|
||||
wither: 0
|
||||
both: 0
|
||||
|
||||
---
|
||||
|
||||
description: Example of generating multiple worlds. World 2 of 2
|
||||
name: ExampleFinder
|
||||
game: ChecksFinder
|
||||
@@ -344,6 +319,6 @@ ChecksFinder:
|
||||
accessibility: items
|
||||
```
|
||||
|
||||
The above example will generate 3 worlds - one Super Mario 64, one Minecraft, and one ChecksFinder.
|
||||
The above example will generate 2 worlds - one Super Mario 64 and one ChecksFinder.
|
||||
|
||||
|
||||
|
@@ -194,7 +194,7 @@ relevant guide: [A Link to the Past Plando Guide](/tutorial/A%20Link%20to%20the%
|
||||
|
||||
## Connection Plando
|
||||
|
||||
This is currently only supported by a few games, including A Link to the Past, Minecraft, and Ocarina of Time. As the way that these games interact with their
|
||||
This is currently only supported by a few games, including A Link to the Past and Ocarina of Time. As the way that these games interact with their
|
||||
connections is different, only the basics are explained here. More specific information for connection plando in A Link to the Past can be found in
|
||||
its [plando guide](/tutorial/A%20Link%20to%20the%20Past/plando/en#connections).
|
||||
|
||||
@@ -207,7 +207,6 @@ its [plando guide](/tutorial/A%20Link%20to%20the%20Past/plando/en#connections).
|
||||
|
||||
[A Link to the Past connections](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/alttp/EntranceShuffle.py#L3852)
|
||||
|
||||
[Minecraft connections](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/minecraft/data/regions.json#L18****)
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -224,18 +223,9 @@ its [plando guide](/tutorial/A%20Link%20to%20the%20Past/plando/en#connections).
|
||||
exit: Old Man Cave Exit (West)
|
||||
direction: exit
|
||||
|
||||
# example block 2 - Minecraft
|
||||
- entrance: Overworld Structure 1
|
||||
exit: Nether Fortress
|
||||
direction: both
|
||||
- entrance: Overworld Structure 2
|
||||
exit: Village
|
||||
direction: both
|
||||
```
|
||||
|
||||
1. These connections are decoupled, so going into the Lake Hylia Cave Shop will take you to the inside of Cave 45, and
|
||||
when you leave the interior, you will exit to the Cave 45 ledge. Going into the Cave 45 entrance will then take you to
|
||||
the Lake Hylia Cave Shop. Walking into the entrance for the Old Man Cave and Agahnim's Tower entrance will both take
|
||||
you to their locations as normal, but leaving Old Man Cave will exit at Agahnim's Tower.
|
||||
2. This will force a Nether fortress and a village to be the Overworld structures for your game. Note that for the
|
||||
Minecraft connection plando to work structure shuffle must be enabled.
|
||||
|
@@ -1,26 +0,0 @@
|
||||
import os
|
||||
import json
|
||||
import pkgutil
|
||||
|
||||
def load_data_file(*args) -> dict:
|
||||
fname = "/".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")
|
@@ -1,55 +0,0 @@
|
||||
from math import ceil
|
||||
from typing import List
|
||||
|
||||
from BaseClasses import Item
|
||||
|
||||
from . import Constants
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import MinecraftWorld
|
||||
|
||||
|
||||
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(world: "MinecraftWorld") -> List[Item]:
|
||||
multiworld = world.multiworld
|
||||
player = world.player
|
||||
|
||||
itempool = []
|
||||
total_location_count = len(multiworld.get_unfilled_locations(player))
|
||||
|
||||
required_pool = Constants.item_info["required_pool"]
|
||||
|
||||
# Add required progression items
|
||||
for item_name, num in required_pool.items():
|
||||
itempool += [world.create_item(item_name) for _ in range(num)]
|
||||
|
||||
# Add structure compasses
|
||||
if world.options.structure_compasses:
|
||||
compasses = [name for name in world.item_name_to_id if "Structure Compass" in name]
|
||||
for item_name in compasses:
|
||||
itempool.append(world.create_item(item_name))
|
||||
|
||||
# Dragon egg shards
|
||||
if world.options.egg_shards_required > 0:
|
||||
num = world.options.egg_shards_available
|
||||
itempool += [world.create_item("Dragon Egg Shard") for _ in range(num)]
|
||||
|
||||
# Bee traps
|
||||
bee_trap_percentage = world.options.bee_traps * 0.01
|
||||
if bee_trap_percentage > 0:
|
||||
bee_trap_qty = ceil(bee_trap_percentage * (total_location_count - len(itempool)))
|
||||
itempool += [world.create_item("Bee Trap") for _ in range(bee_trap_qty)]
|
||||
|
||||
# Fill remaining itempool with randomly generated junk
|
||||
junk = get_junk_item_names(world.random, total_location_count - len(itempool))
|
||||
itempool += [world.create_item(name) for name in junk]
|
||||
|
||||
return itempool
|
@@ -1,143 +0,0 @@
|
||||
from Options import Choice, Toggle, DefaultOnToggle, Range, OptionList, DeathLink, PlandoConnections, \
|
||||
PerGameCommonOptions
|
||||
from .Constants import region_info
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class AdvancementGoal(Range):
|
||||
"""Number of advancements required to spawn bosses."""
|
||||
display_name = "Advancement Goal"
|
||||
range_start = 0
|
||||
range_end = 114
|
||||
default = 40
|
||||
|
||||
|
||||
class EggShardsRequired(Range):
|
||||
"""Number of dragon egg shards to collect to spawn bosses."""
|
||||
display_name = "Egg Shards Required"
|
||||
range_start = 0
|
||||
range_end = 50
|
||||
default = 0
|
||||
|
||||
|
||||
class EggShardsAvailable(Range):
|
||||
"""Number of dragon egg shards available to collect."""
|
||||
display_name = "Egg Shards Available"
|
||||
range_start = 0
|
||||
range_end = 50
|
||||
default = 0
|
||||
|
||||
|
||||
class BossGoal(Choice):
|
||||
"""Bosses which must be defeated to finish the game."""
|
||||
display_name = "Required Bosses"
|
||||
option_none = 0
|
||||
option_ender_dragon = 1
|
||||
option_wither = 2
|
||||
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."""
|
||||
display_name = "Shuffle Structures"
|
||||
|
||||
|
||||
class StructureCompasses(DefaultOnToggle):
|
||||
"""Adds structure compasses to the item pool, which point to the nearest indicated structure."""
|
||||
display_name = "Structure Compasses"
|
||||
|
||||
|
||||
class BeeTraps(Range):
|
||||
"""Replaces a percentage of junk items with bee traps, which spawn multiple angered bees around every player when
|
||||
received."""
|
||||
display_name = "Bee Trap Percentage"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 0
|
||||
|
||||
|
||||
class CombatDifficulty(Choice):
|
||||
"""Modifies the level of items logically required for exploring dangerous areas and fighting bosses."""
|
||||
display_name = "Combat Difficulty"
|
||||
option_easy = 0
|
||||
option_normal = 1
|
||||
option_hard = 2
|
||||
default = 1
|
||||
|
||||
|
||||
class HardAdvancements(Toggle):
|
||||
"""Enables certain RNG-reliant or tedious advancements."""
|
||||
display_name = "Include Hard Advancements"
|
||||
|
||||
|
||||
class UnreasonableAdvancements(Toggle):
|
||||
"""Enables the extremely difficult advancements "How Did We Get Here?" and "Adventuring Time.\""""
|
||||
display_name = "Include Unreasonable Advancements"
|
||||
|
||||
|
||||
class PostgameAdvancements(Toggle):
|
||||
"""Enables advancements that require spawning and defeating the required bosses."""
|
||||
display_name = "Include Postgame Advancements"
|
||||
|
||||
|
||||
class SendDefeatedMobs(Toggle):
|
||||
"""Send killed mobs to other Minecraft worlds which have this option enabled."""
|
||||
display_name = "Send Defeated Mobs"
|
||||
|
||||
|
||||
class StartingItems(OptionList):
|
||||
"""Start with these items. Each entry should be of this format: {item: "item_name", amount: #}
|
||||
`item` can include components, and should be in an identical format to a `/give` command with
|
||||
`"` escaped for json reasons.
|
||||
|
||||
`amount` is optional and will default to 1 if omitted.
|
||||
|
||||
example:
|
||||
```
|
||||
starting_items: [
|
||||
{ "item": "minecraft:stick[minecraft:custom_name=\"{'text':'pointy stick'}\"]" },
|
||||
{ "item": "minecraft:arrow[minecraft:rarity=epic]", amount: 64 }
|
||||
]
|
||||
```
|
||||
"""
|
||||
display_name = "Starting Items"
|
||||
|
||||
|
||||
class MCPlandoConnections(PlandoConnections):
|
||||
entrances = set(connection[0] for connection in region_info["default_connections"])
|
||||
exits = set(connection[1] for connection in region_info["default_connections"])
|
||||
|
||||
@classmethod
|
||||
def can_connect(cls, entrance, exit):
|
||||
if exit in region_info["illegal_connections"] and entrance in region_info["illegal_connections"][exit]:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@dataclass
|
||||
class MinecraftOptions(PerGameCommonOptions):
|
||||
plando_connections: MCPlandoConnections
|
||||
advancement_goal: AdvancementGoal
|
||||
egg_shards_required: EggShardsRequired
|
||||
egg_shards_available: EggShardsAvailable
|
||||
required_bosses: BossGoal
|
||||
shuffle_structures: ShuffleStructures
|
||||
structure_compasses: StructureCompasses
|
||||
|
||||
combat_difficulty: CombatDifficulty
|
||||
include_hard_advancements: HardAdvancements
|
||||
include_unreasonable_advancements: UnreasonableAdvancements
|
||||
include_postgame_advancements: PostgameAdvancements
|
||||
bee_traps: BeeTraps
|
||||
send_defeated_mobs: SendDefeatedMobs
|
||||
death_link: DeathLink
|
||||
starting_items: StartingItems
|
@@ -1,508 +0,0 @@
|
||||
from BaseClasses import CollectionState
|
||||
from worlds.generic.Rules import exclusion_rules
|
||||
|
||||
from . import Constants
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import MinecraftWorld
|
||||
|
||||
|
||||
# Helper functions
|
||||
# moved from logicmixin
|
||||
|
||||
def has_iron_ingots(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return state.has('Progressive Tools', player) and state.has('Progressive Resource Crafting', player)
|
||||
|
||||
|
||||
def has_copper_ingots(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return state.has('Progressive Tools', player) and state.has('Progressive Resource Crafting', player)
|
||||
|
||||
|
||||
def has_gold_ingots(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return (state.has('Progressive Resource Crafting', player)
|
||||
and (
|
||||
state.has('Progressive Tools', player, 2)
|
||||
or state.can_reach_region('The Nether', player)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def has_diamond_pickaxe(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return state.has('Progressive Tools', player, 3) and has_iron_ingots(world, state, player)
|
||||
|
||||
|
||||
def craft_crossbow(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return state.has('Archery', player) and has_iron_ingots(world, state, player)
|
||||
|
||||
|
||||
def has_bottle(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return state.has('Bottles', player) and state.has('Progressive Resource Crafting', player)
|
||||
|
||||
|
||||
def has_spyglass(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return (has_copper_ingots(world, state, player)
|
||||
and state.has('Spyglass', player)
|
||||
and can_adventure(world, state, player)
|
||||
)
|
||||
|
||||
|
||||
def can_enchant(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return state.has('Enchanting', player) and has_diamond_pickaxe(world, state, player) # mine obsidian and lapis
|
||||
|
||||
|
||||
def can_use_anvil(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return (state.has('Enchanting', player)
|
||||
and state.has('Progressive Resource Crafting', player,2)
|
||||
and has_iron_ingots(world, state, player)
|
||||
)
|
||||
|
||||
|
||||
def fortress_loot(world: "MinecraftWorld", state: CollectionState, player: int) -> bool: # saddles, blaze rods, wither skulls
|
||||
return state.can_reach_region('Nether Fortress', player) and basic_combat(world, state, player)
|
||||
|
||||
|
||||
def can_brew_potions(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return state.has('Blaze Rods', player) and state.has('Brewing', player) and has_bottle(world, state, player)
|
||||
|
||||
|
||||
def can_piglin_trade(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return (has_gold_ingots(world, state, player)
|
||||
and (
|
||||
state.can_reach_region('The Nether', player)
|
||||
or state.can_reach_region('Bastion Remnant', player)
|
||||
))
|
||||
|
||||
|
||||
def overworld_villager(world: "MinecraftWorld", 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_location('Zombie Doctor', player)
|
||||
or (
|
||||
has_diamond_pickaxe(world, state, player)
|
||||
and state.can_reach_region('Village', player)
|
||||
))
|
||||
elif village_region == 'The End':
|
||||
return state.can_reach_location('Zombie Doctor', player)
|
||||
return state.can_reach_region('Village', player)
|
||||
|
||||
|
||||
def enter_stronghold(world: "MinecraftWorld", 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(world: "MinecraftWorld", state: CollectionState, player: int) -> str:
|
||||
return world.options.combat_difficulty.current_key
|
||||
|
||||
|
||||
def can_adventure(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
death_link_check = not world.options.death_link or state.has('Bed', player)
|
||||
if combat_difficulty(world, state, player) == 'easy':
|
||||
return state.has('Progressive Weapons', player, 2) and has_iron_ingots(world, state, player) and death_link_check
|
||||
elif combat_difficulty(world, 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(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
if combat_difficulty(world, 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(world, state, player)
|
||||
)
|
||||
elif combat_difficulty(world, 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(world, state, player)
|
||||
)
|
||||
|
||||
|
||||
def complete_raid(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
reach_regions = (state.can_reach_region('Village', player)
|
||||
and state.can_reach_region('Pillager Outpost', player))
|
||||
if combat_difficulty(world, 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(world, state, player)
|
||||
)
|
||||
elif combat_difficulty(world, state, player) == 'hard': # might be too hard?
|
||||
return (reach_regions
|
||||
and state.has('Progressive Weapons', player, 2)
|
||||
and has_iron_ingots(world, 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(world, state, player)
|
||||
and state.has('Progressive Armor', player)
|
||||
and state.has('Shield', player)
|
||||
)
|
||||
|
||||
|
||||
def can_kill_wither(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
normal_kill = (state.has("Progressive Weapons", player, 3)
|
||||
and state.has("Progressive Armor", player, 2)
|
||||
and can_brew_potions(world, state, player)
|
||||
and can_enchant(world, state, player)
|
||||
)
|
||||
if combat_difficulty(world, state, player) == 'easy':
|
||||
return (fortress_loot(world, state, player)
|
||||
and normal_kill
|
||||
and state.has('Archery', player)
|
||||
)
|
||||
elif combat_difficulty(world, state, player) == 'hard': # cheese kill using bedrock ceilings
|
||||
return (fortress_loot(world, state, player)
|
||||
and (
|
||||
normal_kill
|
||||
or state.can_reach_region('The Nether', player)
|
||||
or state.can_reach_region('The End', player)
|
||||
)
|
||||
)
|
||||
|
||||
return fortress_loot(world, state, player) and normal_kill
|
||||
|
||||
|
||||
def can_respawn_ender_dragon(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
return (state.can_reach_region('The Nether', player)
|
||||
and state.can_reach_region('The End', player)
|
||||
and state.has('Progressive Resource Crafting', player) # smelt sand into glass
|
||||
)
|
||||
|
||||
|
||||
def can_kill_ender_dragon(world: "MinecraftWorld", state: CollectionState, player: int) -> bool:
|
||||
if combat_difficulty(world, 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(world, state, player)
|
||||
and can_enchant(world, state, player)
|
||||
)
|
||||
if combat_difficulty(world, 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) # who needs armor when you can respawn right outside the chamber
|
||||
)
|
||||
)
|
||||
return (state.has('Progressive Weapons', player, 2)
|
||||
and state.has('Progressive Armor', player)
|
||||
and state.has('Archery', player)
|
||||
)
|
||||
|
||||
|
||||
def has_structure_compass(world: "MinecraftWorld", state: CollectionState, entrance_name: str, player: int) -> bool:
|
||||
if not world.options.structure_compasses:
|
||||
return True
|
||||
return state.has(f"Structure Compass ({state.multiworld.get_entrance(entrance_name, player).connected_region.name})", player)
|
||||
|
||||
|
||||
def get_rules_lookup(world, player: int):
|
||||
rules_lookup = {
|
||||
"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(world, state, player),
|
||||
"End Portal": lambda state: enter_stronghold(world, state, player)
|
||||
and state.has('3 Ender Pearls', player, 4),
|
||||
"Overworld Structure 1": lambda state: can_adventure(world, state, player)
|
||||
and has_structure_compass(world, state, "Overworld Structure 1", player),
|
||||
"Overworld Structure 2": lambda state: can_adventure(world, state, player)
|
||||
and has_structure_compass(world, state, "Overworld Structure 2", player),
|
||||
"Nether Structure 1": lambda state: can_adventure(world, state, player)
|
||||
and has_structure_compass(world, state, "Nether Structure 1", player),
|
||||
"Nether Structure 2": lambda state: can_adventure(world, state, player)
|
||||
and has_structure_compass(world, state, "Nether Structure 2", player),
|
||||
"The End Structure": lambda state: can_adventure(world, state, player)
|
||||
and has_structure_compass(world, state, "The End Structure", player),
|
||||
},
|
||||
"locations": {
|
||||
"Ender Dragon": lambda state: can_respawn_ender_dragon(world, state, player)
|
||||
and can_kill_ender_dragon(world, state, player),
|
||||
"Wither": lambda state: can_kill_wither(world, state, player),
|
||||
"Blaze Rods": lambda state: fortress_loot(world, state, player),
|
||||
"Who is Cutting Onions?": lambda state: can_piglin_trade(world, state, player),
|
||||
"Oh Shiny": lambda state: can_piglin_trade(world, state, player),
|
||||
"Suit Up": lambda state: state.has("Progressive Armor", player)
|
||||
and has_iron_ingots(world, state, player),
|
||||
"Very Very Frightening": lambda state: state.has("Channeling Book", player)
|
||||
and can_use_anvil(world, state, player)
|
||||
and can_enchant(world, state, player)
|
||||
and overworld_villager(world, state, player),
|
||||
"Hot Stuff": lambda state: state.has("Bucket", player)
|
||||
and has_iron_ingots(world, state, player),
|
||||
"Free the End": lambda state: can_respawn_ender_dragon(world, state, player)
|
||||
and can_kill_ender_dragon(world, state, player),
|
||||
"A Furious Cocktail": lambda state: (can_brew_potions(world, state, player)
|
||||
and state.has("Fishing Rod", player) # Water Breathing
|
||||
and state.can_reach_region("The Nether", player) # Regeneration, Fire Resistance, gold nuggets
|
||||
and state.can_reach_region("Village", player) # Night Vision, Invisibility
|
||||
and state.can_reach_location("Bring Home the Beacon", player)),
|
||||
# Resistance
|
||||
"Bring Home the Beacon": lambda state: can_kill_wither(world, state, player)
|
||||
and has_diamond_pickaxe(world, state, player)
|
||||
and state.has("Progressive Resource Crafting", player, 2),
|
||||
"Not Today, Thank You": lambda state: state.has("Shield", player)
|
||||
and has_iron_ingots(world, state, player),
|
||||
"Isn't It Iron Pick": lambda state: state.has("Progressive Tools", player, 2)
|
||||
and has_iron_ingots(world, state, player),
|
||||
"Local Brewery": lambda state: can_brew_potions(world, state, player),
|
||||
"The Next Generation": lambda state: can_respawn_ender_dragon(world, state, player)
|
||||
and can_kill_ender_dragon(world, state, player),
|
||||
"Fishy Business": lambda state: state.has("Fishing Rod", player),
|
||||
"This Boat Has Legs": lambda state: (
|
||||
fortress_loot(world, state, player)
|
||||
or complete_raid(world, 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(world, state, player),
|
||||
"How Did We Get Here?": lambda state: (can_brew_potions(world, state, player)
|
||||
and has_gold_ingots(world, state, player) # Absorption
|
||||
and state.can_reach_region('End City', player) # Levitation
|
||||
and state.can_reach_region('The Nether', player) # potion ingredients
|
||||
and state.has("Fishing Rod", player) # Pufferfish, Nautilus Shells; spectral arrows
|
||||
and state.has("Archery", player)
|
||||
and state.can_reach_location("Bring Home the Beacon", player) # Haste
|
||||
and state.can_reach_location("Hero of the Village", 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(world, state, player),
|
||||
"Spooky Scary Skeleton": lambda state: basic_combat(world, state, player),
|
||||
"Two by Two": lambda state: has_iron_ingots(world, state, player)
|
||||
and state.has("Bucket", player)
|
||||
and can_adventure(world, state, player),
|
||||
"Two Birds, One Arrow": lambda state: craft_crossbow(world, state, player)
|
||||
and can_enchant(world, state, player),
|
||||
"Who's the Pillager Now?": lambda state: craft_crossbow(world, state, player),
|
||||
"Getting an Upgrade": lambda state: state.has("Progressive Tools", player),
|
||||
"Tactical Fishing": lambda state: state.has("Bucket", player)
|
||||
and has_iron_ingots(world, state, player),
|
||||
"Zombie Doctor": lambda state: can_brew_potions(world, state, player)
|
||||
and has_gold_ingots(world, state, player),
|
||||
"Ice Bucket Challenge": lambda state: has_diamond_pickaxe(world, state, player),
|
||||
"Into Fire": lambda state: basic_combat(world, state, player),
|
||||
"War Pigs": lambda state: basic_combat(world, state, player),
|
||||
"Take Aim": lambda state: state.has("Archery", player),
|
||||
"Total Beelocation": lambda state: state.has("Silk Touch Book", player)
|
||||
and can_use_anvil(world, state, player)
|
||||
and can_enchant(world, state, player),
|
||||
"Arbalistic": lambda state: (craft_crossbow(world, state, player)
|
||||
and state.has("Piercing IV Book", player)
|
||||
and can_use_anvil(world, state, player)
|
||||
and can_enchant(world, state, player)
|
||||
),
|
||||
"The End... Again...": lambda state: can_respawn_ender_dragon(world, state, player)
|
||||
and can_kill_ender_dragon(world, state, player),
|
||||
"Acquire Hardware": lambda state: has_iron_ingots(world, state, player),
|
||||
"Not Quite \"Nine\" Lives": lambda state: can_piglin_trade(world, 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(world, state, player),
|
||||
"Sky's the Limit": lambda state: basic_combat(world, state, player),
|
||||
"Hired Help": lambda state: state.has("Progressive Resource Crafting", player, 2)
|
||||
and has_iron_ingots(world, state, player),
|
||||
"Sweet Dreams": lambda state: state.has("Bed", player)
|
||||
or state.can_reach_region('Village', player),
|
||||
"You Need a Mint": lambda state: can_respawn_ender_dragon(world, state, player)
|
||||
and has_bottle(world, state, player),
|
||||
"Monsters Hunted": lambda state: (can_respawn_ender_dragon(world, state, player)
|
||||
and can_kill_ender_dragon(world, state, player)
|
||||
and can_kill_wither(world, state, player)
|
||||
and state.has("Fishing Rod", player)),
|
||||
"Enchanter": lambda state: can_enchant(world, state, player),
|
||||
"Voluntary Exile": lambda state: basic_combat(world, state, player),
|
||||
"Eye Spy": lambda state: enter_stronghold(world, state, player),
|
||||
"Serious Dedication": lambda state: (can_brew_potions(world, state, player)
|
||||
and state.has("Bed", player)
|
||||
and has_diamond_pickaxe(world, state, player)
|
||||
and has_gold_ingots(world, state, player)),
|
||||
"Postmortal": lambda state: complete_raid(world, state, player),
|
||||
"Adventuring Time": lambda state: can_adventure(world, state, player),
|
||||
"Hero of the Village": lambda state: complete_raid(world, state, player),
|
||||
"Hidden in the Depths": lambda state: can_brew_potions(world, state, player)
|
||||
and state.has("Bed", player)
|
||||
and has_diamond_pickaxe(world, state, player),
|
||||
"Beaconator": lambda state: (can_kill_wither(world, state, player)
|
||||
and has_diamond_pickaxe(world, state, player)
|
||||
and state.has("Progressive Resource Crafting", player, 2)),
|
||||
"Withering Heights": lambda state: can_kill_wither(world, state, player),
|
||||
"A Balanced Diet": lambda state: (has_bottle(world, state, player)
|
||||
and has_gold_ingots(world, state, player)
|
||||
and state.has("Progressive Resource Crafting", player, 2)
|
||||
and state.can_reach_region('The End', player)),
|
||||
# notch apple, chorus fruit
|
||||
"Subspace Bubble": lambda state: has_diamond_pickaxe(world, state, player),
|
||||
"Country Lode, Take Me Home": lambda state: state.can_reach_location("Hidden in the Depths", player)
|
||||
and has_gold_ingots(world, state, player),
|
||||
"Bee Our Guest": lambda state: state.has("Campfire", player)
|
||||
and has_bottle(world, state, player),
|
||||
"Uneasy Alliance": lambda state: has_diamond_pickaxe(world, state, player)
|
||||
and state.has('Fishing Rod', player),
|
||||
"Diamonds!": lambda state: state.has("Progressive Tools", player, 2)
|
||||
and has_iron_ingots(world, state, player),
|
||||
"A Throwaway Joke": lambda state: can_adventure(world, state, player),
|
||||
"Sticky Situation": lambda state: state.has("Campfire", player)
|
||||
and has_bottle(world, state, player),
|
||||
"Ol' Betsy": lambda state: craft_crossbow(world, 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(world, state, player)
|
||||
and has_iron_ingots(world, state, player)
|
||||
and can_brew_potions(world, state, player)
|
||||
and state.has("Bed", player),
|
||||
"Hot Topic": lambda state: state.has("Progressive Resource Crafting", player),
|
||||
"The Lie": lambda state: has_iron_ingots(world, state, player)
|
||||
and state.has("Bucket", player),
|
||||
"On a Rail": lambda state: has_iron_ingots(world, state, player)
|
||||
and state.has('Progressive Tools', player, 2),
|
||||
"When Pigs Fly": lambda state: (
|
||||
fortress_loot(world, state, player)
|
||||
or complete_raid(world, state, player)
|
||||
)
|
||||
and state.has("Saddle", player)
|
||||
and state.has("Fishing Rod", player)
|
||||
and can_adventure(world, state, player),
|
||||
"Overkill": lambda state: can_brew_potions(world, state, player)
|
||||
and (
|
||||
state.has("Progressive Weapons", player)
|
||||
or state.can_reach_region('The Nether', player)
|
||||
),
|
||||
"Librarian": lambda state: state.has("Enchanting", player),
|
||||
"Overpowered": lambda state: has_iron_ingots(world, state, player)
|
||||
and state.has('Progressive Tools', player, 2)
|
||||
and basic_combat(world, state, player),
|
||||
"Wax On": lambda state: has_copper_ingots(world, state, player)
|
||||
and state.has('Campfire', player)
|
||||
and state.has('Progressive Resource Crafting', player, 2),
|
||||
"Wax Off": lambda state: has_copper_ingots(world, state, player)
|
||||
and state.has('Campfire', player)
|
||||
and state.has('Progressive Resource Crafting', player, 2),
|
||||
"The Cutest Predator": lambda state: has_iron_ingots(world, state, player)
|
||||
and state.has('Bucket', player),
|
||||
"The Healing Power of Friendship": lambda state: has_iron_ingots(world, state, player)
|
||||
and state.has('Bucket', player),
|
||||
"Is It a Bird?": lambda state: has_spyglass(world, state, player)
|
||||
and can_adventure(world, state, player),
|
||||
"Is It a Balloon?": lambda state: has_spyglass(world, state, player),
|
||||
"Is It a Plane?": lambda state: has_spyglass(world, state, player)
|
||||
and can_respawn_ender_dragon(world, state, player),
|
||||
"Surge Protector": lambda state: state.has("Channeling Book", player)
|
||||
and can_use_anvil(world, state, player)
|
||||
and can_enchant(world, state, player)
|
||||
and overworld_villager(world, state, player),
|
||||
"Light as a Rabbit": lambda state: can_adventure(world, state, player)
|
||||
and has_iron_ingots(world, state, player)
|
||||
and state.has('Bucket', player),
|
||||
"Glow and Behold!": lambda state: can_adventure(world, state, player),
|
||||
"Whatever Floats Your Goat!": lambda state: can_adventure(world, state, player),
|
||||
"Caves & Cliffs": lambda state: has_iron_ingots(world, state, player)
|
||||
and state.has('Bucket', player)
|
||||
and state.has('Progressive Tools', player, 2),
|
||||
"Feels like home": lambda state: has_iron_ingots(world, state, player)
|
||||
and state.has('Bucket', player)
|
||||
and state.has('Fishing Rod', player)
|
||||
and (
|
||||
fortress_loot(world, state, player)
|
||||
or complete_raid(world, state, player)
|
||||
)
|
||||
and state.has("Saddle", player),
|
||||
"Sound of Music": lambda state: state.has("Progressive Tools", player, 2)
|
||||
and has_iron_ingots(world, state, player)
|
||||
and basic_combat(world, state, player),
|
||||
"Star Trader": lambda state: has_iron_ingots(world, state, player)
|
||||
and state.has('Bucket', player)
|
||||
and (
|
||||
state.can_reach_region("The Nether", player) # soul sand in nether
|
||||
or state.can_reach_region("Nether Fortress", player) # soul sand in fortress if not in nether for water elevator
|
||||
or can_piglin_trade(world, state, player) # piglins give soul sand
|
||||
)
|
||||
and overworld_villager(world, state, player),
|
||||
"Birthday Song": lambda state: state.can_reach_location("The Lie", player)
|
||||
and state.has("Progressive Tools", player, 2)
|
||||
and has_iron_ingots(world, state, player),
|
||||
"Bukkit Bukkit": lambda state: state.has("Bucket", player)
|
||||
and has_iron_ingots(world, state, player)
|
||||
and can_adventure(world, state, player),
|
||||
"It Spreads": lambda state: can_adventure(world, state, player)
|
||||
and has_iron_ingots(world, state, player)
|
||||
and state.has("Progressive Tools", player, 2),
|
||||
"Sneak 100": lambda state: can_adventure(world, state, player)
|
||||
and has_iron_ingots(world, state, player)
|
||||
and state.has("Progressive Tools", player, 2),
|
||||
"When the Squad Hops into Town": lambda state: can_adventure(world, state, player)
|
||||
and state.has("Lead", player),
|
||||
"With Our Powers Combined!": lambda state: can_adventure(world, state, player)
|
||||
and state.has("Lead", player),
|
||||
}
|
||||
}
|
||||
return rules_lookup
|
||||
|
||||
|
||||
def set_rules(self: "MinecraftWorld") -> None:
|
||||
multiworld = self.multiworld
|
||||
player = self.player
|
||||
|
||||
rules_lookup = get_rules_lookup(self, player)
|
||||
|
||||
# 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 = self.options.required_bosses
|
||||
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) -> int:
|
||||
return len([location for location in multiworld.get_locations(player) if
|
||||
location.address is not 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(self.options.egg_shards_required.value, self.options.egg_shards_available.value)
|
||||
completion_requirements = lambda state: (location_count(state) >= self.options.advancement_goal
|
||||
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 self.options.include_hard_advancements:
|
||||
excluded_advancements.update(Constants.exclusion_info["hard"])
|
||||
if not self.options.include_unreasonable_advancements:
|
||||
excluded_advancements.update(Constants.exclusion_info["unreasonable"])
|
||||
if not self.options.include_postgame_advancements:
|
||||
excluded_advancements.update(postgame_advancements)
|
||||
exclusion_rules(multiworld, player, excluded_advancements)
|
@@ -1,59 +0,0 @@
|
||||
from . import Constants
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from . import MinecraftWorld
|
||||
|
||||
|
||||
def shuffle_structures(self: "MinecraftWorld") -> None:
|
||||
multiworld = self.multiworld
|
||||
player = self.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 is 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 self.options.plando_connections:
|
||||
for conn in self.options.plando_connections:
|
||||
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 self.options.shuffle_structures:
|
||||
structs.sort(reverse=True, key=lambda s: len(illegal_connections.get(s, [])))
|
||||
for struct in structs[:]:
|
||||
try:
|
||||
exit = self.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} ({self.player_name})")
|
||||
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} ({self.player_name})")
|
||||
|
||||
for exit in exits_spoiler:
|
||||
multiworld.get_entrance(exit, player).connect(multiworld.get_region(pairs[exit], player))
|
||||
if self.options.shuffle_structures or self.options.plando_connections:
|
||||
multiworld.spoiler.set_entrance(exit, pairs[exit], 'entrance', player)
|
@@ -1,203 +0,0 @@
|
||||
import os
|
||||
import json
|
||||
import settings
|
||||
import typing
|
||||
from base64 import b64encode, b64decode
|
||||
from typing import Dict, Any
|
||||
|
||||
from BaseClasses import Region, Entrance, Item, Tutorial, ItemClassification, Location
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
|
||||
from . import Constants
|
||||
from .Options import MinecraftOptions
|
||||
from .Structures import shuffle_structures
|
||||
from .ItemPool import build_item_pool, get_junk_item_names
|
||||
from .Rules import set_rules
|
||||
|
||||
client_version = 9
|
||||
|
||||
|
||||
class MinecraftSettings(settings.Group):
|
||||
class ForgeDirectory(settings.OptionalUserFolderPath):
|
||||
pass
|
||||
|
||||
class ReleaseChannel(str):
|
||||
"""
|
||||
release channel, currently "release", or "beta"
|
||||
any games played on the "beta" channel have a high likelihood of no longer working on the "release" channel.
|
||||
"""
|
||||
|
||||
class JavaExecutable(settings.OptionalUserFilePath):
|
||||
"""
|
||||
Path to Java executable. If not set, will attempt to fall back to Java system installation.
|
||||
"""
|
||||
|
||||
forge_directory: ForgeDirectory = ForgeDirectory("Minecraft NeoForge server")
|
||||
max_heap_size: str = "2G"
|
||||
release_channel: ReleaseChannel = ReleaseChannel("release")
|
||||
java: JavaExecutable = JavaExecutable("")
|
||||
|
||||
|
||||
class MinecraftWebWorld(WebWorld):
|
||||
theme = "jungle"
|
||||
bug_report_page = "https://github.com/KonoTyran/Minecraft_AP_Randomizer/issues/new?assignees=&labels=bug&template=bug_report.yaml&title=%5BBug%5D%3A+Brief+Description+of+bug+here"
|
||||
|
||||
setup = Tutorial(
|
||||
"Multiworld Setup Guide",
|
||||
"A guide to setting up the Archipelago Minecraft software on your computer. This guide covers"
|
||||
"single-player, multiworld, and related software.",
|
||||
"English",
|
||||
"minecraft_en.md",
|
||||
"minecraft/en",
|
||||
["Kono Tyran"]
|
||||
)
|
||||
|
||||
setup_es = Tutorial(
|
||||
setup.tutorial_name,
|
||||
setup.description,
|
||||
"Español",
|
||||
"minecraft_es.md",
|
||||
"minecraft/es",
|
||||
["Edos"]
|
||||
)
|
||||
|
||||
setup_sv = Tutorial(
|
||||
setup.tutorial_name,
|
||||
setup.description,
|
||||
"Swedish",
|
||||
"minecraft_sv.md",
|
||||
"minecraft/sv",
|
||||
["Albinum"]
|
||||
)
|
||||
|
||||
setup_fr = Tutorial(
|
||||
setup.tutorial_name,
|
||||
setup.description,
|
||||
"Français",
|
||||
"minecraft_fr.md",
|
||||
"minecraft/fr",
|
||||
["TheLynk"]
|
||||
)
|
||||
|
||||
tutorials = [setup, setup_es, setup_sv, setup_fr]
|
||||
|
||||
|
||||
class MinecraftWorld(World):
|
||||
"""
|
||||
Minecraft is a game about creativity. In a world made entirely of cubes, you explore, discover, mine,
|
||||
craft, and try not to explode. Delve deep into the earth and discover abandoned mines, ancient
|
||||
structures, and materials to create a portal to another world. Defeat the Ender Dragon, and claim
|
||||
victory!
|
||||
"""
|
||||
game = "Minecraft"
|
||||
options_dataclass = MinecraftOptions
|
||||
options: MinecraftOptions
|
||||
settings: typing.ClassVar[MinecraftSettings]
|
||||
topology_present = True
|
||||
web = MinecraftWebWorld()
|
||||
|
||||
item_name_to_id = Constants.item_name_to_id
|
||||
location_name_to_id = Constants.location_name_to_id
|
||||
|
||||
def _get_mc_data(self) -> Dict[str, Any]:
|
||||
exits = [connection[0] for connection in Constants.region_info["default_connections"]]
|
||||
return {
|
||||
'world_seed': self.random.getrandbits(32),
|
||||
'seed_name': self.multiworld.seed_name,
|
||||
'player_name': self.player_name,
|
||||
'player_id': self.player,
|
||||
'client_version': client_version,
|
||||
'structures': {exit: self.multiworld.get_entrance(exit, self.player).connected_region.name for exit in exits},
|
||||
'advancement_goal': self.options.advancement_goal.value,
|
||||
'egg_shards_required': min(self.options.egg_shards_required.value,
|
||||
self.options.egg_shards_available.value),
|
||||
'egg_shards_available': self.options.egg_shards_available.value,
|
||||
'required_bosses': self.options.required_bosses.current_key,
|
||||
'MC35': bool(self.options.send_defeated_mobs.value),
|
||||
'death_link': bool(self.options.death_link.value),
|
||||
'starting_items': json.dumps(self.options.starting_items.value),
|
||||
'race': self.multiworld.is_race,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
return MinecraftItem(name, item_class, self.item_name_to_id.get(name, None), self.player)
|
||||
|
||||
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)
|
||||
|
||||
def create_event_item(self, name: str) -> Item:
|
||||
item = self.create_item(name)
|
||||
item.classification = ItemClassification.progression
|
||||
return item
|
||||
|
||||
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)
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
# Add events
|
||||
self.create_event("Nether Fortress", "Blaze Rods")
|
||||
self.create_event("The End", "Ender Dragon")
|
||||
self.create_event("Nether Fortress", "Wither")
|
||||
|
||||
# Shuffle the connections
|
||||
shuffle_structures(self)
|
||||
|
||||
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"{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) -> dict:
|
||||
return self._get_mc_data()
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return get_junk_item_names(self.random, 1)[0]
|
||||
|
||||
|
||||
class MinecraftLocation(Location):
|
||||
game = "Minecraft"
|
||||
|
||||
class MinecraftItem(Item):
|
||||
game = "Minecraft"
|
||||
|
||||
|
||||
def mc_update_output(raw_data, server, port):
|
||||
data = json.loads(b64decode(raw_data))
|
||||
data['server'] = server
|
||||
data['port'] = port
|
||||
return b64encode(bytes(json.dumps(data), 'utf-8'))
|
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
@@ -1,128 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
@@ -1,250 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"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,113 +0,0 @@
|
||||
# Minecraft
|
||||
|
||||
## Where is the options page?
|
||||
|
||||
The [player options page for this game](../player-options) contains all the options you need to configure and export a
|
||||
config file.
|
||||
|
||||
## What does randomization do to this game?
|
||||
|
||||
Some recipes are locked from being able to be crafted and shuffled into the item pool. It can also optionally change which
|
||||
structures appear in each dimension. Crafting recipes are re-learned when they are received from other players as item
|
||||
checks, and occasionally when completing your own achievements. See below for which recipes are shuffled.
|
||||
|
||||
## What is considered a location check in Minecraft?
|
||||
|
||||
Location checks are completed when the player completes various Minecraft achievements. Opening the advancements menu
|
||||
in-game by pressing "L" will display outstanding achievements.
|
||||
|
||||
## When the player receives an item, what happens?
|
||||
|
||||
When the player receives an item in Minecraft, it either unlocks crafting recipes or puts items into the player's
|
||||
inventory directly.
|
||||
|
||||
## What is the victory condition?
|
||||
|
||||
Victory is achieved when the player kills the Ender Dragon, enters the portal in The End, and completes the credits
|
||||
sequence either by skipping it or watching it play out.
|
||||
|
||||
## Which recipes are locked?
|
||||
|
||||
* Archery
|
||||
* Bow
|
||||
* Arrow
|
||||
* Crossbow
|
||||
* Brewing
|
||||
* Blaze Powder
|
||||
* Brewing Stand
|
||||
* Enchanting
|
||||
* Enchanting Table
|
||||
* Bookshelf
|
||||
* Bucket
|
||||
* Flint & Steel
|
||||
* All Beds
|
||||
* Bottles
|
||||
* Shield
|
||||
* Fishing Rod
|
||||
* Fishing Rod
|
||||
* Carrot on a Stick
|
||||
* Warped Fungus on a Stick
|
||||
* Campfire
|
||||
* Campfire
|
||||
* Soul Campfire
|
||||
* Spyglass
|
||||
* Lead
|
||||
* Progressive Weapons
|
||||
* Tier I
|
||||
* Stone Sword
|
||||
* Stone Axe
|
||||
* Tier II
|
||||
* Iron Sword
|
||||
* Iron Axe
|
||||
* Tier III
|
||||
* Diamond Sword
|
||||
* Diamond Axe
|
||||
* Progessive Tools
|
||||
* Tier I
|
||||
* Stone Pickaxe
|
||||
* Stone Shovel
|
||||
* Stone Hoe
|
||||
* Tier II
|
||||
* Iron Pickaxe
|
||||
* Iron Shovel
|
||||
* Iron Hoe
|
||||
* Tier III
|
||||
* Diamond Pickaxe
|
||||
* Diamond Shovel
|
||||
* Diamond Hoe
|
||||
* Netherite Ingot
|
||||
* Progressive Armor
|
||||
* Tier I
|
||||
* Iron Helmet
|
||||
* Iron Chestplate
|
||||
* Iron Leggings
|
||||
* Iron Boots
|
||||
* Tier II
|
||||
* Diamond Helmet
|
||||
* Diamond Chestplate
|
||||
* Diamond Leggings
|
||||
* Diamond Boots
|
||||
* Progressive Resource Crafting
|
||||
* Tier I
|
||||
* Iron Ingot from Nuggets
|
||||
* Iron Nugget
|
||||
* Gold Ingot from Nuggets
|
||||
* Gold Nugget
|
||||
* Furnace
|
||||
* Blast Furnace
|
||||
* Tier II
|
||||
* Redstone
|
||||
* Redstone Block
|
||||
* Glowstone
|
||||
* Iron Ingot from Iron Block
|
||||
* Iron Block
|
||||
* Gold Ingot from Gold Block
|
||||
* Gold Block
|
||||
* Diamond
|
||||
* Diamond Block
|
||||
* Netherite Block
|
||||
* Netherite Ingot from Netherite Block
|
||||
* Anvil
|
||||
* Emerald
|
||||
* Emerald Block
|
||||
* Copper Block
|
@@ -1,74 +0,0 @@
|
||||
# Minecraft Randomizer Setup Guide
|
||||
|
||||
## Required Software
|
||||
|
||||
- Minecraft Java Edition from
|
||||
the [Minecraft Java Edition Store Page](https://www.minecraft.net/en-us/store/minecraft-java-edition)
|
||||
- Archipelago from the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases)
|
||||
|
||||
## Configuring your YAML file
|
||||
|
||||
### What is a YAML file and why do I need one?
|
||||
|
||||
See the guide on setting up a basic YAML at the Archipelago setup
|
||||
guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
|
||||
|
||||
### Where do I get a YAML file?
|
||||
|
||||
You can customize your options by visiting the [Minecraft Player Options Page](/games/Minecraft/player-options)
|
||||
|
||||
## Joining a MultiWorld Game
|
||||
|
||||
### Obtain Your Minecraft Data File
|
||||
|
||||
**Only one yaml file needs to be submitted per minecraft world regardless of how many players play on it.**
|
||||
|
||||
When you join a multiworld game, you will be asked to provide your YAML file to whoever is hosting. Once that is done,
|
||||
the host will provide you with either a link to download your data file, or with a zip file containing everyone's data
|
||||
files. Your data file should have a `.apmc` extension.
|
||||
|
||||
Double-click on your `.apmc` file to have the Minecraft client auto-launch the installed forge server. Make sure to
|
||||
leave this window open as this is your server console.
|
||||
|
||||
### Connect to the MultiServer
|
||||
|
||||
Open Minecraft, go to `Multiplayer > Direct Connection`, and join the `localhost` server address.
|
||||
|
||||
If you are using the website to host the game then it should auto-connect to the AP server without the need to `/connect`
|
||||
|
||||
otherwise once you are in game type `/connect <AP-Address> (Port) (Password)` where `<AP-Address>` is the address of the
|
||||
Archipelago server. `(Port)` is only required if the Archipelago server is not using the default port of 38281. Note that there is no colon between `<AP-Address>` and `(Port)`.
|
||||
`(Password)` is only required if the Archipelago server you are using has a password set.
|
||||
|
||||
### Play the game
|
||||
|
||||
When the console tells you that you have joined the room, you're all set. Congratulations on successfully joining a
|
||||
multiworld game! At this point any additional minecraft players may connect to your forge server. To start the game once
|
||||
everyone is ready use the command `/start`.
|
||||
|
||||
## Non-Windows Installation
|
||||
|
||||
The Minecraft Client will install forge and the mod for other operating systems but Java has to be provided by the
|
||||
user. Head to [minecraft_versions.json on the MC AP GitHub](https://raw.githubusercontent.com/KonoTyran/Minecraft_AP_Randomizer/master/versions/minecraft_versions.json)
|
||||
to see which java version is required. New installations will default to the topmost "release" version.
|
||||
- Install the matching Amazon Corretto JDK
|
||||
- see [Manual Installation Software Links](#manual-installation-software-links)
|
||||
- or package manager provided by your OS / distribution
|
||||
- Open your `host.yaml` and add the path to your Java below the `minecraft_options` key
|
||||
- ` java: "path/to/java-xx-amazon-corretto/bin/java"`
|
||||
- Run the Minecraft Client and select your .apmc file
|
||||
|
||||
## Full Manual Installation
|
||||
|
||||
It is highly recommended to ues the Archipelago installer to handle the installation of the forge server for you.
|
||||
Support will not be given for those wishing to manually install forge. For those of you who know how, and wish to do so,
|
||||
the following links are the versions of the software we use.
|
||||
|
||||
### Manual Installation Software Links
|
||||
|
||||
- [Minecraft Forge Download Page](https://files.minecraftforge.net/net/minecraftforge/forge/)
|
||||
- [Minecraft Archipelago Randomizer Mod Releases Page](https://github.com/KonoTyran/Minecraft_AP_Randomizer/releases)
|
||||
- **DO NOT INSTALL THIS ON YOUR CLIENT**
|
||||
- [Amazon Corretto](https://docs.aws.amazon.com/corretto/)
|
||||
- pick the matching version and select "Downloads" on the left
|
||||
|
@@ -1,148 +0,0 @@
|
||||
# Guia instalación de Minecraft Randomizer
|
||||
|
||||
# Instalacion automatica para el huesped de partida
|
||||
|
||||
- descarga e instala [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) and activa el
|
||||
modulo `Minecraft Client`
|
||||
|
||||
## Software Requerido
|
||||
|
||||
- [Minecraft Java Edition](https://www.minecraft.net/en-us/store/minecraft-java-edition)
|
||||
|
||||
## Configura tu fichero YAML
|
||||
|
||||
### Que es un fichero YAML y potque necesito uno?
|
||||
|
||||
Tu fichero YAML contiene un numero de opciones que proveen al generador con informacion sobre como debe generar tu
|
||||
juego. Cada jugador de un multiworld entregara u propio fichero YAML. Esto permite que cada jugador disfrute de una
|
||||
experiencia personalizada a su gusto y diferentes jugadores dentro del mismo multiworld pueden tener diferentes opciones
|
||||
|
||||
### Where do I get a YAML file?
|
||||
|
||||
Un fichero basico yaml para minecraft tendra este aspecto.
|
||||
|
||||
```yaml
|
||||
description: Basic Minecraft Yaml
|
||||
# Tu nombre en el juego. Espacios seran sustituidos por guinoes bajos y
|
||||
# hay un limite de 16 caracteres
|
||||
name: TuNombre
|
||||
game: Minecraft
|
||||
|
||||
# Opciones compartidas por todos los juegos:
|
||||
accessibility: full
|
||||
progression_balancing: 50
|
||||
# Opciones Especficicas para Minecraft
|
||||
|
||||
Minecraft:
|
||||
# Numero de logros requeridos (87 max) para que aparezca el Ender Dragon y completar el juego.
|
||||
advancement_goal: 50
|
||||
|
||||
# Numero de trozos de huevo de dragon a obtener (30 max) antes de que el Ender Dragon aparezca.
|
||||
egg_shards_required: 10
|
||||
|
||||
# Numero de huevos disponibles en la partida (30 max).
|
||||
egg_shards_available: 15
|
||||
|
||||
# Modifica el nivel de objetos logicamente requeridos para
|
||||
# explorar areas peligrosas y luchar contra jefes.
|
||||
combat_difficulty:
|
||||
easy: 0
|
||||
normal: 1
|
||||
hard: 0
|
||||
|
||||
# Si off, los logros que dependan de suerte o sean tediosos tendran objetos de apoyo, no necesarios para completar el juego.
|
||||
include_hard_advancements:
|
||||
on: 0
|
||||
off: 1
|
||||
|
||||
# Si off, los logros muy dificiles tendran objetos de apoyo, no necesarios para completar el juego.
|
||||
# Solo afecta a How Did We Get Here? and Adventuring Time.
|
||||
include_insane_advancements:
|
||||
on: 0
|
||||
off: 1
|
||||
|
||||
# Algunos logros requieren derrotar al Ender Dragon;
|
||||
# Si esto se queda en off, dichos logros no tendran objetos necesarios.
|
||||
include_postgame_advancements:
|
||||
on: 0
|
||||
off: 1
|
||||
|
||||
# Permite el mezclado de villas, puesto, fortalezas, bastiones y ciudades de END.
|
||||
shuffle_structures:
|
||||
on: 0
|
||||
off: 1
|
||||
|
||||
# Añade brujulas de estructura al juego,
|
||||
# apuntaran a la estructura correspondiente mas cercana.
|
||||
structure_compasses:
|
||||
on: 0
|
||||
off: 1
|
||||
|
||||
# Reemplaza un porcentaje de objetos innecesarios por trampas abeja
|
||||
# las cuales crearan multiples abejas agresivas alrededor de los jugadores cuando se reciba.
|
||||
bee_traps:
|
||||
0: 1
|
||||
25: 0
|
||||
50: 0
|
||||
75: 0
|
||||
100: 0
|
||||
```
|
||||
|
||||
## Unirse a un juego MultiWorld
|
||||
|
||||
### Obten tu ficheros de datos Minecraft
|
||||
|
||||
**Solo un fichero yaml es necesario por mundo minecraft, sin importar el numero de jugadores que jueguen en el.**
|
||||
|
||||
Cuando te unes a un juego multiworld, se te pedirá que entregues tu fichero YAML a quien sea que hospede el juego
|
||||
multiworld (no confundir con hospedar el mundo minecraft). Una vez la generación acabe, el anfitrión te dará un enlace a
|
||||
tu fichero de datos o un zip con los ficheros de todos. Tu fichero de datos tiene una extensión `.apmc`.
|
||||
|
||||
Haz doble click en tu fichero `.apmc` para que se arranque el cliente de minecraft y el servidor forge se ejecute.
|
||||
|
||||
### Conectar al multiserver
|
||||
|
||||
Despues de poner tu fichero en el directorio `APData`, arranca el Forge server y asegurate que tienes el estado OP
|
||||
tecleando `/op TuUsuarioMinecraft` en la consola del servidor y entonces conectate con tu cliente Minecraft.
|
||||
|
||||
Una vez en juego introduce `/connect <AP-Address> (Port) (<Password>)` donde `<AP-Address>` es la dirección del
|
||||
servidor. `(Port)` solo es requerido si el servidor Archipelago no esta usando el puerto por defecto 38281.
|
||||
`(<Password>)`
|
||||
solo se necesita si el servidor Archipleago tiene un password activo.
|
||||
|
||||
### Jugar al juego
|
||||
|
||||
Cuando la consola te diga que te has unido a la sala, estas lista/o para empezar a jugar. Felicidades por unirte
|
||||
exitosamente a un juego multiworld! Llegados a este punto cualquier jugador adicional puede conectarse a tu servidor
|
||||
forge.
|
||||
|
||||
## Procedimiento de instalación manual
|
||||
|
||||
Solo es requerido si quieres usar una instalacion de forge por ti mismo, recomendamos usar el instalador de Archipelago
|
||||
|
||||
### Software Requerido
|
||||
|
||||
- [Minecraft Forge](https://files.minecraftforge.net/net/minecraftforge/forge/index_1.16.5.html)
|
||||
- [Minecraft Archipelago Randomizer Mod](https://github.com/KonoTyran/Minecraft_AP_Randomizer/releases)
|
||||
**NO INSTALES ESTO EN TU CLIENTE MINECRAFT**
|
||||
|
||||
### Instalación de servidor dedicado
|
||||
|
||||
Solo una persona ha de realizar este proceso y hospedar un servidor dedicado para que los demas jueguen conectandose a
|
||||
él.
|
||||
|
||||
1. Descarga el instalador de **Minecraft Forge** 1.16.5 desde el enlace proporcionado, siempre asegurandose de bajar la
|
||||
version mas reciente.
|
||||
|
||||
2. Ejecuta el fichero `forge-1.16.5-xx.x.x-installer.jar` y elije **install server**.
|
||||
- En esta pagina elegiras ademas donde instalar el servidor, importante recordar esta localización en el siguiente
|
||||
paso.
|
||||
|
||||
3. Navega al directorio donde hayas instalado el servidor y abre `forge-1.16.5-xx.x.x.jar`
|
||||
- La primera vez que lances el servidor se cerrara (o no aparecerá nada en absoluto), debería haber un fichero nuevo
|
||||
en el directorio llamado `eula.txt`, el cual que contiene un enlace al EULA de minecraft, cambia la linea
|
||||
a `eula=true` para aceptar el EULA y poder utilizar el software de servidor.
|
||||
- Esto creara la estructura de directorios apropiada para el siguiente paso
|
||||
|
||||
4. Coloca el fichero `aprandomizer-x.x.x.jar` del segundo enlace en el directorio `mods`
|
||||
- Cuando se ejecute el servidor de nuevo, generara el directorio `APData` que se necesitara para jugar
|
@@ -1,74 +0,0 @@
|
||||
# Guide de configuration du randomiseur Minecraft
|
||||
|
||||
## Logiciel requis
|
||||
|
||||
- Minecraft Java Edition à partir de
|
||||
la [page de la boutique Minecraft Java Edition](https://www.minecraft.net/en-us/store/minecraft-java-edition)
|
||||
- Archipelago depuis la [page des versions d'Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases)
|
||||
- (sélectionnez `Minecraft Client` lors de l'installation.)
|
||||
|
||||
## Configuration de votre fichier YAML
|
||||
|
||||
### Qu'est-ce qu'un fichier YAML et pourquoi en ai-je besoin ?
|
||||
|
||||
Voir le guide sur la configuration d'un YAML de base lors de la configuration d'Archipelago
|
||||
guide : [Guide de configuration de base de Multiworld](/tutorial/Archipelago/setup/en)
|
||||
|
||||
### Où puis-je obtenir un fichier YAML ?
|
||||
|
||||
Vous pouvez personnaliser vos paramètres Minecraft en allant sur la [page des paramètres de joueur](/games/Minecraft/player-options)
|
||||
|
||||
## Rejoindre une partie MultiWorld
|
||||
|
||||
### Obtenez votre fichier de données Minecraft
|
||||
|
||||
**Un seul fichier yaml doit être soumis par monde minecraft, quel que soit le nombre de joueurs qui y jouent.**
|
||||
|
||||
Lorsque vous rejoignez un jeu multimonde, il vous sera demandé de fournir votre fichier YAML à l'hébergeur. Une fois cela fait,
|
||||
l'hébergeur vous fournira soit un lien pour télécharger votre fichier de données, soit un fichier zip contenant les données de chacun
|
||||
des dossiers. Votre fichier de données doit avoir une extension `.apmc`.
|
||||
|
||||
Double-cliquez sur votre fichier `.apmc` pour que le client Minecraft lance automatiquement le serveur forge installé. Assurez-vous de
|
||||
laissez cette fenêtre ouverte car il s'agit de votre console serveur.
|
||||
|
||||
### Connectez-vous au multiserveur
|
||||
|
||||
Ouvrez Minecraft, accédez à "Multijoueur> Connexion directe" et rejoignez l'adresse du serveur "localhost".
|
||||
|
||||
Si vous utilisez le site Web pour héberger le jeu, il devrait se connecter automatiquement au serveur AP sans avoir besoin de `/connect`
|
||||
|
||||
sinon, une fois que vous êtes dans le jeu, tapez `/connect <AP-Address> (Port) (Password)` où `<AP-Address>` est l'adresse du
|
||||
Serveur Archipelago. `(Port)` n'est requis que si le serveur Archipelago n'utilise pas le port par défaut 38281. Notez qu'il n'y a pas de deux-points entre `<AP-Address>` et `(Port)` mais un espace.
|
||||
`(Mot de passe)` n'est requis que si le serveur Archipelago que vous utilisez a un mot de passe défini.
|
||||
|
||||
### Jouer le jeu
|
||||
|
||||
Lorsque la console vous indique que vous avez rejoint la salle, vous êtes prêt. Félicitations pour avoir rejoint avec succès un
|
||||
jeu multimonde ! À ce stade, tous les joueurs minecraft supplémentaires peuvent se connecter à votre serveur forge. Pour commencer le jeu une fois
|
||||
que tout le monde est prêt utilisez la commande `/start`.
|
||||
|
||||
## Installation non Windows
|
||||
|
||||
Le client Minecraft installera forge et le mod pour d'autres systèmes d'exploitation, mais Java doit être fourni par l'
|
||||
utilisateur. Rendez-vous sur [minecraft_versions.json sur le MC AP GitHub](https://raw.githubusercontent.com/KonoTyran/Minecraft_AP_Randomizer/master/versions/minecraft_versions.json)
|
||||
pour voir quelle version de Java est requise. Les nouvelles installations utiliseront par défaut la version "release" la plus élevée.
|
||||
- Installez le JDK Amazon Corretto correspondant
|
||||
- voir les [Liens d'installation manuelle du logiciel](#manual-installation-software-links)
|
||||
- ou gestionnaire de paquets fourni par votre OS/distribution
|
||||
- Ouvrez votre `host.yaml` et ajoutez le chemin vers votre Java sous la clé `minecraft_options`
|
||||
- ` java : "chemin/vers/java-xx-amazon-corretto/bin/java"`
|
||||
- Exécutez le client Minecraft et sélectionnez votre fichier .apmc
|
||||
|
||||
## Installation manuelle complète
|
||||
|
||||
Il est fortement recommandé d'utiliser le programme d'installation d'Archipelago pour gérer l'installation du serveur forge pour vous.
|
||||
Le support ne sera pas fourni pour ceux qui souhaitent installer manuellement forge. Pour ceux d'entre vous qui savent comment faire et qui souhaitent le faire,
|
||||
les liens suivants sont les versions des logiciels que nous utilisons.
|
||||
|
||||
### Liens d'installation manuelle du logiciel
|
||||
|
||||
- [Page de téléchargement de Minecraft Forge] (https://files.minecraftforge.net/net/minecraftforge/forge/)
|
||||
- [Page des versions du mod Minecraft Archipelago Randomizer] (https://github.com/KonoTyran/Minecraft_AP_Randomizer/releases)
|
||||
- **NE PAS INSTALLER CECI SUR VOTRE CLIENT**
|
||||
- [Amazon Corretto](https://docs.aws.amazon.com/corretto/)
|
||||
- choisissez la version correspondante et sélectionnez "Téléchargements" sur la gauche
|
@@ -1,132 +0,0 @@
|
||||
# Minecraft Randomizer Uppsättningsguide
|
||||
|
||||
## Nödvändig Mjukvara
|
||||
|
||||
### Server Värd
|
||||
|
||||
- [Minecraft Forge](https://files.minecraftforge.net/net/minecraftforge/forge/index_1.16.5.html)
|
||||
- [Minecraft Archipelago Randomizer Mod](https://github.com/KonoTyran/Minecraft_AP_Randomizer/releases)
|
||||
|
||||
### Spelare
|
||||
|
||||
- [Minecraft Java Edition](https://www.minecraft.net/en-us/store/minecraft-java-edition)
|
||||
|
||||
## Installationsprocedurer
|
||||
|
||||
### Tillägnad
|
||||
|
||||
Bara en person behöver göra denna uppsättning och vara värd för en server för alla andra spelare att koppla till.
|
||||
|
||||
1. Ladda ner 1.16.5 **Minecraft Forge** installeraren från länken ovanför och se till att ladda ner den senaste
|
||||
rekommenderade versionen.
|
||||
|
||||
2. Kör `forge-1.16.5-xx.x.x-installer.jar` filen och välj **installera server**.
|
||||
- På denna sida kommer du också välja vart du ska installera servern för att komma ihåg denna katalog. Detta är
|
||||
viktigt för nästa steg.
|
||||
|
||||
3. Navigera till vart du har installerat servern och öppna `forge-1.16.5-xx.x.x-installer.jar`
|
||||
- Under första serverstart så kommer den att stängas ner och fråga dig att acceptera Minecrafts EULA. En ny fil
|
||||
kommer skapas vid namn `eula.txt` som har en länk till Minecrafts EULA, och en linje som du behöver byta
|
||||
till `eula=true` för att acceptera Minecrafts EULA.
|
||||
- Detta kommer skapa de lämpliga katalogerna för dig att placera filerna i de följande steget.
|
||||
|
||||
4. Placera `aprandomizer-x.x.x.jar` länken ovanför i `mods` mappen som ligger ovanför installationen av din forge
|
||||
server.
|
||||
- Kör servern igen. Den kommer ladda up och generera den nödvändiga katalogen `APData` för när du är redo att spela!
|
||||
|
||||
### Grundläggande Spelaruppsättning
|
||||
|
||||
- Köp och installera Minecraft från länken ovanför.
|
||||
|
||||
**Du är klar**.
|
||||
|
||||
Andra spelare behöver endast ha en 'Vanilla' omodifierad version av Minecraft för att kunna spela!
|
||||
|
||||
### Avancerad Spelaruppsättning
|
||||
|
||||
***Detta är inte nödvändigt för att spela ett slumpmässigt Minecraftspel.***
|
||||
Dock så är det rekommenderat eftersom det hjälper att göra upplevelsen mer trevligt.
|
||||
|
||||
#### Rekommenderade Moddar
|
||||
|
||||
- [JourneyMap](https://www.curseforge.com/minecraft/mc-mods/journeymap) (Minimap)
|
||||
|
||||
|
||||
1. Installera och Kör Minecraft från länken ovanför minst en gång.
|
||||
2. Kör `forge-1.16.5-xx.x.x-installer.jar` filen och välj **installera klient**.
|
||||
- Starta Minecraft Forge minst en gång för att skapa katalogerna som behövs för de nästa stegen.
|
||||
3. Navigera till din Minecraft installationskatalog och placera de önskade moddarna med `.jar` i `mods` -katalogen.
|
||||
- Standardinstallationskatalogerna är som följande;
|
||||
- Windows `%APPDATA%\.minecraft\mods`
|
||||
- macOS `~/Library/Application Support/minecraft/mods`
|
||||
- Linux `~/.minecraft/mods`
|
||||
|
||||
## Konfigurera Din YAML-fil
|
||||
|
||||
### Vad är en YAML-fil och varför behöver jag en?
|
||||
|
||||
Din YAML-fil behåller en uppsättning av konfigurationsalternativ som ger generatorn med information om hur den borde
|
||||
generera ditt spel. Varje spelare i en multivärld kommer behöva ge deras egen YAML-fil. Denna uppsättning tillåter varje
|
||||
spelare att an njuta av en upplevelse anpassade för deras smaker, och olika spelare i samma multivärld kan ha helt olika
|
||||
alternativ.
|
||||
|
||||
### Vart kan jag få tag i en YAML-fil?
|
||||
|
||||
En grundläggande Minecraft YAML kommer se ut så här.
|
||||
|
||||
```yaml
|
||||
description: Template Name
|
||||
# Ditt spelnamn. Mellanslag kommer bli omplacerad med understräck och det är en 16-karaktärsgräns.
|
||||
name: YourName
|
||||
game: Minecraft
|
||||
accessibility: full
|
||||
progression_balancing: 0
|
||||
advancement_goal:
|
||||
few: 0
|
||||
normal: 1
|
||||
many: 0
|
||||
combat_difficulty:
|
||||
easy: 0
|
||||
normal: 1
|
||||
hard: 0
|
||||
include_hard_advancements:
|
||||
on: 0
|
||||
off: 1
|
||||
include_insane_advancements:
|
||||
on: 0
|
||||
off: 1
|
||||
include_postgame_advancements:
|
||||
on: 0
|
||||
off: 1
|
||||
shuffle_structures:
|
||||
on: 1
|
||||
off: 0
|
||||
```
|
||||
|
||||
|
||||
## Gå med i ett Multivärld-spel
|
||||
|
||||
### Skaffa din Minecraft data-fil
|
||||
|
||||
**Endast en YAML-fil behöver användats per Minecraft-värld oavsett hur många spelare det är som spelar.**
|
||||
|
||||
När du går med it ett Multivärld spel så kommer du bli ombedd att lämna in din YAML-fil till personen som värdar. När
|
||||
detta är klart så kommer värden att ge dig antingen en länk till att ladda ner din data-fil, eller mer en zip-fil som
|
||||
innehåller allas data-filer. Din data-fil borde ha en `.apmc` -extension.
|
||||
|
||||
Lägg din data-fil i dina forge-servrar `APData` -mapp. Se till att ta bort alla tidigare data-filer som var i där förut.
|
||||
|
||||
### Koppla till Multiservern
|
||||
|
||||
Efter du har placerat din data-fil i `APData` -mappen, starta forge-servern och se till att you har OP-status genom att
|
||||
skriva `/op DittAnvändarnamn` i forger-serverns konsol innan du kopplar dig till din Minecraft klient. När du är inne i
|
||||
spelet, skriv `/connect <AP-Address> (<Lösenord>)` där `<AP-Address>` är addressen av
|
||||
Archipelago-servern. `(<Lösenord>)` är endast nödvändigt om Archipelago-servern som du använder har ett tillsatt
|
||||
lösenord.
|
||||
|
||||
### Spela spelet
|
||||
|
||||
När konsolen har informerat att du har gått med i rummet så är du redo att börja spela. Grattis att du har lykats med
|
||||
att gått med i ett Multivärld-spel! Vid detta tillfälle, alla ytterligare Minecraft-spelare må koppla in till din
|
||||
forge-server.
|
||||
|
@@ -1 +0,0 @@
|
||||
requests >= 2.28.1 # used by client
|
File diff suppressed because it is too large
Load Diff
@@ -1,60 +0,0 @@
|
||||
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,97 +0,0 @@
|
||||
from . import MCTestBase
|
||||
|
||||
|
||||
class TestEntrances(MCTestBase):
|
||||
options = {
|
||||
"shuffle_structures": False,
|
||||
"structure_compasses": False
|
||||
}
|
||||
|
||||
def testPortals(self):
|
||||
self.run_entrance_tests([
|
||||
['Nether Portal', False, []],
|
||||
['Nether Portal', False, [], ['Flint and Steel']],
|
||||
['Nether Portal', False, [], ['Progressive Resource Crafting']],
|
||||
['Nether Portal', False, [], ['Progressive Tools']],
|
||||
['Nether Portal', False, ['Progressive Tools', 'Progressive Tools'], ['Bucket', 'Progressive Tools']],
|
||||
['Nether Portal', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Bucket']],
|
||||
['Nether Portal', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Progressive Tools', 'Progressive Tools']],
|
||||
|
||||
['End Portal', False, []],
|
||||
['End Portal', False, [], ['Brewing']],
|
||||
['End Portal', False, ['3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls'], ['3 Ender Pearls']],
|
||||
['End Portal', False, [], ['Flint and Steel']],
|
||||
['End Portal', False, [], ['Progressive Resource Crafting']],
|
||||
['End Portal', False, [], ['Progressive Tools']],
|
||||
['End Portal', False, ['Progressive Tools', 'Progressive Tools'], ['Bucket', 'Progressive Tools']],
|
||||
['End Portal', False, [], ['Progressive Weapons']],
|
||||
['End Portal', False, [], ['Progressive Armor', 'Shield']],
|
||||
['End Portal', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Bucket',
|
||||
'Progressive Weapons', 'Progressive Armor',
|
||||
'Brewing', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls']],
|
||||
['End Portal', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Bucket',
|
||||
'Progressive Weapons', 'Shield',
|
||||
'Brewing', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls']],
|
||||
['End Portal', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Progressive Tools', 'Progressive Tools',
|
||||
'Progressive Weapons', 'Progressive Armor',
|
||||
'Brewing', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls']],
|
||||
['End Portal', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Progressive Tools', 'Progressive Tools',
|
||||
'Progressive Weapons', 'Shield',
|
||||
'Brewing', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls']],
|
||||
])
|
||||
|
||||
def testStructures(self):
|
||||
self.run_entrance_tests([ # Structures 1 and 2 should be logically equivalent
|
||||
['Overworld Structure 1', False, []],
|
||||
['Overworld Structure 1', False, [], ['Progressive Weapons']],
|
||||
['Overworld Structure 1', False, [], ['Progressive Resource Crafting', 'Campfire']],
|
||||
['Overworld Structure 1', True, ['Progressive Weapons', 'Progressive Resource Crafting']],
|
||||
['Overworld Structure 1', True, ['Progressive Weapons', 'Campfire']],
|
||||
|
||||
['Overworld Structure 2', False, []],
|
||||
['Overworld Structure 2', False, [], ['Progressive Weapons']],
|
||||
['Overworld Structure 2', False, [], ['Progressive Resource Crafting', 'Campfire']],
|
||||
['Overworld Structure 2', True, ['Progressive Weapons', 'Progressive Resource Crafting']],
|
||||
['Overworld Structure 2', True, ['Progressive Weapons', 'Campfire']],
|
||||
|
||||
['Nether Structure 1', False, []],
|
||||
['Nether Structure 1', False, [], ['Flint and Steel']],
|
||||
['Nether Structure 1', False, [], ['Progressive Resource Crafting']],
|
||||
['Nether Structure 1', False, [], ['Progressive Tools']],
|
||||
['Nether Structure 1', False, ['Progressive Tools', 'Progressive Tools'], ['Bucket', 'Progressive Tools']],
|
||||
['Nether Structure 1', False, [], ['Progressive Weapons']],
|
||||
['Nether Structure 1', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Bucket', 'Progressive Weapons']],
|
||||
['Nether Structure 1', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Progressive Tools', 'Progressive Tools', 'Progressive Weapons']],
|
||||
|
||||
['Nether Structure 2', False, []],
|
||||
['Nether Structure 2', False, [], ['Flint and Steel']],
|
||||
['Nether Structure 2', False, [], ['Progressive Resource Crafting']],
|
||||
['Nether Structure 2', False, [], ['Progressive Tools']],
|
||||
['Nether Structure 2', False, ['Progressive Tools', 'Progressive Tools'], ['Bucket', 'Progressive Tools']],
|
||||
['Nether Structure 2', False, [], ['Progressive Weapons']],
|
||||
['Nether Structure 2', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Bucket', 'Progressive Weapons']],
|
||||
['Nether Structure 2', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Progressive Tools', 'Progressive Tools', 'Progressive Weapons']],
|
||||
|
||||
['The End Structure', False, []],
|
||||
['The End Structure', False, [], ['Brewing']],
|
||||
['The End Structure', False, ['3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls'], ['3 Ender Pearls']],
|
||||
['The End Structure', False, [], ['Flint and Steel']],
|
||||
['The End Structure', False, [], ['Progressive Resource Crafting']],
|
||||
['The End Structure', False, [], ['Progressive Tools']],
|
||||
['The End Structure', False, ['Progressive Tools', 'Progressive Tools'], ['Bucket', 'Progressive Tools']],
|
||||
['The End Structure', False, [], ['Progressive Weapons']],
|
||||
['The End Structure', False, [], ['Progressive Armor', 'Shield']],
|
||||
['The End Structure', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Bucket',
|
||||
'Progressive Weapons', 'Progressive Armor',
|
||||
'Brewing', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls']],
|
||||
['The End Structure', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Bucket',
|
||||
'Progressive Weapons', 'Shield',
|
||||
'Brewing', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls']],
|
||||
['The End Structure', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Progressive Tools', 'Progressive Tools',
|
||||
'Progressive Weapons', 'Progressive Armor',
|
||||
'Brewing', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls']],
|
||||
['The End Structure', True, ['Flint and Steel', 'Progressive Resource Crafting', 'Progressive Tools', 'Progressive Tools', 'Progressive Tools',
|
||||
'Progressive Weapons', 'Shield',
|
||||
'Brewing', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls', '3 Ender Pearls']],
|
||||
|
||||
])
|
@@ -1,49 +0,0 @@
|
||||
from . import MCTestBase
|
||||
from ..Constants import region_info
|
||||
from .. import Options
|
||||
|
||||
from BaseClasses import ItemClassification
|
||||
|
||||
class AdvancementTestBase(MCTestBase):
|
||||
options = {
|
||||
"advancement_goal": Options.AdvancementGoal.range_end
|
||||
}
|
||||
# beatability test implicit
|
||||
|
||||
class ShardTestBase(MCTestBase):
|
||||
options = {
|
||||
"egg_shards_required": Options.EggShardsRequired.range_end,
|
||||
"egg_shards_available": Options.EggShardsAvailable.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": Options.BeeTraps.range_start
|
||||
}
|
||||
|
||||
# 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": Options.BeeTraps.range_end
|
||||
}
|
||||
|
||||
# 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
|
@@ -1,33 +0,0 @@
|
||||
from test.bases import TestBase, WorldTestBase
|
||||
from .. import MinecraftWorld, MinecraftOptions
|
||||
|
||||
|
||||
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)
|
||||
|
Reference in New Issue
Block a user