aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/base/ItemStack/ItemStackPicker.vue4
-rw-r--r--components/editor/quest/modal/Yaml.vue59
-rw-r--r--components/editor/task/Configuration.vue8
-rw-r--r--components/editor/task/ConfigurationRow.vue18
-rw-r--r--lib/questsLoader.ts42
-rw-r--r--pages/quest/[id].vue9
6 files changed, 120 insertions, 20 deletions
diff --git a/components/base/ItemStack/ItemStackPicker.vue b/components/base/ItemStack/ItemStackPicker.vue
index 9141100..bb0b84d 100644
--- a/components/base/ItemStack/ItemStackPicker.vue
+++ b/components/base/ItemStack/ItemStackPicker.vue
@@ -50,8 +50,8 @@ const update = (newValue: any) => {
<div class="itemstack" @click="showItemStackModal = true">
<span v-if="empty" class="empty">ItemStack...</span>
<span v-if="isQuestItem" class="item"><font-awesome-icon :icon="['fas', 'tag']" /> Quest Item</span>
- <span v-if="isItemStack" class="item"><font-awesome-icon :icon="['fas', 'cube']" /> ItemStack of '{{ value.type ||
- value.item || value.material }}'</span>
+ <span v-if="isItemStack" class="item"><font-awesome-icon :icon="['fas', 'cube']" /> ItemStack: {{ value.type ||
+ value.item || value.material }}</span>
<span v-if="isMaterial" class="item"><font-awesome-icon :icon="['fas', 'apple-whole']" /> {{ value }}</span>
<span v-if="!empty && !isQuestItem && !isItemStack && !isMaterial" class="invalid"><font-awesome-icon
:icon="['fas', 'triangle-exclamation']" /> Invalid ItemStack</span>
diff --git a/components/editor/quest/modal/Yaml.vue b/components/editor/quest/modal/Yaml.vue
new file mode 100644
index 0000000..365055c
--- /dev/null
+++ b/components/editor/quest/modal/Yaml.vue
@@ -0,0 +1,59 @@
+<script setup lang="ts">
+import { stringify } from 'yaml';
+import { mapJsonQuestToYamlObject } from '~/lib/questsLoader';
+
+const emit = defineEmits(['delete']);
+const props = defineProps({
+ questId: {
+ required: true,
+ type: String
+ },
+});
+
+const session = useSessionStore();
+
+const showModal = ref(false);
+const yamlString = ref('');
+
+const open = () => {
+ const quest = session.getQuestById(props.questId);
+ if (!quest) {
+ return
+ }
+ const mappedObject = mapJsonQuestToYamlObject(quest);
+ yamlString.value = stringify(mappedObject);
+ showModal.value = true;
+}
+
+defineExpose({
+ open
+})
+</script>
+
+<template>
+ <Modal v-model="showModal">
+ <template v-slot:header>
+ <h2>YAML</h2>
+ </template>
+
+ <p>YAML file for <code>{{ props.questId }}</code></p>
+
+ <textarea rows="20" :value="yamlString" readonly />
+
+ <div id="confirm" class="control-group">
+ <Button type="solid" :icon="['fas', 'check']" :label="'Close'" @click="showModal = false"></Button>
+ </div>
+ </Modal>
+</template>
+
+<style scoped>
+#confirm {
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 1rem;
+}
+
+textarea {
+ width: 100%;
+}
+</style> \ No newline at end of file
diff --git a/components/editor/task/Configuration.vue b/components/editor/task/Configuration.vue
index a209b58..bda75f4 100644
--- a/components/editor/task/Configuration.vue
+++ b/components/editor/task/Configuration.vue
@@ -109,8 +109,8 @@ const deleteTaskType = (taskId: string) => {
:type="(taskDefintion.configuration[fieldName].type as string)"
@update="(newValue: any) => updateValue(fieldName, newValue)" @delete="() => deleteValue(fieldName)" />
<div id="add-option">
- <multiselect class="multiselect" :options="configKeysOptions" :searchable="true" @select="onAddOption"
- placeholder="Add option...">
+ <multiselect class="configuration-multiselect" :options="configKeysOptions" :searchable="true"
+ @select="onAddOption" placeholder="Add option...">
</multiselect>
</div>
</div>
@@ -173,12 +173,12 @@ const deleteTaskType = (taskId: string) => {
border-top: 1px solid var(--color-border);
}
-:deep(.multiselect) .multiselect__tags {
+:deep(.configuration-multiselect) .multiselect__tags {
border: unset !important;
border-radius: 0px !important;
}
-:deep(.multiselect) .multiselect__select {
+:deep(.configuration-multiselect) .multiselect__select {
background: unset !important;
}
</style>
diff --git a/components/editor/task/ConfigurationRow.vue b/components/editor/task/ConfigurationRow.vue
index 98a644c..666669d 100644
--- a/components/editor/task/ConfigurationRow.vue
+++ b/components/editor/task/ConfigurationRow.vue
@@ -76,12 +76,13 @@ const addValue = (searchQuery: any) => {
<TrueFalseSwitch v-else-if="props.type === 'boolean'" :value="!!currentValue" @update="updateValue" />
<!-- Data type 'material-list' -->
- <multiselect v-else-if="props.type === 'material-list'" v-model="currentValue" :options="materials"
- :multiple="true" :taggable="true" :searchable="true" placeholder="Enter material name" />
+ <multiselect v-else-if="props.type === 'material-list'" v-model="currentValue" class="configuration-multiselect"
+ :options="materials" :multiple="true" :taggable="true" :searchable="true" placeholder="Enter material name" />
<!-- Data type 'string-list' -->
- <multiselect v-else-if="props.type === 'string-list'" v-model="currentValue" :options="[]" @tag="addValue"
- :multiple="true" :taggable="true" :searchable="true" placeholder="Enter string" />
+ <multiselect v-else-if="props.type === 'string-list'" v-model="currentValue" class="configuration-multiselect"
+ :options="[]" @tag="addValue" :multiple="true" :taggable="true" :searchable="true"
+ placeholder="Enter string" />
<!-- Data type 'itemstack' -->
<ItemStackPicker v-else-if="props.type === 'itemstack'" :value="currentValue" @update="updateValue" />
@@ -181,13 +182,4 @@ input {
.error {
color: var(--color-false);
}
-
-:deep(.multiselect) .multiselect__tags {
- border: unset !important;
- border-radius: 0px !important;
-}
-
-:deep(.multiselect) .multiselect__select {
- background: unset !important;
-}
</style> \ No newline at end of file
diff --git a/lib/questsLoader.ts b/lib/questsLoader.ts
index 0623f04..a4ea64f 100644
--- a/lib/questsLoader.ts
+++ b/lib/questsLoader.ts
@@ -66,3 +66,45 @@ export function loadCategoriesFromJson(config: any): EditorCategory[] {
};
});
}
+
+//TODO don't write fields if they're unchanged
+export function mapJsonQuestToYamlObject(quest: EditorQuest): any {
+ return {
+ tasks: Object.fromEntries(Object.keys(quest.tasks).map((taskId: string) => {
+ return [taskId, quest.tasks[taskId].config]
+ })),
+ display: {
+ name: quest.display.name,
+ "lore-normal": quest.display.lore.normal,
+ "lore-started": quest.display.lore.started,
+ type: quest.display.type
+ },
+ rewards: quest.rewards,
+ ...(quest.startCommands && { startcommands: quest.startCommands }),
+ ...(quest.startString && { startstring: quest.startString }),
+ ...(quest.rewardString && { rewardstring: quest.rewardString }),
+ ...(quest.placeholders && { placeholders: quest.placeholders }),
+ options: {
+ category: quest.options.category,
+ requires: quest.options.requirements,
+ "permission-required": quest.options.permissionRequired,
+ cancellable: quest.options.cancellable,
+ "counts-towards-limit": quest.options.countsTowardsLimit,
+ repeatable: quest.options.repeatable,
+ cooldown: {
+ enabled: quest.options.cooldown.enabled,
+ time: quest.options.cooldown.time,
+ },
+ "time-limit": {
+ enabled: quest.options.timeLimit.enabled,
+ time: quest.options.timeLimit.time,
+ },
+ "sort-order": quest.options.sortOrder,
+ autostart: quest.options.autostart || false,
+ ...(quest.options.completedDisplay && { completedDisplay: quest.options.completedDisplay }),
+ ...(quest.options.cooldownDisplay && { cooldownDisplay: quest.options.cooldownDisplay }),
+ ...(quest.options.permissionDisplay && { permissionDisplay: quest.options.permissionDisplay }),
+ ...(quest.options.lockedDisplay && { lockedDisplay: quest.options.lockedDisplay }),
+ },
+ }
+} \ No newline at end of file
diff --git a/pages/quest/[id].vue b/pages/quest/[id].vue
index ad8ef4c..a9b2074 100644
--- a/pages/quest/[id].vue
+++ b/pages/quest/[id].vue
@@ -2,6 +2,7 @@
import { useSessionStore } from '@/stores/session';
import { computed, ref } from 'vue';
import { navigateToEditorPane, stripColorCodes } from '@/lib/util';
+import type Yaml from '~/components/editor/quest/modal/Yaml.vue';
definePageMeta({
layout: 'editor'
@@ -23,6 +24,7 @@ const categoryFromSelectedQuest = computed(() => {
}
});
+const yamlModal = ref<InstanceType<typeof Yaml> | null>(null);
const showDeleteModal = ref(false);
const showRenameModal = ref(false);
const showDuplicateModal = ref(false);
@@ -44,6 +46,10 @@ const duplicateQuest = (oldId: string, newId: string) => {
navigateToEditorPane('quest', newId);
showDuplicateModal.value = false;
};
+
+const showYaml = () => {
+ yamlModal.value?.open();
+}
</script>
<template>
@@ -59,7 +65,7 @@ const duplicateQuest = (oldId: string, newId: string) => {
<code>({{ questId }})</code>
</span>
<span id="controls" class="control-group">
- <Button :icon="['fas', 'fa-code']" :label="'YAML'"></Button>
+ <Button :icon="['fas', 'fa-code']" :label="'YAML'" @click="showYaml"></Button>
<Button :icon="['fas', 'fa-copy']" :label="'Duplicate'" @click="showDuplicateModal = true"></Button>
<Button :icon="['fas', 'fa-pen']" :label="'Rename'" @click="showRenameModal = true"></Button>
<Button :icon="['fas', 'fa-trash']" :label="'Delete'" @click="showDeleteModal = true"></Button>
@@ -72,6 +78,7 @@ const duplicateQuest = (oldId: string, newId: string) => {
<EditorQuestTasksOptionsPanel :questId="questId" />
</div>
+ <EditorQuestModalYaml ref="yamlModal" :key="`yaml-quest-${questId}`" :questId="questId" />
<EditorQuestModalDelete v-model="showDeleteModal" :key="`delete-quest-${questId}`" :questId="questId"
@delete="() => questId && deleteQuest(questId)" />
<EditorQuestModalRename v-model="showRenameModal" :key="`rename-quest-${questId}`" :questId="questId"