Merge branch 'main' into breaking_changes

# Conflicts:
#	MultiClient.py
#	Utils.py
#	worlds/alttp/ItemPool.py
#	worlds/alttp/Main.py
#	worlds/alttp/Shops.py
This commit is contained in:
Fabian Dill
2021-02-19 13:45:50 +01:00
46 changed files with 10942 additions and 6539 deletions

View File

@@ -126,10 +126,8 @@ done so already, please do this now. SD2SNES and FXPak Pro users may download th
[on this page](http://usb2snes.com/#supported-platforms).
1. Close your emulator, which may have auto-launched.
2. Close QUsb2Snes, which launched automatically with the client.
3. Launch the appropriate version of QUsb2Snes (v0.7.16).
4. Power on your device and load the ROM.
5. Observe the client window now shows "SNES Device: Connected", and lists the name of your device.
2. Power on your device and load the ROM.
3. Observe the client window now shows "SNES Device: Connected", and lists the name of your device.
### Connect to the MultiServer
The patch file which launched your client should have automatically connected you to the MultiServer.

View File

@@ -2,9 +2,17 @@ let spriteData = null;
window.addEventListener('load', () => {
const gameSettings = document.getElementById('weighted-settings');
Promise.all([fetchPlayerSettingsYaml(), fetchPlayerSettingsJson(), fetchSpriteData()]).then((results) => {
Promise.all([fetchWeightedSettingsYaml(), fetchWeightedSettingsJson(), fetchSpriteData()]).then((results) => {
// Load YAML into object
const sourceData = jsyaml.safeLoad(results[0], { json: true });
const wsVersion = sourceData.ws_version;
delete sourceData.ws_version; // Do not include the settings version number in the export
// Check if settings exist in localStorage. If no settings are present, this is a first load (or reset to default)
// and the version number should be silently updated
if (!localStorage.getItem('weightedSettings1')) {
localStorage.setItem('wsVersion', wsVersion);
}
// Update localStorage with three settings objects. Preserve original objects if present.
for (let i=1; i<=3; i++) {
@@ -25,6 +33,16 @@ window.addEventListener('load', () => {
document.getElementById('export-button').addEventListener('click', exportSettings);
document.getElementById('reset-to-default').addEventListener('click', resetToDefaults);
adjustHeaderWidth();
if (localStorage.getItem('wsVersion') !== wsVersion) {
const userWarning = document.getElementById('user-warning');
const messageSpan = document.createElement('span');
messageSpan.innerHTML = "A new version of the weighted settings file is available. Click here to update!" +
"<br />Be aware this will also reset your presets, so you should export them now if you want to save them.";
userWarning.appendChild(messageSpan);
userWarning.style.display = 'block';
userWarning.addEventListener('click', resetToDefaults);
}
}).catch((error) => {
console.error(error);
gameSettings.innerHTML = `
@@ -37,7 +55,7 @@ window.addEventListener('load', () => {
document.getElementById('generate-race').addEventListener('click', () => generateGame(true));
});
const fetchPlayerSettingsYaml = () => new Promise((resolve, reject) => {
const fetchWeightedSettingsYaml = () => new Promise((resolve, reject) => {
const ajax = new XMLHttpRequest();
ajax.onreadystatechange = () => {
if (ajax.readyState !== 4) { return; }
@@ -51,7 +69,7 @@ const fetchPlayerSettingsYaml = () => new Promise((resolve, reject) => {
ajax.send();
});
const fetchPlayerSettingsJson = () => new Promise((resolve, reject) => {
const fetchWeightedSettingsJson = () => new Promise((resolve, reject) => {
const ajax = new XMLHttpRequest();
ajax.onreadystatechange = () => {
if (ajax.readyState !== 4) { return; }
@@ -113,6 +131,7 @@ const handleOptionChange = (event) => {
};
const populateSettings = () => {
buildSpriteOptions();
const presetNumber = document.getElementById('preset-number').value;
const settings = JSON.parse(localStorage.getItem(`weightedSettings${presetNumber}`))
const settingsInputs = Array.from(document.querySelectorAll('.setting'));
@@ -206,7 +225,21 @@ const buildUI = (settings, spriteData) => {
settingsWrapper.appendChild(spriteOptionsHeader);
const spriteOptionsWrapper = document.createElement('div');
spriteOptionsWrapper.setAttribute('id', 'sprite-options-wrapper');
spriteOptionsWrapper.className = 'setting-wrapper';
settingsWrapper.appendChild(spriteOptionsWrapper);
// Append sprite picker
settingsWrapper.appendChild(buildSpritePicker(spriteData));
};
const buildSpriteOptions = () => {
const spriteOptionsWrapper = document.getElementById('sprite-options-wrapper');
// Clear the contents of the wrapper div
while(spriteOptionsWrapper.firstChild){
spriteOptionsWrapper.removeChild(spriteOptionsWrapper.lastChild);
}
const spriteOptionsTitle = document.createElement('span');
spriteOptionsTitle.className = 'title-span';
@@ -240,11 +273,6 @@ const buildUI = (settings, spriteData) => {
spriteOptionsTable.appendChild(tbody);
spriteOptionsWrapper.appendChild(spriteOptionsTable);
settingsWrapper.appendChild(spriteOptionsWrapper);
// Append sprite picker
settingsWrapper.appendChild(buildSpritePicker(spriteData));
};
const buildRangeSettings = (parentElement, settings) => {

View File

@@ -406,6 +406,38 @@
}
}
},
"pyramid_open": {
"keyString": "pyramid_open",
"friendlyName": "Pyramid Open",
"description": "",
"inputType": "range",
"subOptions": {
"goal": {
"keyString": "pyramid_open.goal",
"friendlyName": "Goal",
"description": "Opens the pyramid if the goal requires you to kill Ganon, unless the goal is Slow Ganon or All Dungeons.",
"defaultValue": 50
},
"auto": {
"keyString": "pyramid_open.auto",
"friendlyName": "Auto",
"description": "Same as Goal, but also opens when any non-dungeon entrance shuffle is used.",
"defaultValue": 0
},
"yes": {
"keyString": "pyramid_open.yes",
"friendlyName": "Always Open",
"description": "Pyramid hole is always open. Ganon's vulnerable condition is still required before he can he hurt.",
"defaultValue": 0
},
"no": {
"keyString": "pyramid_open.no",
"friendlyName": "Always Closed",
"description": "Pyramid hole is always closed until you defeat Agahnim atop Ganon's Tower.",
"defaultValue": 0
}
}
},
"triforce_pieces_required": {
"keyString": "triforce_pieces_required",
"friendlyName": "Triforce Pieces Required",
@@ -1135,7 +1167,7 @@
"beemizer": {
"keyString": "beemizer",
"friendlyName": "Beemizer",
"description": "Remove items from the global item pool and replace them with single bees and bee traps.",
"description": "Remove non-health items from the global item pool and replace them with single bees and bee traps.",
"inputType": "range",
"subOptions": {
"0": {
@@ -1147,25 +1179,25 @@
"1": {
"keyString": "beemizer.1",
"friendlyName": "Level 1",
"description": "25% of the non-essential item pool is replaced with bee traps.",
"description": "25% of rupees, bombs and arrows are replaced with bees, of which 60% are traps and 40% single bees",
"defaultValue": 1
},
"2": {
"keyString": "beemizer.2",
"friendlyName": "Level 2",
"description": "60% of the non-essential item pool is replaced with bee traps, of which 20% could be single bees.",
"description": "50% of rupees, bombs and arrows are replaced with bees, of which 70% are traps and 30% single bees",
"defaultValue": 2
},
"3": {
"keyString": "beemizer.3",
"friendlyName": "Level 3",
"description": "100% of the non-essential item pool is replaced with bee traps, of which 50% could be single bees.",
"description": "75% of rupees, bombs and arrows are replaced with bees, of which 80% are traps and 20% single bees",
"defaultValue": 3
},
"4": {
"keyString": "beemizer.4",
"friendlyName": "Level 4",
"description": "100% of the non-essential item pool is replaced with bee traps.",
"description": "100% of rupees, bombs and arrows are replaced with bees, of which 90% are traps and 10% single bees",
"defaultValue": 4
}
}
@@ -1250,19 +1282,19 @@
"description": "No items are moved",
"defaultValue": 50
},
"1": {
"10": {
"keyString": "shop_shuffle_slots.10",
"friendlyName": "Level 1",
"description": "10 Items are moved into shops.",
"defaultValue": 0
},
"2": {
"20": {
"keyString": "shop_shuffle_slots.20",
"friendlyName": "Level 2",
"description": "20 Items are moved into shops.",
"defaultValue": 0
},
"3": {
"30": {
"keyString": "shop_shuffle_slots.30",
"friendlyName": "Level 3",
"description": "30 Items are moved into shops.",

View File

@@ -17,6 +17,11 @@
# To test if your yaml is valid or not, you can use this website:
# http://www.yamllint.com/
# For use with the weighted-settings page on the website. Changing this value will cause all users to be prompted
# to update their settings. The version number should match the current released version number, and the revision
# should be updated manually by whoever edits this file.
ws_version: 4.0.1 rev0
description: Template Name # Used to describe your yaml. Useful if you have multiple files
name: YourName # Your name in-game. Spaces will be replaced with underscores and there is a 16 character limit
### Logic Section ###
@@ -97,6 +102,11 @@ goals:
ganon_triforce_hunt: 0 # Collect 20 of 30 Triforce pieces spread throughout the worlds, then kill Ganon
local_ganon_triforce_hunt: 0 # Collect 20 of 30 Triforce pieces spread throughout your world, then kill Ganon
ice_rod_hunt: 0 # You start with everything needed to 216 the seed. Find the Ice rod, then kill Trinexx at Turtle rock.
pyramid_open:
goal: 50 # Opens the pyramid if the goal requires you to kill Ganon, unless the goal is Slow Ganon or All Dungeons
auto: 0 # Same as Goal, but also opens when any non-dungeon entrance shuffle is used
yes: 0 # Pyramid hole is always open. Ganon's vulnerable condition is still required before he can he hurt
no: 0 # Pyramid hole is always closed until you defeat Agahnim atop Ganon's Tower
triforce_pieces_mode: #Determine how to calculate the extra available triforce pieces.
extra: 0 # available = triforce_pieces_extra + triforce_pieces_required
percentage: 0 # available = (triforce_pieces_percentage /100) * triforce_pieces_required
@@ -218,10 +228,10 @@ pot_shuffle:
### End of Enemizer Section ###
beemizer: # Remove items from the global item pool and replace them with single bees and bee traps
0: 50 # No bee traps are placed
1: 0 # 25% of the non-essential item pool is replaced with bee traps
2: 0 # 60% of the non-essential item pool is replaced with bee traps, of which 20% could be single bees
3: 0 # 100% of the non-essential item pool is replaced with bee traps, of which 50% could be single bees
4: 0 # 100% of the non-essential item pool is replaced with bee traps
1: 0 # 25% of rupees, bombs and arrows are replaced with bees, of which 60% are traps and 40% single bees
2: 0 # 50% of rupees, bombs and arrows are replaced with bees, of which 70% are traps and 30% single bees
3: 0 # 75% of rupees, bombs and arrows are replaced with bees, of which 80% are traps and 20% single bees
4: 0 # 100% of rupees, bombs and arrows are replaced with bees, of which 90% are traps and 10% single bees
### Shop Settings ###
shop_shuffle_slots: # Maximum amount of shop slots to be filled with regular item pool items (such as Moon Pearl)
0: 50

View File

@@ -14,6 +14,17 @@ html{
color: #eeffeb;
}
#user-warning{
display: none;
width: calc(100% - 8px);
background-color: #ffe86b;
border-radius: 4px;
color: #000000;
padding: 4px;
text-align: center;
cursor: pointer;
}
#weighted-settings code{
background-color: #d9cd8e;
border-radius: 4px;

View File

@@ -11,6 +11,7 @@
{% block body %}
{% include 'header/grassHeader.html' %}
<div id="weighted-settings">
<header id="user-warning"></header>
<h1>Weighted Settings</h1>
<div id="instructions">
This page is used to configure your weighted settings. You have three presets you can control, which

View File

@@ -11,7 +11,7 @@ from Utils import Hint
def get_id(item_name):
return Items.item_table[item_name][3]
return Items.item_table[item_name][2]
app.jinja_env.filters["location_name"] = lambda location: Regions.lookup_id_to_name.get(location, location)
@@ -231,9 +231,9 @@ for item_name, data in Items.item_table.items():
if "Key" in item_name:
area = item_name.split("(")[1][:-1]
if "Small" in item_name:
small_key_ids[area] = data[3]
small_key_ids[area] = data[2]
else:
big_key_ids[area] = data[3]
big_key_ids[area] = data[2]
from MultiServer import get_item_name_from_id