feat: 119-Quest-Intro (#154)

- Add an introduction for every quest
- Not yet written documents for the introduction, so the contents are
from the information in the game
This commit is contained in:
Justin Lin authored and GitHub committed 2024-08-01 20:52:10 +08:00
commit 3f08452ea4
3 files changed
+192 -63

No files matched your search

+121
View File
@@ -0,0 +1,121 @@
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { useToast } from 'vue-toastification'
import { ref, defineProps, onMounted } from 'vue'
import { getQuest } from '../utils/api'
import MarkdownText from './MarkdownText.vue'
const props = defineProps({
questTitle: {
type: String,
required: true,
},
questId: {
type: String,
required: true,
},
questUnlocked: {
type: Boolean,
required: true,
},
})
const emit = defineEmits(['closeIntro'])
const showIntro = ref(true) // Controls whether the modal is visible or not
const router = useRouter()
const toast = useToast()
const { questTitle, questId, questUnlocked } = props
const questInstruction = ref('')
const questColor = ref('')
const questTextColor = ref('')
const closeModal = () => {
showIntro.value = false
emit('closeIntro', true)
router.push({ name: 'map' })
}
const handleQuests = (id: string) => {
if (questUnlocked) {
router.push({ name: 'game', params: { questId: id } })
} else {
toast.warning('你還沒完成前一個關卡!')
}
}
const handleColour = () => {
if (questUnlocked) {
questColor.value = '#00ff00'
questTextColor.value = '#000000'
} else {
questColor.value = '#505050'
questTextColor.value = '#a0a0a0'
}
}
onMounted(async () => {
const quest = await getQuest(questId)
questInstruction.value = quest.instruction
handleColour()
})
</script>
<template>
<div v-if="showIntro" class="modal">
<div class="modal-content bg-bg-secondary rounded-lg">
<span class="close text-text content-between" @click="closeModal"
>&times;</span
>
<h2
id="QuestTitle"
class="text-2xl text-text mb-5 w-full inline-flex justify-center"
>
{{ questTitle }}
</h2>
<div class="w-full h-3/5 p-8">
<div class="px-5 whitespace-pre-wrap">
<MarkdownText
class="text-text markdown-content"
:content="questInstruction"
/>
</div>
</div>
<a
id="StartAdventure"
target="_blank"
@click="handleQuests(questId)"
:style="{ backgroundColor: questColor, color: questTextColor }"
class="inline-flex justify-center rounded-lg text-base font-black py-3 mt-5 w-full text-lg"
>開始冒險</a
>
</div>
</div>
</template>
<style scoped>
.modal {
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 1;
}
.modal-content {
position: relative;
margin: 10% auto;
padding: 20px;
width: 60%;
height: 50%;
}
.close {
position: absolute;
top: 0;
right: 10px;
font-size: 30px;
cursor: pointer;
}
.markdown-content {
text-align: left;
}
</style>
+68 -63
View File
@@ -1,13 +1,12 @@
<script setup lang="ts">
import { ref, onMounted, watch, computed } from 'vue'
import { useRouter } from 'vue-router'
import { useToast } from 'vue-toastification'
import { DAG } from '@linux-odyssey/utils'
import { getQuests } from '../utils/api'
import useUserProfile from '../store/userProfile'
import QuestIntro from './QuestIntro.vue'
const store = useUserProfile()
const router = useRouter()
const toast = useToast()
const svgWidth = ref(window.innerWidth)
@@ -18,7 +17,7 @@ const marginY = 120
const nodeWidth = 160
const nodeHeight = 50
const opened = ref<Node | null>(null)
type Node = {
id: string
title: string
@@ -101,12 +100,12 @@ async function computeGraphData() {
toast.error('Failed to load quest data')
}
}
function handleNodeClick(node: { id: string; unlocked: boolean }) {
if (node.unlocked) {
router.push({ name: 'game', params: { questId: node.id } })
} else {
toast.warning('你還沒完成前一個關卡!')
function handleNodeClick(node: Node) {
opened.value = node
}
function closeIntro(close: boolean) {
if (close) {
opened.value = null
}
}
@@ -145,63 +144,69 @@ const edgeStyle = computed(() => {
<template>
<div class="relative bg-black w-full h-full flex">
<img
src="../img/catelogbg.svg"
alt="bg"
class="p-2 stroke-2 scale-y-120 justify-center items-center"
/>
<h1
class="p-10 absolute w-fit z-2 font-mono flex flax-wrap text-xl"
style="width: 30%; height: 6%; font-size: 3vh; color: #00ff00"
<div
class="flex place-content-center w-full bg-catelogbg bg-cover bg-scroll"
>
踏上你的Linux冒險之旅吧!
</h1>
<svg
:width="svgWidth"
:height="svgHeight"
class="absolute"
@mousedown="startDrag"
@mousemove="drag"
@mouseup="endDrag"
@mouseleave="endDrag"
>
<g :transform="`translate(${offsetX}, ${offsetY})`">
<g
v-for="edge in graphData.edges"
:key="`${edge.source.id}-${edge.target.id}`"
>
<path
:d="curvedPath(edge.source, edge.target)"
:class="['edge', edgeStyle(edge)]"
/>
</g>
<g
v-for="node in graphData.nodes"
:key="node.id"
@click="handleNodeClick(node)"
>
<rect
:x="node.x - nodeWidth / 2"
:y="node.y - nodeHeight / 2"
:width="nodeWidth"
:height="nodeHeight"
rx="10"
ry="10"
:class="['node', nodeStyle(node)]"
/>
<text
:x="node.x"
:y="node.y"
text-anchor="middle"
alignment-baseline="middle"
:class="['node-text', nodeStyle(node)]"
font-size="18"
<h1
class="p-10 absolute w-fit z-2 font-mono flex flax-wrap text-xl"
style="width: 30%; height: 6%; font-size: 3vh; color: #00ff00"
>
踏上你的Linux冒險之旅吧!
</h1>
<svg
:width="svgWidth"
:height="svgHeight"
class="absolute"
@mousedown="startDrag"
@mousemove="drag"
@mouseup="endDrag"
@mouseleave="endDrag"
>
<g :transform="`translate(${offsetX}, ${offsetY})`">
<g
v-for="edge in graphData.edges"
:key="`${edge.source.id}-${edge.target.id}`"
>
{{ node.title }}
</text>
<path
:d="curvedPath(edge.source, edge.target)"
:class="['edge', edgeStyle(edge)]"
/>
</g>
<g
v-for="node in graphData.nodes"
:key="node.id"
@click="handleNodeClick(node)"
>
<rect
:x="node.x - nodeWidth / 2"
:y="node.y - nodeHeight / 2"
:width="nodeWidth"
:height="nodeHeight"
rx="10"
ry="10"
:class="['node', nodeStyle(node)]"
/>
<text
:x="node.x"
:y="node.y"
text-anchor="middle"
alignment-baseline="middle"
:class="['node-text', nodeStyle(node)]"
font-size="18"
>
{{ node.title }}
</text>
</g>
</g>
</g>
</svg>
</svg>
<QuestIntro
:questTitle="opened.title"
:questId="opened.id"
:questUnlocked="opened.unlocked"
v-if="opened"
@close-intro="closeIntro"
/>
</div>
</div>
</template>
+3
View File
@@ -24,6 +24,9 @@ export default {
DEFAULT: '#FF0000',
},
},
backgroundImage: {
catelogbg: "url('src/img/catelogbg.svg')",
},
},
},
plugins: [],