poi-plugin-quest-info-2 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -2
- package/build/kcQuestsData/DATA_VERSION +1 -1
- package/build/kcQuestsData/index.ts +1 -1
- package/build/kcQuestsData/quests-scn-new.json +1 -3
- package/build/kcQuestsData/quests-scn.json +72 -72
- package/build/kcanotifyGamedata/DATA_VERSION +1 -1
- package/build/kcanotifyGamedata/index.ts +1 -1
- package/build/kcanotifyGamedata/quests-en.json +12 -0
- package/build/kcanotifyGamedata/quests-jp.json +12 -0
- package/build/kcanotifyGamedata/quests-ko.json +12 -5
- package/build/kcanotifyGamedata/quests-scn.json +12 -0
- package/build/kcanotifyGamedata/quests-tcn.json +12 -0
- package/build/prePostQuest.json +4498 -0
- package/build/questCategory.json +590 -588
- package/build/questCodeMap.json +575 -0
- package/i18n/en-US.json +1 -0
- package/i18n/ja-JP.json +1 -0
- package/i18n/ko-KR.json +1 -0
- package/i18n/zh-CN.json +1 -0
- package/i18n/zh-TW.json +1 -0
- package/package.json +15 -15
- package/src/__tests__/__snapshots__/questCategory.spec.ts.snap +113 -113
- package/src/__tests__/kcanotifyData.spec.ts +14 -1
- package/src/__tests__/kcwikiData.spec.ts +11 -1
- package/src/__tests__/questCategory.spec.ts +4 -4
- package/src/components/QuestCard/MinimalQuestCard.tsx +14 -14
- package/src/components/QuestCard/index.tsx +90 -55
- package/src/components/QuestCard/styles.ts +17 -0
- package/src/components/QuestList.tsx +5 -8
- package/src/components/QuestTag.tsx +104 -0
- package/src/poi/hooks.ts +8 -5
- package/src/questHelper.ts +25 -4
- package/src/store/kcwiki.ts +7 -3
- package/src/store/quest.ts +91 -10
- package/src/tags.tsx +2 -2
- package/scripts/convertAssets.ts +0 -57
- package/scripts/downloadKcQuestsData.ts +0 -136
- package/scripts/downloadKcanotifyGamedata.ts +0 -132
- package/scripts/downloadSprites.ts +0 -126
- package/scripts/genQuestCategory.ts +0 -58
- package/scripts/proxyFetch.ts +0 -42
- package/scripts/utils.ts +0 -8
- package/src/components/PreTaskTag.tsx +0 -40
package/src/store/quest.ts
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
import { useCallback } from 'react'
|
|
2
|
-
import { QuestData } from '../../build/kcanotifyGamedata'
|
|
3
2
|
import { useGameQuest, usePluginTranslation } from '../poi/hooks'
|
|
4
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
DocQuest,
|
|
5
|
+
getCategory,
|
|
6
|
+
getKcanotifyQuestData,
|
|
7
|
+
getQuestIdByCode,
|
|
8
|
+
getQuestPrePost,
|
|
9
|
+
QUEST_STATUS,
|
|
10
|
+
} from '../questHelper'
|
|
5
11
|
import type { UnionQuest } from '../questHelper'
|
|
6
12
|
import { useKcwikiData, checkIsKcwikiSupportedLanguages } from './kcwiki'
|
|
7
13
|
import { useStore, useSyncWithGame } from './store'
|
|
8
14
|
|
|
9
15
|
const DEFAULT_LANG = 'ja-JP'
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
const checkIsKcanotifySupportedLanguages = (
|
|
12
18
|
lang: string
|
|
13
|
-
): lang is keyof typeof
|
|
19
|
+
): lang is keyof typeof kcaQuestData => {
|
|
20
|
+
const kcaQuestData = getKcanotifyQuestData()
|
|
21
|
+
return lang in kcaQuestData
|
|
22
|
+
}
|
|
14
23
|
|
|
15
24
|
export const isSupportedLanguages = (
|
|
16
25
|
lang: string
|
|
17
|
-
): lang is keyof typeof
|
|
26
|
+
): lang is keyof ReturnType<typeof getKcanotifyQuestData> =>
|
|
18
27
|
checkIsKcanotifySupportedLanguages(lang) ||
|
|
19
28
|
checkIsKcwikiSupportedLanguages(lang)
|
|
20
29
|
|
|
@@ -28,13 +37,14 @@ export const useLanguage = () => {
|
|
|
28
37
|
return lang
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
const useQuestMap = () => {
|
|
40
|
+
const useQuestMap = (): Record<string, DocQuest> => {
|
|
32
41
|
const lang = useLanguage()
|
|
33
42
|
const kcwikiData = useKcwikiData(lang)
|
|
34
43
|
if (kcwikiData) {
|
|
35
44
|
return kcwikiData
|
|
36
45
|
}
|
|
37
|
-
|
|
46
|
+
const kcaQuestData = getKcanotifyQuestData()
|
|
47
|
+
return kcaQuestData[lang]
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
export const useQuest = (): UnionQuest[] => {
|
|
@@ -44,12 +54,12 @@ export const useQuest = (): UnionQuest[] => {
|
|
|
44
54
|
|
|
45
55
|
if (syncWithGame && gameQuest.length) {
|
|
46
56
|
return gameQuest.map((quest) => {
|
|
47
|
-
const gameId =
|
|
57
|
+
const gameId = quest.api_no
|
|
48
58
|
if (gameId in docQuestMap) {
|
|
49
59
|
return {
|
|
50
60
|
gameId,
|
|
51
61
|
gameQuest: quest,
|
|
52
|
-
docQuest: docQuestMap[gameId as keyof typeof docQuestMap],
|
|
62
|
+
docQuest: docQuestMap[String(gameId) as keyof typeof docQuestMap],
|
|
53
63
|
}
|
|
54
64
|
}
|
|
55
65
|
|
|
@@ -68,7 +78,7 @@ export const useQuest = (): UnionQuest[] => {
|
|
|
68
78
|
} else {
|
|
69
79
|
// Return all recorded quests
|
|
70
80
|
return Object.entries(docQuestMap).map(([gameId, val]) => ({
|
|
71
|
-
gameId,
|
|
81
|
+
gameId: +gameId,
|
|
72
82
|
// Maybe empty
|
|
73
83
|
gameQuest: gameQuest.find((quest) => quest.api_no === Number(gameId))!,
|
|
74
84
|
docQuest: val,
|
|
@@ -76,6 +86,77 @@ export const useQuest = (): UnionQuest[] => {
|
|
|
76
86
|
}
|
|
77
87
|
}
|
|
78
88
|
|
|
89
|
+
export const useQuestByCode = (code: string): UnionQuest | null => {
|
|
90
|
+
const questMap = useQuestMap()
|
|
91
|
+
const gameId = getQuestIdByCode(code)
|
|
92
|
+
if (gameId && gameId in questMap) {
|
|
93
|
+
return {
|
|
94
|
+
gameId,
|
|
95
|
+
docQuest: questMap[String(gameId) as keyof typeof questMap],
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const useCompletedQuest = () => {
|
|
102
|
+
const completedQuest: Record<number, true> = {}
|
|
103
|
+
const gameQuest = useGameQuest()
|
|
104
|
+
const queue: number[] = gameQuest.map((quest) => quest.api_no)
|
|
105
|
+
while (queue.length) {
|
|
106
|
+
const gameId = queue.shift()!
|
|
107
|
+
if (gameId in completedQuest) {
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
completedQuest[gameId] = true
|
|
111
|
+
|
|
112
|
+
const prePostQuests = getQuestPrePost(gameId)
|
|
113
|
+
prePostQuests.pre.forEach((nextCode) => {
|
|
114
|
+
const nextGameId = getQuestIdByCode(nextCode)
|
|
115
|
+
if (nextGameId) {
|
|
116
|
+
queue.push(nextGameId)
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
return completedQuest
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const useLockedQuest = () => {
|
|
124
|
+
const lockedQuest: Record<number, true> = {}
|
|
125
|
+
const gameQuest = useGameQuest()
|
|
126
|
+
const queue: number[] = gameQuest.map((quest) => quest.api_no)
|
|
127
|
+
while (queue.length) {
|
|
128
|
+
const gameId = queue.shift()!
|
|
129
|
+
if (gameId in lockedQuest) {
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
lockedQuest[gameId] = true
|
|
133
|
+
const prePostQuests = getQuestPrePost(gameId)
|
|
134
|
+
prePostQuests.post.forEach((nextCode) => {
|
|
135
|
+
const nextGameId = getQuestIdByCode(nextCode)
|
|
136
|
+
if (nextGameId) {
|
|
137
|
+
queue.push(nextGameId)
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
return lockedQuest
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const useQuestStatus = (gameId: number | null) => {
|
|
145
|
+
const completedQuest = useCompletedQuest()
|
|
146
|
+
const lockedQuest = useLockedQuest()
|
|
147
|
+
|
|
148
|
+
if (!gameId) {
|
|
149
|
+
return QUEST_STATUS.DEFAULT
|
|
150
|
+
}
|
|
151
|
+
if (gameId in completedQuest) {
|
|
152
|
+
return QUEST_STATUS.ALREADY_COMPLETED
|
|
153
|
+
}
|
|
154
|
+
if (gameId in lockedQuest) {
|
|
155
|
+
return QUEST_STATUS.LOCKED
|
|
156
|
+
}
|
|
157
|
+
return QUEST_STATUS.DEFAULT
|
|
158
|
+
}
|
|
159
|
+
|
|
79
160
|
/**
|
|
80
161
|
* @deprecated Not large card now
|
|
81
162
|
*/
|
package/src/tags.tsx
CHANGED
|
@@ -38,12 +38,12 @@ export const ALL_CATEGORY_TAG = {
|
|
|
38
38
|
export const ALL_TYPE_TAG = ALL_CATEGORY_TAG
|
|
39
39
|
|
|
40
40
|
const withDocQuest =
|
|
41
|
-
<T
|
|
41
|
+
<T,>(filterFn: (q: UnionQuest['docQuest']) => T) =>
|
|
42
42
|
(unionQuest: UnionQuest) =>
|
|
43
43
|
filterFn(unionQuest.docQuest)
|
|
44
44
|
|
|
45
45
|
const withGameQuestOr =
|
|
46
|
-
<T
|
|
46
|
+
<T,>(filterFn: (q: GameQuest) => T, fallback: T) =>
|
|
47
47
|
({ gameQuest }: UnionQuest) => {
|
|
48
48
|
if (!gameQuest) {
|
|
49
49
|
return fallback
|
package/scripts/convertAssets.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import { prepareDir } from './utils'
|
|
4
|
-
|
|
5
|
-
const ASSETS_PATH = path.resolve('assets')
|
|
6
|
-
const OUTPUT_PATH = path.resolve('build')
|
|
7
|
-
const OUTPUT_FILE = path.resolve(OUTPUT_PATH, 'assets.ts')
|
|
8
|
-
const CONVERT_EXTS = ['jpg', 'png'] as const
|
|
9
|
-
|
|
10
|
-
const HEADER = `/* eslint-disable prettier/prettier */
|
|
11
|
-
/**
|
|
12
|
-
* This file was automatically generated by \`${path.relative(
|
|
13
|
-
// project root
|
|
14
|
-
process.cwd(),
|
|
15
|
-
__filename
|
|
16
|
-
)}\`
|
|
17
|
-
* Do not edit this file directly.
|
|
18
|
-
*/` as const
|
|
19
|
-
|
|
20
|
-
function base64Encode(file: string) {
|
|
21
|
-
const bitmap = fs.readFileSync(file)
|
|
22
|
-
return bitmap.toString('base64')
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function main() {
|
|
26
|
-
prepareDir(OUTPUT_PATH)
|
|
27
|
-
const imageData = fs
|
|
28
|
-
.readdirSync(ASSETS_PATH)
|
|
29
|
-
// exclusive ignored ext
|
|
30
|
-
.filter((f) => CONVERT_EXTS.some((ext) => f.endsWith('.' + ext)))
|
|
31
|
-
.map((fileName) => {
|
|
32
|
-
const filePath = path.resolve(ASSETS_PATH, fileName)
|
|
33
|
-
const parsedFile = path.parse(fileName)
|
|
34
|
-
return {
|
|
35
|
-
name: parsedFile.name,
|
|
36
|
-
ext: parsedFile.ext.slice(1),
|
|
37
|
-
base64: base64Encode(filePath),
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
const data = `${HEADER}
|
|
42
|
-
|
|
43
|
-
${imageData
|
|
44
|
-
.map(
|
|
45
|
-
({ name, ext, base64 }) =>
|
|
46
|
-
`export const ${name} = 'data:image/${ext};base64, ${base64}'`
|
|
47
|
-
)
|
|
48
|
-
.join('\n')}
|
|
49
|
-
`
|
|
50
|
-
|
|
51
|
-
fs.writeFileSync(OUTPUT_FILE, data)
|
|
52
|
-
|
|
53
|
-
// eslint-disable-next-line no-console
|
|
54
|
-
console.log('Converted', imageData.length, 'images.')
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
main()
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
|
3
|
-
import pangu from 'pangu'
|
|
4
|
-
import path from 'path'
|
|
5
|
-
import { fetch } from './proxyFetch'
|
|
6
|
-
import { prepareDir } from './utils'
|
|
7
|
-
|
|
8
|
-
// See https://github.com/kcwikizh/kcQuests
|
|
9
|
-
|
|
10
|
-
const OUTPUT_PATH = path.resolve('build', 'kcQuestsData')
|
|
11
|
-
const DATA_FILE_NAME = 'quests-scn.json'
|
|
12
|
-
const DATA_URL = `https://kcwikizh.github.io/kcQuests/${DATA_FILE_NAME}`
|
|
13
|
-
const NEW_QUEST_FILE_NAME = 'quests-scn-new.json'
|
|
14
|
-
const NEW_QUEST_URL = `https://kcwikizh.github.io/kcQuests/${NEW_QUEST_FILE_NAME}`
|
|
15
|
-
const VERSION_URL =
|
|
16
|
-
'https://api.github.com/repos/kcwikizh/kcQuests/branches/main'
|
|
17
|
-
|
|
18
|
-
// maybe need ignore some expired quest
|
|
19
|
-
const IGNORE_DATA = [] as const
|
|
20
|
-
|
|
21
|
-
const getRemoteVersion = async () => {
|
|
22
|
-
const resp = await fetch(VERSION_URL)
|
|
23
|
-
if (!resp.ok) {
|
|
24
|
-
throw new Error(`Fetch Error!\nurl: ${resp.url}\nstatus: ${resp.status}`)
|
|
25
|
-
}
|
|
26
|
-
return (await resp.json()).commit.sha
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const getLocalVersion = () => {
|
|
30
|
-
const localVersionFile = path.resolve(OUTPUT_PATH, 'DATA_VERSION')
|
|
31
|
-
const version = existsSync(localVersionFile)
|
|
32
|
-
? readFileSync(localVersionFile).toString()
|
|
33
|
-
: '0'
|
|
34
|
-
return version
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @example
|
|
39
|
-
* ```ts
|
|
40
|
-
* import zh_CN from './quests-scn.json'
|
|
41
|
-
* export default {
|
|
42
|
-
* 'zh-CN': zh_CN,
|
|
43
|
-
* }
|
|
44
|
-
* export const version = '5.1.2.1'
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
const genTS = (version: string) => {
|
|
48
|
-
const importCode = `import zh_CN from './quests-scn.json'`
|
|
49
|
-
|
|
50
|
-
const exportCode = [
|
|
51
|
-
'export const KcwikiQuestData = {',
|
|
52
|
-
` 'zh-CN': zh_CN,`,
|
|
53
|
-
'}',
|
|
54
|
-
].join('\n')
|
|
55
|
-
|
|
56
|
-
const versionCode = `export const version = '${version}'`
|
|
57
|
-
return `${importCode}\n\n${exportCode}\n\n${versionCode}\n`
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const downloadQuestData = async () => {
|
|
61
|
-
const resp = await fetch(DATA_URL)
|
|
62
|
-
if (!resp.ok) {
|
|
63
|
-
console.error(`Fetch Error!\nurl: ${resp.url}\nstatus: ${resp.status}`)
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
const text = await resp.text()
|
|
67
|
-
|
|
68
|
-
const json = JSON.parse(text) as {
|
|
69
|
-
[gameId: string]: {
|
|
70
|
-
code: string
|
|
71
|
-
name: string
|
|
72
|
-
desc: string
|
|
73
|
-
memo?: string
|
|
74
|
-
memo2?: string
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
for (const gameId in json) {
|
|
78
|
-
const { code, name, desc, memo, memo2 } = json[gameId]
|
|
79
|
-
json[gameId].code = code.trim()
|
|
80
|
-
json[gameId].name = pangu.spacing(name)
|
|
81
|
-
json[gameId].desc = pangu.spacing(desc)
|
|
82
|
-
if (memo) {
|
|
83
|
-
json[gameId].memo = pangu.spacing(memo)
|
|
84
|
-
}
|
|
85
|
-
if (memo2) {
|
|
86
|
-
json[gameId].memo2 = pangu.spacing(memo2)
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
IGNORE_DATA.forEach((gameId) => delete json[gameId])
|
|
91
|
-
const data = JSON.stringify(json, undefined, 2)
|
|
92
|
-
writeFileSync(path.resolve(OUTPUT_PATH, DATA_FILE_NAME), data)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const downloadNewQuest = async () => {
|
|
96
|
-
const resp = await fetch(NEW_QUEST_URL)
|
|
97
|
-
if (!resp.ok) {
|
|
98
|
-
console.error(`Fetch Error!\nurl: ${resp.url}\nstatus: ${resp.status}`)
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
const text = await resp.text()
|
|
102
|
-
const json = Object.keys(JSON.parse(text))
|
|
103
|
-
const data = JSON.stringify(json, undefined, 2)
|
|
104
|
-
writeFileSync(path.resolve(OUTPUT_PATH, NEW_QUEST_FILE_NAME), data)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const main = async () => {
|
|
108
|
-
const args = process.argv.slice(2)
|
|
109
|
-
|
|
110
|
-
prepareDir(OUTPUT_PATH)
|
|
111
|
-
const remoteVersion = await getRemoteVersion()
|
|
112
|
-
const localVersion = getLocalVersion()
|
|
113
|
-
if (remoteVersion === localVersion) {
|
|
114
|
-
console.log('The local version is up to date. Version:', localVersion)
|
|
115
|
-
if (!args.find((v) => v === '-f' || v === '--force')) {
|
|
116
|
-
return
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
console.log('New Version Detected. Version:', remoteVersion)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
console.log(`Download kcQuests data...`)
|
|
123
|
-
await Promise.all([downloadQuestData(), downloadNewQuest()])
|
|
124
|
-
|
|
125
|
-
const ts = genTS(remoteVersion)
|
|
126
|
-
writeFileSync(`${OUTPUT_PATH}/index.ts`, ts)
|
|
127
|
-
|
|
128
|
-
// Finally record the version number
|
|
129
|
-
writeFileSync(`${OUTPUT_PATH}/DATA_VERSION`, remoteVersion)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
main()
|
|
133
|
-
|
|
134
|
-
process.on('unhandledRejection', (up) => {
|
|
135
|
-
throw up
|
|
136
|
-
})
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
|
3
|
-
import pangu from 'pangu'
|
|
4
|
-
import path from 'path'
|
|
5
|
-
import { fetch } from './proxyFetch'
|
|
6
|
-
import { prepareDir } from './utils'
|
|
7
|
-
|
|
8
|
-
// See https://github.com/antest1/kcanotify-gamedata
|
|
9
|
-
|
|
10
|
-
const OUTPUT_PATH = path.resolve('build', 'kcanotifyGamedata')
|
|
11
|
-
const URL_PREFIX =
|
|
12
|
-
'https://raw.githubusercontent.com/antest1/kcanotify-gamedata/master'
|
|
13
|
-
const VERSION_URL = `${URL_PREFIX}/KCAINFO`
|
|
14
|
-
const DATA_URL = `${URL_PREFIX}/files`
|
|
15
|
-
const LANGS = ['scn', 'tcn', 'jp', 'en', 'ko'] as const
|
|
16
|
-
const LOCALES = ['zh-CN', 'zh-TW', 'ja-JP', 'en-US', 'ko-KR'] as const
|
|
17
|
-
|
|
18
|
-
const getRemoteVersion = async () => {
|
|
19
|
-
const resp = await fetch(VERSION_URL)
|
|
20
|
-
if (!resp.ok) {
|
|
21
|
-
throw new Error(`Fetch Error!\nurl: ${resp.url}\nstatus: ${resp.status}`)
|
|
22
|
-
}
|
|
23
|
-
const KCAINFO: {
|
|
24
|
-
version: string
|
|
25
|
-
data_version: string
|
|
26
|
-
kcadata_version: number
|
|
27
|
-
kc_maintenance: string[]
|
|
28
|
-
} = await resp.json()
|
|
29
|
-
|
|
30
|
-
return String(KCAINFO.kcadata_version)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const getLocalVersion = () => {
|
|
34
|
-
const localVersionFile = path.resolve(OUTPUT_PATH, 'DATA_VERSION')
|
|
35
|
-
const version = existsSync(localVersionFile)
|
|
36
|
-
? readFileSync(localVersionFile).toString()
|
|
37
|
-
: '0'
|
|
38
|
-
return version
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @example
|
|
43
|
-
* ```ts
|
|
44
|
-
* import zh_CN from './quests-scn.json'
|
|
45
|
-
* export default {
|
|
46
|
-
* 'zh-CN': zh_CN,
|
|
47
|
-
* }
|
|
48
|
-
* export const version = '5.1.2.1'
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
const genTS = (version: string) => {
|
|
52
|
-
const importCode = LOCALES.map(
|
|
53
|
-
(locale, idx) =>
|
|
54
|
-
`import ${locale.replace('-', '_')} from './quests-${LANGS[idx]}.json'`
|
|
55
|
-
).join('\n')
|
|
56
|
-
|
|
57
|
-
const exportCode =
|
|
58
|
-
'export const QuestData = {\n' +
|
|
59
|
-
LOCALES.map((locale) => ` '${locale}': ${locale.replace('-', '_')},`).join(
|
|
60
|
-
'\n'
|
|
61
|
-
) +
|
|
62
|
-
'\n}'
|
|
63
|
-
|
|
64
|
-
const versionCode = `export const version = '${version}'`
|
|
65
|
-
return `${importCode}\n\n${exportCode}\n\n${versionCode}\n`
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const main = async () => {
|
|
69
|
-
const args = process.argv.slice(2)
|
|
70
|
-
|
|
71
|
-
prepareDir(OUTPUT_PATH)
|
|
72
|
-
const remoteVersion = await getRemoteVersion()
|
|
73
|
-
const localVersion = getLocalVersion()
|
|
74
|
-
if (remoteVersion === localVersion) {
|
|
75
|
-
console.log('The local version is up to date. Version:', localVersion)
|
|
76
|
-
if (!args.find((v) => v === '-f' || v === '--force')) {
|
|
77
|
-
return
|
|
78
|
-
}
|
|
79
|
-
} else {
|
|
80
|
-
console.log('New Version Detected. Version:', remoteVersion)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
await Promise.all(
|
|
84
|
-
LANGS.map(async (lang) => {
|
|
85
|
-
const filename = `quests-${lang}.json`
|
|
86
|
-
const fileURL = `${DATA_URL}/${filename}`
|
|
87
|
-
|
|
88
|
-
console.log(`Download ${filename}...`)
|
|
89
|
-
const resp = await fetch(fileURL)
|
|
90
|
-
if (!resp.ok) {
|
|
91
|
-
console.error(`Fetch Error!\nurl: ${resp.url}\nstatus: ${resp.status}`)
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
let text = await resp.text()
|
|
95
|
-
// TODO fix source file
|
|
96
|
-
// Remove BOM(U+FEFF) from the header of the quests-ko.json
|
|
97
|
-
text = text.trim()
|
|
98
|
-
|
|
99
|
-
const json = JSON.parse(text) as {
|
|
100
|
-
[gameId: string]: {
|
|
101
|
-
code: string
|
|
102
|
-
name: string
|
|
103
|
-
desc: string
|
|
104
|
-
memo?: string
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
for (const gameId in json) {
|
|
108
|
-
const { name, desc, memo } = json[gameId]
|
|
109
|
-
json[gameId].name = pangu.spacing(name)
|
|
110
|
-
json[gameId].desc = pangu.spacing(desc)
|
|
111
|
-
if (memo) {
|
|
112
|
-
json[gameId].memo = pangu.spacing(memo)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const data = JSON.stringify(json, undefined, 2)
|
|
117
|
-
writeFileSync(`${OUTPUT_PATH}/${filename}`, data)
|
|
118
|
-
})
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
const ts = genTS(remoteVersion)
|
|
122
|
-
writeFileSync(`${OUTPUT_PATH}/index.ts`, ts)
|
|
123
|
-
|
|
124
|
-
// Finally record the version number
|
|
125
|
-
writeFileSync(`${OUTPUT_PATH}/DATA_VERSION`, remoteVersion)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
main()
|
|
129
|
-
|
|
130
|
-
process.on('unhandledRejection', (up) => {
|
|
131
|
-
throw up
|
|
132
|
-
})
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import fs from 'fs'
|
|
4
|
-
import crypto from 'crypto'
|
|
5
|
-
// See https://sharp.pixelplumbing.com/
|
|
6
|
-
import sharp from 'sharp'
|
|
7
|
-
import { fetch } from './proxyFetch'
|
|
8
|
-
import { prepareDir } from './utils'
|
|
9
|
-
|
|
10
|
-
const OUTPUT_PATH = path.resolve('build', 'dutySprites')
|
|
11
|
-
|
|
12
|
-
const SERVER_URL = 'http://203.104.209.199'
|
|
13
|
-
const JSON_PATH = '/kcs2/img/duty/duty_main.json'
|
|
14
|
-
const SPRITES_PATH = '/kcs2/img/duty/duty_main.png'
|
|
15
|
-
const VERSION = '5.1.2.0'
|
|
16
|
-
const SPRITES_URL = `${SERVER_URL}${SPRITES_PATH}?version=${VERSION}`
|
|
17
|
-
const META_URL = `${SERVER_URL}${JSON_PATH}?version=${VERSION}`
|
|
18
|
-
|
|
19
|
-
const getFilename = (url: string) => {
|
|
20
|
-
const pathname = new URL(url).pathname
|
|
21
|
-
const index = pathname.lastIndexOf('/')
|
|
22
|
-
return index !== -1 ? pathname.slice(index + 1) : pathname
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const download = async (url: string, filename?: string) => {
|
|
26
|
-
if (!filename) {
|
|
27
|
-
filename = getFilename(url)
|
|
28
|
-
}
|
|
29
|
-
const filePath = path.resolve(OUTPUT_PATH, filename)
|
|
30
|
-
const response = await fetch(url)
|
|
31
|
-
const buffer = await response.buffer()
|
|
32
|
-
await fs.writeFileSync(filePath, buffer)
|
|
33
|
-
return { filePath, buffer }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const checksumFile = (algorithm: string, path: fs.PathLike) => {
|
|
37
|
-
return new Promise<string>((resolve, reject) => {
|
|
38
|
-
const hash = crypto.createHash(algorithm)
|
|
39
|
-
const stream = fs.createReadStream(path)
|
|
40
|
-
stream.on('error', (err) => reject(err))
|
|
41
|
-
stream.on('data', (chunk) => hash.update(chunk))
|
|
42
|
-
stream.on('end', () => resolve(hash.digest('hex')))
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const cropAndSaveImage = (
|
|
47
|
-
img: Buffer,
|
|
48
|
-
{
|
|
49
|
-
name,
|
|
50
|
-
format,
|
|
51
|
-
x,
|
|
52
|
-
y,
|
|
53
|
-
w,
|
|
54
|
-
h,
|
|
55
|
-
}: {
|
|
56
|
-
name: string
|
|
57
|
-
format: string
|
|
58
|
-
x: number
|
|
59
|
-
y: number
|
|
60
|
-
w: number
|
|
61
|
-
h: number
|
|
62
|
-
}
|
|
63
|
-
) => {
|
|
64
|
-
const filename = `${name}.${format}`
|
|
65
|
-
sharp(img)
|
|
66
|
-
.extract({ left: x, top: y, width: w, height: h })
|
|
67
|
-
.toFile(path.resolve(OUTPUT_PATH, filename))
|
|
68
|
-
.catch((err) => {
|
|
69
|
-
console.error('Failed to process image:', filename, err)
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
type KCS2Meta = {
|
|
74
|
-
frames: {
|
|
75
|
-
[name: string]: {
|
|
76
|
-
frame: {
|
|
77
|
-
x: number
|
|
78
|
-
y: number
|
|
79
|
-
w: number
|
|
80
|
-
h: number
|
|
81
|
-
}
|
|
82
|
-
rotated: boolean
|
|
83
|
-
trimmed: boolean
|
|
84
|
-
spriteSourceSize: {
|
|
85
|
-
x: number
|
|
86
|
-
y: number
|
|
87
|
-
w: number
|
|
88
|
-
h: number
|
|
89
|
-
}
|
|
90
|
-
sourceSize: {
|
|
91
|
-
w: number
|
|
92
|
-
h: number
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
meta: any
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const parseSprites = (sprites: Buffer, meta: KCS2Meta) => {
|
|
100
|
-
const { frames } = meta
|
|
101
|
-
for (const [name, { frame }] of Object.entries(frames)) {
|
|
102
|
-
cropAndSaveImage(sprites, { ...frame, name, format: 'png' })
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const main = async () => {
|
|
107
|
-
prepareDir(OUTPUT_PATH)
|
|
108
|
-
const [
|
|
109
|
-
{ buffer: metaBuffer },
|
|
110
|
-
{ filePath: spritesPath, buffer: spritesBuffer },
|
|
111
|
-
] = await Promise.all([download(META_URL), download(SPRITES_URL)])
|
|
112
|
-
|
|
113
|
-
const spritesFilename = path.parse(spritesPath).base
|
|
114
|
-
const md5 = await checksumFile('md5', spritesPath)
|
|
115
|
-
fs.writeFileSync(path.resolve(OUTPUT_PATH, `${spritesFilename}.md5`), md5)
|
|
116
|
-
console.log('File download complete')
|
|
117
|
-
console.log(spritesFilename, 'MD5:', md5)
|
|
118
|
-
|
|
119
|
-
parseSprites(spritesBuffer, JSON.parse(metaBuffer.toString()))
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
main()
|
|
123
|
-
|
|
124
|
-
process.on('unhandledRejection', (up) => {
|
|
125
|
-
throw up
|
|
126
|
-
})
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { writeFileSync } from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import { QuestData } from '../build/kcanotifyGamedata'
|
|
4
|
-
import { KcwikiQuestData } from '../build/kcQuestsData'
|
|
5
|
-
|
|
6
|
-
const OUTPUT_PATH = path.resolve('build', 'questCategory.json')
|
|
7
|
-
|
|
8
|
-
const kcaQuestStartsFilter = (str: string) =>
|
|
9
|
-
Object.entries(QuestData['zh-CN'])
|
|
10
|
-
.filter(([, quest]) => quest.name.startsWith(str))
|
|
11
|
-
.map(([gameId]) => gameId)
|
|
12
|
-
|
|
13
|
-
const kcwikiDataSelector = () => Object.entries(KcwikiQuestData['zh-CN'])
|
|
14
|
-
const mergeDataSelector = () =>
|
|
15
|
-
Object.entries({ ...QuestData['zh-CN'], ...KcwikiQuestData['zh-CN'] })
|
|
16
|
-
|
|
17
|
-
const main = () => {
|
|
18
|
-
const dailyQuest = kcaQuestStartsFilter('(日任)')
|
|
19
|
-
const weeklyQuest = kcaQuestStartsFilter('(周任)')
|
|
20
|
-
const monthlyQuest = kcaQuestStartsFilter('(月任)')
|
|
21
|
-
const quarterlyQuest = [
|
|
22
|
-
...new Set([
|
|
23
|
-
...kcaQuestStartsFilter('(季任)'),
|
|
24
|
-
...kcwikiDataSelector()
|
|
25
|
-
.filter(([, quest]) => quest.memo2.includes('季常任务'))
|
|
26
|
-
.map(([gameId]) => gameId),
|
|
27
|
-
]),
|
|
28
|
-
].sort((a, b) => +a - +b)
|
|
29
|
-
// (年任) (年任 / x 月)
|
|
30
|
-
const yearlyQuest = kcwikiDataSelector()
|
|
31
|
-
.filter(([, quest]) => quest.memo2.includes('年常任务'))
|
|
32
|
-
.map(([gameId]) => gameId)
|
|
33
|
-
const singleQuest = mergeDataSelector()
|
|
34
|
-
.filter(
|
|
35
|
-
([gameId]) =>
|
|
36
|
-
![
|
|
37
|
-
...dailyQuest,
|
|
38
|
-
...weeklyQuest,
|
|
39
|
-
...monthlyQuest,
|
|
40
|
-
...quarterlyQuest,
|
|
41
|
-
...yearlyQuest,
|
|
42
|
-
].includes(gameId)
|
|
43
|
-
)
|
|
44
|
-
.map(([gameId]) => gameId)
|
|
45
|
-
|
|
46
|
-
const data = {
|
|
47
|
-
dailyQuest,
|
|
48
|
-
weeklyQuest,
|
|
49
|
-
monthlyQuest,
|
|
50
|
-
quarterlyQuest,
|
|
51
|
-
yearlyQuest,
|
|
52
|
-
singleQuest,
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
writeFileSync(OUTPUT_PATH, JSON.stringify(data, null, 2))
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
main()
|