feat: chapter boundary gateway nodes in flow, prevent cross-chapter BFS expansion
This commit is contained in:
@@ -10,6 +10,7 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits<{
|
||||
selectScene: [sceneId: string]
|
||||
selectGateway: [chapterId: string]
|
||||
}>()
|
||||
|
||||
interface FlowNode {
|
||||
@@ -19,6 +20,8 @@ interface FlowNode {
|
||||
thumbnail?: string
|
||||
visited: boolean
|
||||
isMystery: boolean
|
||||
isGateway: boolean
|
||||
gatewayChapterId?: string
|
||||
locked: boolean
|
||||
lockHint?: string
|
||||
x: number
|
||||
@@ -40,10 +43,29 @@ const containerW = ref(800)
|
||||
const containerH = ref(400)
|
||||
|
||||
function buildFlow(root: PlayerTreeNode) {
|
||||
const dagreNodes: { id: string; sceneId: string; thumbnail?: string; parent: string | null; label: string; visited: boolean; isMystery: boolean; locked: boolean; lockHint?: string }[] = []
|
||||
const dagreNodes: { id: string; sceneId: string; thumbnail?: string; parent: string | null; label: string; visited: boolean; isMystery: boolean; isGateway: boolean; gatewayChapterId?: string; locked: boolean; lockHint?: string }[] = []
|
||||
const dagreEdges: { from: string; to: string; visited: boolean }[] = []
|
||||
|
||||
function walk(node: PlayerTreeNode, parentId: string | null) {
|
||||
if (node.isGateway) {
|
||||
const dagreId = parentId ? `${parentId}/gw_${node.gatewayChapterId}` : `gw_${node.gatewayChapterId}`
|
||||
dagreNodes.push({
|
||||
id: dagreId,
|
||||
sceneId: '',
|
||||
parent: parentId,
|
||||
label: node.label,
|
||||
visited: false,
|
||||
isMystery: false,
|
||||
isGateway: true,
|
||||
gatewayChapterId: node.gatewayChapterId,
|
||||
locked: node.locked,
|
||||
})
|
||||
if (parentId) {
|
||||
dagreEdges.push({ from: parentId, to: dagreId, visited: false })
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (node.visited) {
|
||||
const dagreId = parentId ? `${parentId}/${node.sceneId}` : node.sceneId
|
||||
dagreNodes.push({
|
||||
@@ -54,6 +76,7 @@ function buildFlow(root: PlayerTreeNode) {
|
||||
label: node.label,
|
||||
visited: true,
|
||||
isMystery: false,
|
||||
isGateway: false,
|
||||
locked: node.locked,
|
||||
lockHint: node.lockHint,
|
||||
})
|
||||
@@ -63,7 +86,7 @@ function buildFlow(root: PlayerTreeNode) {
|
||||
|
||||
const unvisited: PlayerTreeNode[] = []
|
||||
for (const child of node.children) {
|
||||
if (child.visited) {
|
||||
if (child.visited || child.isGateway) {
|
||||
walk(child, dagreId)
|
||||
} else {
|
||||
unvisited.push(child)
|
||||
@@ -79,6 +102,7 @@ function buildFlow(root: PlayerTreeNode) {
|
||||
label: '? ?',
|
||||
visited: false,
|
||||
isMystery: true,
|
||||
isGateway: false,
|
||||
locked: true,
|
||||
})
|
||||
dagreEdges.push({ from: dagreId, to: mysteryId, visited: false })
|
||||
@@ -120,6 +144,8 @@ function buildFlow(root: PlayerTreeNode) {
|
||||
label: n.label,
|
||||
visited: n.visited,
|
||||
isMystery: n.isMystery,
|
||||
isGateway: n.isGateway || false,
|
||||
gatewayChapterId: n.gatewayChapterId,
|
||||
locked: n.locked,
|
||||
lockHint: n.lockHint,
|
||||
x: pos.x - nw / 2,
|
||||
@@ -238,13 +264,13 @@ const svgH = computed(() => containerH.value)
|
||||
v-for="n in nodes"
|
||||
:key="n.id"
|
||||
class="flow-node"
|
||||
:class="{ visited: n.visited, mystery: n.isMystery, locked: n.locked, clickable: n.visited && n.sceneId }"
|
||||
:class="{ visited: n.visited, mystery: n.isMystery, locked: n.locked, gateway: n.isGateway, clickable: (n.visited && n.sceneId) || n.isGateway }"
|
||||
:style="{ left: n.x + 'px', top: n.y + 'px', width: n.w + 'px', height: n.h + 'px' }"
|
||||
:title="n.lockHint || ''"
|
||||
@click="n.visited && n.sceneId && emit('selectScene', n.sceneId)"
|
||||
@click="n.isGateway ? emit('selectGateway', n.gatewayChapterId!) : n.visited && n.sceneId && emit('selectScene', n.sceneId)"
|
||||
>
|
||||
<img v-if="n.thumbnail" :src="n.thumbnail" class="node-thumb" />
|
||||
<span class="node-icon">{{ n.visited ? '✦' : n.isMystery ? '?' : '⬜' }}</span>
|
||||
<span class="node-icon">{{ n.visited ? '✦' : n.isMystery ? '?' : n.isGateway ? '►' : '⬜' }}</span>
|
||||
<span class="node-label">{{ n.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -306,6 +332,24 @@ const svgH = computed(() => containerH.value)
|
||||
border-color: rgba(201, 168, 76, 0.5);
|
||||
}
|
||||
|
||||
.flow-node.gateway {
|
||||
background: rgba(201, 168, 76, 0.04);
|
||||
border: 1px dashed rgba(201, 168, 76, 0.2);
|
||||
}
|
||||
|
||||
.flow-node.gateway:hover {
|
||||
background: rgba(201, 168, 76, 0.12);
|
||||
border-color: rgba(201, 168, 76, 0.4);
|
||||
}
|
||||
|
||||
.flow-node.gateway .node-icon {
|
||||
color: #c9a84c;
|
||||
}
|
||||
|
||||
.flow-node.gateway.locked {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.flow-node.visited {
|
||||
background: rgba(201, 168, 76, 0.12);
|
||||
border: 1px solid rgba(201, 168, 76, 0.3);
|
||||
|
||||
Reference in New Issue
Block a user