Support rolling mystery seeds
This commit is contained in:
@@ -261,7 +261,7 @@ export default defineComponent({
|
||||
<MsuResumePicker ref="msu_resume" />
|
||||
<ReduceFlashingPicker ref="reduce_flashing" />
|
||||
<QuickswapPicker ref="quickswap" />
|
||||
<CollectionRatePicker v-if="allow_cr" ref="cr" />
|
||||
<CollectionRatePicker v-if="allow_cr || settings.mystery == 'mystery'" ref="cr" />
|
||||
<UltraFastJunkPicker :created="created" :race="settings.race" ref="ufj" />
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -58,76 +58,83 @@ export default defineComponent({
|
||||
{{ settingsDisplay.randomizer[settings.randomizer] }}
|
||||
<hr class="mt-2 mb-2" />
|
||||
</div>
|
||||
<div v-if="settings.mode">
|
||||
Mode: {{ settingsDisplay.mode[settings.mode] }}
|
||||
</div>
|
||||
<div v-if="settings.tile_swap && settings.tile_swap != 'vanilla'">
|
||||
{{ settingsDisplay.tile_swap[settings.tile_swap] }}
|
||||
</div>
|
||||
<div v-if="settings.overworld_layout && settings.overworld_layout != 'vanilla'">
|
||||
{{ settingsDisplay.overworld_layout[settings.overworld_layout] }}
|
||||
</div>
|
||||
<div v-if="settings.goal">
|
||||
Goal: {{ settingsDisplay.goal[settings.goal] }}
|
||||
</div>
|
||||
<div v-if="settings.crystals_gt">
|
||||
Ganon's Tower Entry: {{ settingsDisplay.crystals_gt[settings.crystals_gt] }}
|
||||
</div>
|
||||
<div v-if="settings.weapons">
|
||||
Weapons: {{ settingsDisplay.weapons[settings.weapons] }}
|
||||
</div>
|
||||
<div v-if="settings.entrance_shuffle && settings.entrance_shuffle != 'vanilla'">
|
||||
<hr class="mt-2 mb-2" />
|
||||
<template v-if="settings.mystery == 'mystery'">
|
||||
<div>
|
||||
{{ settingsDisplay.entrance_shuffle[settings.entrance_shuffle] }}
|
||||
Mystery Settings
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="settings.door_shuffle && settings.door_shuffle != 'vanilla'">
|
||||
<hr class="mt-2 mb-2" />
|
||||
<div>
|
||||
{{ settingsDisplay.door_shuffle[settings.door_shuffle] }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-if="settings.mode">
|
||||
Mode: {{ settingsDisplay.mode[settings.mode] }}
|
||||
</div>
|
||||
<div v-if="settings.lobbies && settings.lobbies != 'vanilla'">
|
||||
{{ settingsDisplay.lobbies[settings.lobbies] }}
|
||||
<div v-if="settings.tile_swap && settings.tile_swap != 'vanilla'">
|
||||
{{ settingsDisplay.tile_swap[settings.tile_swap] }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isEnemizer">
|
||||
<hr class="mt-2 mb-2" />
|
||||
<div v-if="settings.enemy_shuffle && settings.enemy_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.enemy_shuffle[settings.enemy_shuffle] }}
|
||||
<div v-if="settings.overworld_layout && settings.overworld_layout != 'vanilla'">
|
||||
{{ settingsDisplay.overworld_layout[settings.overworld_layout] }}
|
||||
</div>
|
||||
<div v-if="settings.boss_shuffle && settings.boss_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.boss_shuffle[settings.boss_shuffle] }}
|
||||
<div v-if="settings.goal">
|
||||
Goal: {{ settingsDisplay.goal[settings.goal] }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dungeonItemsShuffled">
|
||||
<hr class="mt-2 mb-2" />
|
||||
<div v-if="settings.small_keys && settings.small_keys != 'dungeon'">
|
||||
{{ settingsDisplay.dungeon_items[settings.small_keys] }} Small Keys
|
||||
<div v-if="settings.crystals_gt">
|
||||
Ganon's Tower Entry: {{ settingsDisplay.crystals_gt[settings.crystals_gt] }}
|
||||
</div>
|
||||
<div v-if="settings.big_keys && settings.big_keys != 'dungeon'">
|
||||
{{ settingsDisplay.dungeon_items[settings.big_keys] }} Big Keys
|
||||
<div v-if="settings.weapons">
|
||||
Weapons: {{ settingsDisplay.weapons[settings.weapons] }}
|
||||
</div>
|
||||
<div v-if="settings.maps && settings.maps != 'dungeon'">
|
||||
{{ settingsDisplay.dungeon_items[settings.maps] }} Maps
|
||||
<div v-if="settings.entrance_shuffle && settings.entrance_shuffle != 'vanilla'">
|
||||
<hr class="mt-2 mb-2" />
|
||||
<div>
|
||||
{{ settingsDisplay.entrance_shuffle[settings.entrance_shuffle] }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="settings.compasses && settings.compasses != 'dungeon'">
|
||||
{{ settingsDisplay.dungeon_items[settings.compasses] }} Compasses
|
||||
<div v-if="settings.door_shuffle && settings.door_shuffle != 'vanilla'">
|
||||
<hr class="mt-2 mb-2" />
|
||||
<div>
|
||||
{{ settingsDisplay.door_shuffle[settings.door_shuffle] }}
|
||||
</div>
|
||||
<div v-if="settings.lobbies && settings.lobbies != 'vanilla'">
|
||||
{{ settingsDisplay.lobbies[settings.lobbies] }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="settings.prize_shuffle && settings.prize_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.prize_shuffle[settings.prize_shuffle] }}
|
||||
<div v-if="isEnemizer">
|
||||
<hr class="mt-2 mb-2" />
|
||||
<div v-if="settings.enemy_shuffle && settings.enemy_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.enemy_shuffle[settings.enemy_shuffle] }}
|
||||
</div>
|
||||
<div v-if="settings.boss_shuffle && settings.boss_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.boss_shuffle[settings.boss_shuffle] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="extraPool">
|
||||
<hr class="mt-2 mb-2" />
|
||||
<div v-if="settings.shop_shuffle && settings.shop_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.shop_shuffle[settings.shop_shuffle] }}
|
||||
<div v-if="dungeonItemsShuffled">
|
||||
<hr class="mt-2 mb-2" />
|
||||
<div v-if="settings.small_keys && settings.small_keys != 'dungeon'">
|
||||
{{ settingsDisplay.dungeon_items[settings.small_keys] }} Small Keys
|
||||
</div>
|
||||
<div v-if="settings.big_keys && settings.big_keys != 'dungeon'">
|
||||
{{ settingsDisplay.dungeon_items[settings.big_keys] }} Big Keys
|
||||
</div>
|
||||
<div v-if="settings.maps && settings.maps != 'dungeon'">
|
||||
{{ settingsDisplay.dungeon_items[settings.maps] }} Maps
|
||||
</div>
|
||||
<div v-if="settings.compasses && settings.compasses != 'dungeon'">
|
||||
{{ settingsDisplay.dungeon_items[settings.compasses] }} Compasses
|
||||
</div>
|
||||
<div v-if="settings.prize_shuffle && settings.prize_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.prize_shuffle[settings.prize_shuffle] }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="settings.drop_shuffle && settings.drop_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.drop_shuffle[settings.drop_shuffle] }}
|
||||
<div v-if="extraPool">
|
||||
<hr class="mt-2 mb-2" />
|
||||
<div v-if="settings.shop_shuffle && settings.shop_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.shop_shuffle[settings.shop_shuffle] }}
|
||||
</div>
|
||||
<div v-if="settings.drop_shuffle && settings.drop_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.drop_shuffle[settings.drop_shuffle] }}
|
||||
</div>
|
||||
<div v-if="settings.pot_shuffle && settings.pot_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.pot_shuffle[settings.pot_shuffle] }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="settings.pot_shuffle && settings.pot_shuffle != 'vanilla'">
|
||||
{{ settingsDisplay.pot_shuffle[settings.pot_shuffle] }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -76,7 +76,7 @@ export default defineComponent({
|
||||
<b>{{ settings.display ?? name }}:</b>
|
||||
<div class="nav nav-pills nav-fill mt-1 mb-1">
|
||||
<template v-for="value of settings.order ?? Object.keys(settings.values)">
|
||||
<template v-if="settings.values[value]">
|
||||
<template v-if="settings.values[value] && !settings.values[value].hidden">
|
||||
<input type="radio" class="btn-check" :name="`${prefix}${name}`" :id="`${prefix}${name}_${value}`"
|
||||
autocomplete="off" :value="value" v-model="selected" @change="change" />
|
||||
<label :class="`btn btn-outline-${color} nav-item m-1`" :for="`${prefix}${name}_${value}`" data-bs-toggle="tooltip" data-bs-placement="bottom" :title="settings.values[value].tip" ref="tips">
|
||||
|
||||
@@ -84,6 +84,20 @@ base:
|
||||
beta:
|
||||
default:
|
||||
display: Default
|
||||
wjb_cross:
|
||||
display: wjbCross
|
||||
goal: fast_ganon
|
||||
show_loot: always
|
||||
show_map: always
|
||||
show_loot_hud: never
|
||||
entrance_shuffle: crossed
|
||||
skull_woods: vanilla_drops
|
||||
small_keys: wild
|
||||
big_keys: wild
|
||||
maps: wild
|
||||
compasses: wild
|
||||
boots: pseudoboots
|
||||
flute: preactivated
|
||||
pikit:
|
||||
default:
|
||||
display: Default
|
||||
|
||||
+37
-1
@@ -93,21 +93,57 @@ bosses_ganon:
|
||||
display: Bosses for Ganon
|
||||
default: "8of10"
|
||||
values:
|
||||
"0of10":
|
||||
hidden: true
|
||||
"1of10":
|
||||
hidden: true
|
||||
"2of10":
|
||||
hidden: true
|
||||
"3of10":
|
||||
hidden: true
|
||||
"4of10":
|
||||
hidden: true
|
||||
"5of10":
|
||||
hidden: true
|
||||
"6of10":
|
||||
hidden: true
|
||||
"7of10":
|
||||
display: "7 Prize Bosses"
|
||||
tip: To defeat Ganon, you must defeat 7 of the 10 bosses (not including either form of Agahnim).
|
||||
"8of10":
|
||||
display: "8 Prize Bosses"
|
||||
tip: To defeat Ganon, you must defeat 8 of the 10 bosses (not including either form of Agahnim).
|
||||
"9of10":
|
||||
hidden: true
|
||||
"10of10":
|
||||
display: "10 Prize Bosses"
|
||||
tip: To defeat Ganon, you must defeat all 10 bosses (not including either form of Agahnim).
|
||||
"0of12":
|
||||
hidden: true
|
||||
"1of12":
|
||||
hidden: true
|
||||
"2of12":
|
||||
hidden: true
|
||||
"3of12":
|
||||
hidden: true
|
||||
"4of12":
|
||||
hidden: true
|
||||
"5of12":
|
||||
hidden: true
|
||||
"6of12":
|
||||
hidden: true
|
||||
"7of12":
|
||||
hidden: true
|
||||
"8of12":
|
||||
display: "8 Total Bosses"
|
||||
tip: To defeat Ganon, you must defeat 8 of the 12 dungeon bosses.
|
||||
"9of12":
|
||||
hidden: true
|
||||
"10of12":
|
||||
display: "10 Total Bosses"
|
||||
tip: To defeat Ganon, you must defeat 10 of the 12 dungeon bosses.
|
||||
"11of12":
|
||||
hidden: true
|
||||
"12of12":
|
||||
display: "12 Total Bosses"
|
||||
tip: To defeat Ganon, you must defeat all 12 of the dungeon bosses.
|
||||
@@ -460,7 +496,7 @@ dark_rooms:
|
||||
no_dark_rooms:
|
||||
display: All Rooms Lit
|
||||
tip: There are no dark rooms; all rooms that would normally be dark (except for Ganon) are instead fully lit, regardless of whether the player has the lamp.
|
||||
always_in_logic:
|
||||
dark_in_logic:
|
||||
display: Dark Rooms in Logic
|
||||
tip: Dark rooms are always considered to be in logic, even if the player does not have the lamp.
|
||||
starting_lamp:
|
||||
|
||||
+13
-1
@@ -3,6 +3,7 @@ import SeedView from "@/views/SeedView.vue";
|
||||
import MultiView from "@/views/MultiView.vue";
|
||||
import GenerateView from "@/views/GenerateView.vue";
|
||||
import GenerateMulti from "@/views/GenerateMulti.vue";
|
||||
import GenerateMystery from "@/views/GenerateMystery.vue";
|
||||
import GenerateApr2025View from "@/views/GenerateApr2025View.vue";
|
||||
|
||||
const router = createRouter({
|
||||
@@ -38,10 +39,21 @@ const router = createRouter({
|
||||
props: { generator: "pikit", prefix: "pikit-" },
|
||||
},
|
||||
{
|
||||
path: '/multibeta',
|
||||
path: '/multiworld/beta',
|
||||
component: GenerateMulti,
|
||||
alias: '/multibeta',
|
||||
props: { generator: "beta", prefix: "dungeon-maps-" },
|
||||
},
|
||||
{
|
||||
path: '/mystery',
|
||||
component: GenerateMystery,
|
||||
props: { generator: "base", prefix: "" },
|
||||
},
|
||||
{
|
||||
path: '/mystery/beta',
|
||||
component: GenerateMystery,
|
||||
props: { generator: "beta", prefix: "beta-" },
|
||||
},
|
||||
{
|
||||
path: '/seed/:id',
|
||||
name: 'seed',
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import axios from "axios";
|
||||
import localforage from "localforage";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
import settingsData from "@/data/settings.yaml";
|
||||
import generatorSettings from "@/data/generator-settings.yaml";
|
||||
|
||||
import VueMarkdown from "vue-markdown-render";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
VueMarkdown,
|
||||
},
|
||||
props: {
|
||||
prefix: null,
|
||||
generator: null,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uploaded_yaml: null,
|
||||
yaml_error: null,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
document.title = "ALttPR Mystery"
|
||||
},
|
||||
computed: {
|
||||
settings() {
|
||||
const settingsList = generatorSettings[this.generator];
|
||||
const validSettings = {};
|
||||
|
||||
for (const setting of Object.keys(settingsList)) {
|
||||
var generatorValue = settingsList[setting];
|
||||
if (generatorValue == "all") {
|
||||
validSettings[setting] = Object.keys(settingsData[setting].values);
|
||||
} else if (Array.isArray(generatorValue)) {
|
||||
validSettings[setting] = generatorValue;
|
||||
} else {
|
||||
validSettings[setting] = Object.keys(generatorValue.values);
|
||||
}
|
||||
}
|
||||
|
||||
return validSettings;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async generate(race) {
|
||||
await axios.post(`/mystery/${this.generator}`, this.uploaded_yaml, {
|
||||
headers: {
|
||||
"Content-Type": "text/yaml",
|
||||
},
|
||||
params: {
|
||||
race: race,
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
const id = response.data;
|
||||
this.$router.push(`/seed/${id}`);
|
||||
})
|
||||
.catch(error => {
|
||||
this.yaml_error = "Bad YAML";
|
||||
console.log(error.response?.data);
|
||||
});
|
||||
},
|
||||
yamlErrors(yaml) {
|
||||
const errorList = [];
|
||||
|
||||
for (const key of Object.keys(yaml)) {
|
||||
if (!(key in this.settings)) {
|
||||
errorList.push(`Invalid setting \`${key}\``);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (yaml[key] instanceof Object) {
|
||||
for (const option of Object.keys(yaml[key])) {
|
||||
if (!this.settings[key].includes(option)) {
|
||||
errorList.push(`Invalid setting \`${key}.${option}\``);
|
||||
continue;
|
||||
}
|
||||
|
||||
const weight = yaml[key][option];
|
||||
if (!/^\d+$/.test(weight)) {
|
||||
errorList.push(`Invalid setting weight \`${yaml[key][option]}\` for \`${key}.${option}\``);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const option = yaml[key];
|
||||
if (!this.settings[key].includes(option)) {
|
||||
errorList.push(`Invalid setting \`${key}.${option}\``);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errorList;
|
||||
},
|
||||
uploadYaml(file) {
|
||||
if (!file) {
|
||||
this.yaml_error = null;
|
||||
this.uploaded_yaml = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = async function() {
|
||||
try {
|
||||
const parsed_yaml = yaml.load(reader.result, { schema: yaml.FAILSAFE_SCHEMA });
|
||||
const errors = this.yamlErrors(parsed_yaml);
|
||||
if (errors) {
|
||||
this.uploaded_yaml = null;
|
||||
this.yaml_error = errors.join("\\\n");
|
||||
} else {
|
||||
this.uploaded_yaml = reader.result;
|
||||
this.yaml_error = null;
|
||||
}
|
||||
} catch (error) {
|
||||
this.uploaded_yaml = null;
|
||||
this.yaml_error = "Error parsing YAML";
|
||||
if (error.reason) {
|
||||
this.yaml_error += `: ${error.reason}`;
|
||||
}
|
||||
console.log(error);
|
||||
}
|
||||
}.bind(this);
|
||||
reader.readAsText(file);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mw-30">
|
||||
<div class="card content-div m-3">
|
||||
<div class="card-header">
|
||||
Generate Mystery Seed
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
Upload Mystery YAML:
|
||||
<a class="example-yaml-link" href="/sample_mystery.yaml">(example)</a>
|
||||
<div class="input-group mt-2 mb-2">
|
||||
<input id="yaml-input" class="form-control" type="file" accept=".yaml,.yml" @change="uploadYaml($event.target.files[0], true)" />
|
||||
</div>
|
||||
<div v-if="yaml_error" class="invalid mt-3">
|
||||
<vue-markdown :source="yaml_error" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="card-footer">
|
||||
<div class="nav nav-pills nav-fill" role="group">
|
||||
<button type="button" class="m-1 nav-item btn btn-outline-danger"
|
||||
@click="generate(true);" :disabled="uploaded_yaml == null || yaml_error">
|
||||
Generate Race Mystery
|
||||
</button>
|
||||
<button type="button" class="m-1 nav-item btn btn-outline-primary"
|
||||
@click="generate(false);" :disabled="uploaded_yaml == null || yaml_error">
|
||||
Generate Mystery
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.example-yaml-link {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
padding-right: var(--bs-list-group-item-padding-x);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user