new:添加缔造者

This commit is contained in:
2026-08-10 02:10:51 +08:00
parent 8e75e718aa
commit e6abdcc0f8
9 changed files with 272 additions and 192 deletions
+1
View File
@@ -108,6 +108,7 @@ export interface ISkill {
name: string
desc: string
}[]
cellPosition: number[]
uuid: string
upType: 'damage' | 'heal'
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -1,9 +1,13 @@
import { getImageURL, getLocalImageURL } from '@/utils/images'
import { getImageURL } from '@/utils/images'
import type { ISkill } from '@/api/info/type'
import { useVModel } from '@vueuse/core'
import CalcButton from '@/components/calc/button/index.vue'
import './style.scss'
const rowPrefixes = ['通用', 1, '', 10, '', 20, '', 30, '', 40, 50, 60, 70, 80, 90, 100]
const defaultColumnCount = 11
const altersWithoutRowPrefix = new Set(['creator', 'dark_knight'])
export default defineComponent({
name: 'SkillTree',
props: {
@@ -19,34 +23,73 @@ export default defineComponent({
type: Number,
default: () => 50,
},
alter: {
type: String,
default: '',
},
},
emits: ['chooseSkill'],
setup(props,{ emit }) {
setup(props, { emit }) {
const skills = computed(() => props.skills)
const lvInfo = useVModel(props, 'lvInfo')
const bindAwake = useVModel(props, 'bindAwake')
// const emit = defineEmits(['chooseSkill'])
const getSkill = (lvStart: number, lvEnd: number, position: number) => {
if (position >= 0)
return skills.value.find(
(item) =>
item.line > lvStart - 5 &&
item.line <= Math.max(lvEnd - 5, 1) &&
item.position == position,
)
else
return skills.value.filter(
(item) => item.line > lvStart - 5 && item.line <= Math.max(lvEnd - 5, 1),
)
const skillGrid = computed(() => {
const grid = new Map<string, ISkill>()
const occupiedRows = new Set<number>()
let maxColumn = defaultColumnCount - 1
skills.value.forEach((skill) => {
const [column, row] = skill.cellPosition ?? []
if (!Number.isInteger(column) || !Number.isInteger(row) || column < 0 || row < 0) return
grid.set(`${row}:${column}`, skill)
occupiedRows.add(row)
maxColumn = Math.max(maxColumn, column)
})
return {
grid,
columns: Array.from({ length: maxColumn + 1 }, (_, index) => index),
rows: Array.from(occupiedRows).sort((left, right) => left - right),
}
})
const showRowPrefix = computed(() => !altersWithoutRowPrefix.has(props.alter))
const isExtraAlter = computed(() => altersWithoutRowPrefix.has(props.alter))
const getRowPrefix = (row: number) => {
const lastRow = skillGrid.value.rows.at(-1) ?? -1
if (lastRow >= rowPrefixes.length && row >= rowPrefixes.length - 1)
return row === lastRow ? rowPrefixes.at(-1) : ''
return rowPrefixes[row]
}
const lvList = [15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100]
const columns = [...new Array(11).keys()]
const activeSkill = ref<number>(-1)
const getLastPositionedSkill = (learnLv: number) =>
skills.value
.filter((skill) => skill.learnLv == learnLv && skill.type == 'active' && skill.bind)
.sort((left, right) => {
const [leftColumn = -1, leftRow = -1] = left.cellPosition ?? []
const [rightColumn = -1, rightRow = -1] = right.cellPosition ?? []
return leftRow - rightRow || leftColumn - rightColumn
})
.at(-1)
const extraAwakeSkill_2 = computed(() =>
props.alter == 'creator'
? skills.value.find((skill) => skill.learnLv == 85 && skill.name == '生命之树')
: getLastPositionedSkill(85),
)
const extraAwakeSkill_3 = computed(() => getLastPositionedSkill(100))
watchEffect(() => {
if (isExtraAlter.value && bindAwake.value != 85) bindAwake.value = 85
})
const chooseSkill = (skill?: ISkill, operation: string = 'add') => {
if (!skill) {
activeSkill.value = -1
@@ -66,19 +109,25 @@ export default defineComponent({
const actionSkillLv = (skill: ISkill, operation: string) => {
if (operation == 'add') {
lvInfo.value[skill.id.toString()].lv = Math.min(lvInfo.value[skill.id.toString()]?.lv + 1, skill.maxLv)
lvInfo.value[skill.id.toString()].lv = Math.min(
lvInfo.value[skill.id.toString()]?.lv + 1,
skill.maxLv,
)
return
}
if (operation == 'addMax') {
lvInfo.value[skill.id.toString()].lv = skill.maxLearnLv
lvInfo.value[skill.id.toString()].lv = skill.maxLearnLv
return
}
if (operation == 'sub') {
lvInfo.value[skill.id.toString()].lv = Math.max(lvInfo.value[skill.id.toString()]?.lv - 1, 0)
lvInfo.value[skill.id.toString()].lv = Math.max(
lvInfo.value[skill.id.toString()]?.lv - 1,
0,
)
return
}
if (operation == 'subMax') {
lvInfo.value[skill.id.toString()] = { lv: 0 , vp: 0, up: 0 }
lvInfo.value[skill.id.toString()] = { lv: 0, vp: 0, up: 0 }
return
}
emit('chooseSkill', skill.id)
@@ -88,78 +137,86 @@ export default defineComponent({
bindAwake.value = lv
}
const renderSkill = (skill: ISkill, index: number) => {
const hasAwakeBinding = (skill: ISkill) =>
isExtraAlter.value
? skill.id == extraAwakeSkill_3.value?.id
: skill.learnLv == 100 && skill.bind
const renderExtraAwakeBinding = () => (
<div class="absolute extra-awake-85 w-117px h-54px left-0 top--3px box-border">
<div class="absolute w-36px h-48px left-78px top-3.5px flex flex-col box-border items-center py-3px z-2">
<img class="h-28px w-28px" src={getImageURL(extraAwakeSkill_2.value?.icon ?? '')}></img>
<div class="w-100% flex flex-1 justify-center text-#ffc701 pt-1.5px">2次</div>
</div>
</div>
)
const renderSkill = (skill: ISkill) => {
const info = lvInfo.value?.[skill.id.toString()]
let color = 'white'
if (info?.lv < skill.maxLearnLv) color = '#32E432'
if (info?.lv > skill.maxLearnLv) color = '#58E0FC'
return (
<>
<div class="relative w-35px h-55px">
<div class="flex w-full items-center justify-center gap-2px absolute top--18px left-1px">
{info?.vp > 0 && (<div class={`vp_${info.vp}`}></div>)}
{info?.up > 0 && (<div class={`up_${info.up}`}></div>)}
</div>
{activeSkill.value == skill.id && renderSkillAction(skill)}
<div
class={[
'w-36px absolute h-48px flex flex-col box-border items-center py-3px z-2',
skill.learnLv == 100 && skill.bind ? '' : 'skill',
]}
onClick={(e) => {
e.stopPropagation()
chooseSkill(skill)
}}
onContextmenu={(e) => {
e.stopPropagation()
e.preventDefault()
chooseSkill(skill, 'sub')
}}
key={index}
>
<img
class={['h-28px w-28px', skill.type === 'passive' ? 'passive' : '']}
src={getImageURL(skill.icon)}
alt={skill.name}
></img>
<div class="w-100% flex flex-1 justify-center" style={{ color }}>
{info?.lv ?? 0}
</div>
</div>
{skill.learnLv == 100 && skill.bind && (
<>
<div
class={`pos-absolute w-196px h-54px box-border translate-x--50% left-18px top--3px flex justify-between items-center py-2px px-3px awake_${bindAwake.value}`}
>
<div
class="w-36px flex flex-col box-border items-center py-2px z-2"
onClick={() => chooseBindAwake(50)}
>
<img
class="h-28px w-28px mt-2px"
src={getImageURL(awakeSkill_1.value?.icon ?? '')}
></img>
<div class="w-100% mt-1px flex items-center justify-center text-#ffc701">
1次
</div>
</div>
<div
class="w-36px flex flex-col box-border items-center py-2px z-2"
onClick={() => chooseBindAwake(85)}
>
<img
class="h-28px w-28px mt-1px"
src={getImageURL(awakeSkill_2.value?.icon ?? '')}
></img>
<div class="w-100% mt-1px flex items-center justify-center text-#ffc701">
2次
</div>
</div>
</div>
</>
)}
<div class="relative w-35px h-55px" key={skill.id}>
<div class="flex w-full items-center justify-center gap-2px absolute top--18px left-1px">
{info?.vp > 0 && <div class={`vp_${info.vp}`}></div>}
{info?.up > 0 && <div class={`up_${info.up}`}></div>}
</div>
</>
{activeSkill.value == skill.id && renderSkillAction(skill)}
<div
class={[
'w-36px absolute h-48px flex flex-col box-border items-center py-3px z-2',
hasAwakeBinding(skill) ? '' : 'skill',
]}
onClick={(e) => {
e.stopPropagation()
chooseSkill(skill)
}}
onContextmenu={(e) => {
e.stopPropagation()
e.preventDefault()
chooseSkill(skill, 'sub')
}}
>
<img
class={['h-28px w-28px', skill.type === 'passive' ? 'passive' : '']}
src={getImageURL(skill.icon)}
alt={skill.name}
></img>
<div class="w-100% flex flex-1 justify-center pt-1.5px" style={{ color }}>
{info?.lv ?? 0}
</div>
</div>
{hasAwakeBinding(skill) &&
(isExtraAlter.value ? (
renderExtraAwakeBinding()
) : (
<div
class={`pos-absolute w-196px h-54px box-border translate-x--50% left-18px top--3px flex justify-between items-center py-2px px-3px awake_${bindAwake.value}`}
>
<div
class="w-36px flex flex-col box-border items-center py-2px z-2"
onClick={() => chooseBindAwake(50)}
>
<img
class="h-28px w-28px mt-2px"
src={getImageURL(awakeSkill_1.value?.icon ?? '')}
></img>
<div class="w-100% mt-1px flex items-center justify-center text-#ffc701">1次</div>
</div>
<div
class="w-36px flex flex-col box-border items-center py-2px z-2"
onClick={() => chooseBindAwake(85)}
>
<img
class="h-28px w-28px mt-1px"
src={getImageURL(awakeSkill_2.value?.icon ?? '')}
></img>
<div class="w-100% mt-1px flex items-center justify-center text-#ffc701">2次</div>
</div>
</div>
))}
</div>
)
}
@@ -236,37 +293,24 @@ export default defineComponent({
return () => (
<>
<div onClick={() => (activeSkill.value = -1)}>
{((getSkill(0, 15, -1) ?? []) as ISkill[]).length > 0 && (
<>
<div class="skill-tree-line flex items-center gap-8px px-5px">
<div class="w-34px h-34px flex items-center justify-center text-white">
其<br />余
{skillGrid.value.rows.map((row) => (
<div class="skill-tree-line flex items-center gap-8px px-5px" key={row}>
{showRowPrefix.value && (
<div class="w-34px h-34px flex shrink-0 items-center justify-center text-white">
{getRowPrefix(row)}
</div>
{((getSkill(0, 15, -1) ?? []) as ISkill[])?.map((skill, index) =>
renderSkill(skill, index),
)}
</div>
</>
)}
{lvList.map((lv, index) =>
((getSkill(lv, lvList[index + 1] ?? 150, -1) as ISkill[])?.length ?? 0) > 0 ? (
<div class="skill-tree-line flex items-center gap-8px px-5px" key={index}>
{lv == 1 || lv % 10 == 0 ? (
<div class="w-34px h-34px flex items-center justify-center text-white">{lv}</div>
)}
{skillGrid.value.columns.map((column) => {
const skill = skillGrid.value.grid.get(`${row}:${column}`)
return skill ? (
renderSkill(skill)
) : (
<div class="w-34px h-34px"></div>
)}
{columns.map((column, i) => {
const skill = getSkill(lv, lvList[index + 1] ?? 150, column) as ISkill
return skill ? renderSkill(skill, index) : <div class="w-34px h-34px"></div>
})}
<div class="w-5px h-34px" onClick={() => (activeSkill.value = -1)}></div>
</div>
) : (
<></>
),
)}
<div class="w-34px h-34px shrink-0" key={column}></div>
)
})}
<div class="w-5px h-34px shrink-0" onClick={() => (activeSkill.value = -1)}></div>
</div>
))}
</div>
</>
)
@@ -34,6 +34,11 @@
background-size: 100% 100%;
}
.extra-awake-85 {
background-image: url('./img/extra_awake_85.png');
background-size: 100% 100%;
}
.vp_1{
width: 18px;
height: 18px;
@@ -22,6 +22,7 @@
<div class="overflow-y-auto h-full box-border" v-if="tab === 0">
<SkillTree
:skills="infoStore.skills"
:alter="infoStore.infos?.alter"
v-model:lvInfo="configStore.config.skills"
v-model:bindAwake="configStore.config.bindAwake"
@chooseSkill="setCurrentSkill"