diff options
Diffstat (limited to 'src/components/Editor/Quest')
5 files changed, 158 insertions, 7 deletions
diff --git a/src/components/Editor/Quest/QuestTasksOptionsPanel.vue b/src/components/Editor/Quest/QuestTasksOptionsPanel.vue index 12b1263..a79e636 100644 --- a/src/components/Editor/Quest/QuestTasksOptionsPanel.vue +++ b/src/components/Editor/Quest/QuestTasksOptionsPanel.vue @@ -1,9 +1,10 @@ <script setup lang="ts"> import { useSessionStore, type EditorQuest } from '@/stores/session'; -import { computed } from 'vue'; +import { computed, ref } from 'vue'; import EditorOptionsPanel from '@/components/Editor/EditorOptionsPanel.vue'; import TaskConfiguration from '@/components/Editor/Quest/Task/TaskConfiguration.vue'; import Button from '@/components/Control/Button.vue'; +import AddTaskModal from './Task/Modal/AddTaskModal.vue'; const props = defineProps<{ questId: string; @@ -14,6 +15,19 @@ const sessionStore = useSessionStore(); const quest = computed(() => { return sessionStore.getQuestById(props.questId) as EditorQuest; }); + +const showAddTaskModal = ref(false); + +const addTask = (newId: string, newType: string) => { + sessionStore.getQuestById(props.questId)!.tasks[newId] = { + id: newId, + config: { + type: newType, + }, + }; + + showAddTaskModal.value = false; +}; </script> <template> @@ -21,6 +35,7 @@ const quest = computed(() => { <div id="options"> <h2>Tasks <code>({{ Object.keys(quest.tasks).length }})</code></h2> + <p v-if="Object.keys(quest.tasks).length === 0" class="error-text">This quest does not have any tasks.</p> <TaskConfiguration v-for="(task, taskId) in quest.tasks" :key="taskId" :taskId="String(taskId)" :quest="quest" /> <div id="controls"> @@ -29,10 +44,18 @@ const quest = computed(() => { :icon="['fas', 'fa-plus']" type="solid" label="Add task" + @click="showAddTaskModal = true" /> </div> </div> </EditorOptionsPanel> + + <AddTaskModal + v-if="quest" + v-model="showAddTaskModal" + :questId="questId" + @add="addTask" + /> </template> diff --git a/src/components/Editor/Quest/Task/Modal/AddTaskModal.vue b/src/components/Editor/Quest/Task/Modal/AddTaskModal.vue new file mode 100644 index 0000000..57139bb --- /dev/null +++ b/src/components/Editor/Quest/Task/Modal/AddTaskModal.vue @@ -0,0 +1,90 @@ +<script setup lang="ts"> +import Modal from '@/components/Control/Modal.vue'; +import Button from '@/components/Control/Button.vue'; +import { computed, ref } from 'vue'; +import { useSessionStore } from '@/stores/session'; +import { validateTaskId } from '@/lib/util'; + +const model = defineModel(); + +const emit = defineEmits(['add']); + +const session = useSessionStore(); + +const props = defineProps({ + questId: { + type: String, + required: true, + }, +}); + +const knownTasks = computed(() => session.getQuestById(props.questId)!.tasks); +const knownTaskTypes = computed(() => session.getKnownTaskTypes()); + +const newId = ref(''); +const newType = ref(''); +const unknownTaskType = computed(() => !knownTaskTypes.value.includes(newType.value)); +const invalidTaskId = computed(() => !validateTaskId(newId.value)); +const duplicateTaskId = computed(() => knownTasks.value[newId.value] !== undefined); + +const newTypeDescription = computed(() => session.getTaskDefinitionByTaskType(newType.value)?.description); +</script> + +<template> + <Modal v-model="model"> + <template v-slot:header> + <h2>Add new task</h2> + </template> + + <template v-slot:body> + <div id="body"> + <div class="option-group"> + <label for="new-type">Task ID</label> + <input id="new-id" name="new-id" type="text" v-model="newId" /> + <p v-if="invalidTaskId" class="error-text">Invalid task ID.</p> + <p v-if="duplicateTaskId" class="error-text">Task ID already exists.</p> + </div> + <div class="option-group"> + <label for="new-type">Task type</label> + <multiselect + id="new-type" + v-model="newType" + :options="knownTaskTypes" + :searchable="true" + placeholder="Select a new type" + ></multiselect> + <p v-if="unknownTaskType" class="error-text">Invalid task type.</p> + </div> + <p v-if="newTypeDescription">{{ newTypeDescription }}</p> + <p>A task ID must be unique, alphanumeric, and not contain any spaces.</p> + <div id="confirm" class="control-group"> + <Button + :icon="['fas', 'fa-times']" + :label="'Cancel'" + @click="model = false" + ></Button> + <Button + type="solid" + :icon="['fas', 'fa-check']" + :label="'Confirm'" + :disabled="unknownTaskType || invalidTaskId || duplicateTaskId" + @click="emit('add', newId, newType)" + ></Button> + </div> + </div> + </template> + </Modal> +</template> + +<style scoped> +#confirm { + display: flex; + justify-content: flex-end; +} + +#body { + display: flex; + flex-direction: column; + gap: 0.5rem; +} +</style>
\ No newline at end of file diff --git a/src/components/Editor/Quest/Task/Modal/ChangeTaskModal.vue b/src/components/Editor/Quest/Task/Modal/ChangeTaskModal.vue index f8ffef7..c6b5921 100644 --- a/src/components/Editor/Quest/Task/Modal/ChangeTaskModal.vue +++ b/src/components/Editor/Quest/Task/Modal/ChangeTaskModal.vue @@ -1,17 +1,26 @@ <script setup lang="ts"> import Modal from '@/components/Control/Modal.vue'; import Button from '@/components/Control/Button.vue'; -import { ref } from 'vue'; +import { computed, ref } from 'vue'; +import { useSessionStore } from '@/stores/session'; const model = defineModel(); const emit = defineEmits(['update']); -defineProps({ +const session = useSessionStore(); + +const props = defineProps({ taskId: String, + currentTaskType: String, }); +const knownTaskTypes = computed(() => session.getKnownTaskTypes()); + const newType = ref(''); +const unknownTaskType = computed(() => !knownTaskTypes.value.includes(newType.value)); +const noChange = computed(() => newType.value === props.currentTaskType); +const newTypeDescription = computed(() => session.getTaskDefinitionByTaskType(newType.value)?.description); </script> <template> @@ -24,8 +33,16 @@ const newType = ref(''); <div id="body"> <div class="option-group"> <label for="new-type">New type</label> - <input id="new-type" name="new-type" type="text" v-model="newType" /> + <multiselect + id="new-type" + v-model="newType" + :options="knownTaskTypes" + :searchable="true" + placeholder="Select a new type" + ></multiselect> </div> + <p v-if="unknownTaskType" class="error-text">Invalid task type.</p> + <p v-if="newTypeDescription">{{ newTypeDescription }}</p> <p>Any configured options for this task will be overwritten.</p> <div id="confirm" class="control-group"> <Button @@ -37,6 +54,7 @@ const newType = ref(''); type="solid" :icon="['fas', 'fa-check']" :label="'Change'" + :disabled="unknownTaskType || noChange" @click="emit('update', newType)" ></Button> </div> diff --git a/src/components/Editor/Quest/Task/TaskConfiguration.vue b/src/components/Editor/Quest/Task/TaskConfiguration.vue index 7006f18..0646ad4 100644 --- a/src/components/Editor/Quest/Task/TaskConfiguration.vue +++ b/src/components/Editor/Quest/Task/TaskConfiguration.vue @@ -65,6 +65,16 @@ const deleteValue = (fieldName: string) => { const showChangeModal = ref(false); +const updateTaskType = (newType: string) => { + sessionStore.getQuestById(props.quest.id)!.tasks[props.taskId].config = { + type: newType + }; + showChangeModal.value = false; +} + +const deleteTaskType = (taskId: string) => { + delete sessionStore.getQuestById(props.quest.id)!.tasks[taskId]; +} </script> <template> @@ -87,6 +97,7 @@ const showChangeModal = ref(false); <Button :icon="['fas', 'fa-trash']" :label="'Delete'" + @click="deleteTaskType(props.taskId)" ></Button> </div> </div> @@ -105,7 +116,7 @@ const showChangeModal = ref(false); <div v-if="taskDefintion"> <TaskConfigurationRow v-for="fieldName in [...givenRequiredFields, ...missingFields, ...remainingGivenFields]" - :key="`${quest.id}-${props.taskId}-${fieldName}`" + :key="`${quest.id}-${props.taskId}-${taskType}-${fieldName}`" :required="requiredFields.includes(fieldName)" :configKey="fieldName" :initialValue="taskConfig[fieldName]" @@ -130,6 +141,9 @@ const showChangeModal = ref(false); <ChangeTaskModal v-model="showChangeModal" :taskId="props.taskId" + :currentTaskType="taskType" + :key="`change-task-${props.taskId}`" + @update="updateTaskType" /> </template> diff --git a/src/components/Editor/Quest/Task/TaskConfigurationRow.vue b/src/components/Editor/Quest/Task/TaskConfigurationRow.vue index d77e450..f68ce97 100644 --- a/src/components/Editor/Quest/Task/TaskConfigurationRow.vue +++ b/src/components/Editor/Quest/Task/TaskConfigurationRow.vue @@ -2,7 +2,8 @@ import { useSessionStore } from '@/stores/session'; import { computed, ref, toRefs, watch } from 'vue'; import TrueFalseSwitch from '@/components/Control/TrueFalseSwitch.vue'; -import materials from '@/data/materials.json'; +import ItemStackPicker from '@/components/Control/ItemStackPicker.vue'; +import materials from '@/lib/materials'; const props = defineProps({ taskType: { @@ -33,7 +34,9 @@ const currentValue = ref(props.initialValue || ? false : (props.type === 'material-list' || props.type === 'string-list' ? [] - : '' + : props.type === 'itemstack' + ? null + : '' ))); if (props.initialValue !== currentValue.value) { @@ -81,6 +84,9 @@ const addValue = (searchQuery: any) => { <!-- 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" /> + + <!-- Data type 'itemstack' --> + <ItemStackPicker v-else-if="props.type === 'itemstack'" :value="currentValue" @update="updateValue" /> <div v-if="showDescription || error" id="task-configuration-row-info"> <p v-if="error" class="error">A value is required.</p> |
