diff options
| -rw-r--r-- | components/base/ItemStack/ItemStackPicker.vue | 4 | ||||
| -rw-r--r-- | components/editor/quest/modal/Yaml.vue | 59 | ||||
| -rw-r--r-- | components/editor/task/Configuration.vue | 8 | ||||
| -rw-r--r-- | components/editor/task/ConfigurationRow.vue | 18 | ||||
| -rw-r--r-- | lib/questsLoader.ts | 42 | ||||
| -rw-r--r-- | pages/quest/[id].vue | 9 |
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" |
