@@ -22,18 +22,78 @@ const BASE = 'https://raw.githubusercontent.com/yoimiya-kokomi/miao-plugin'
2222const BRANCH = process . argv . includes ( '--branch' )
2323 ? process . argv [ process . argv . indexOf ( '--branch' ) + 1 ] : 'master'
2424
25+ // ============================================================
26+ // 本地覆盖
27+ //
28+ // 上游 miao-plugin 的数据不一定完全符合预期,比如某个角色的默认
29+ // 流派选得不对、权重有争议等等。这里可以覆盖 usefulAttr 的权重
30+ // 或者直接替换整条特殊规则,重新生成时会自动合入,不用担心被冲掉。
31+ // ============================================================
32+
33+ const LOCAL_OVERRIDES = {
34+ // 角色基础权重(只写需要改的字段即可,其余沿用上游)
35+ weights : {
36+ // 玛薇卡默认应该是蒸发/融化精通流,而不是纯火
37+ '玛薇卡' : { mastery : 100 }
38+ } ,
39+ // 角色特殊规则(会完全替换上游的 artis.js 转换结果)
40+ rules : {
41+ '玛薇卡' : `'玛薇卡': ({ charAttrs }) => {
42+ let particularAttr = {...usefulAttr['玛薇卡']}
43+ // 只有面板精通明确很低的时候才切到纯火/超载
44+ if ((charAttrs?.mastery || 0) > 0 && (charAttrs?.mastery || 0) < 40) {
45+ particularAttr.atk = 85
46+ particularAttr.mastery = 0
47+ return { title: '玛薇卡-纯火/超载', attrWeight: particularAttr, useDefaultPipeline: false }
48+ }
49+ // 其他情况一律按精通流处理
50+ particularAttr.mastery = 100
51+ return { title: '玛薇卡-精通', attrWeight: particularAttr, useDefaultPipeline: false }
52+ }`
53+ }
54+ }
55+
2556// ============================================================
2657// HTTP helpers
2758// ============================================================
2859
29- async function fetchRaw ( path ) {
60+ const RETRY_MAX = 3
61+ const RETRY_DELAY_MS = 1000
62+
63+ async function sleep ( ms ) {
64+ return new Promise ( resolve => setTimeout ( resolve , ms ) )
65+ }
66+
67+ /**
68+ * 带重试的 fetch。404 不重试,网络错误和服务端错误(5xx / 429)退避重试。
69+ */
70+ async function fetchRaw ( path , retries = RETRY_MAX ) {
3071 const url = `${ BASE } /${ BRANCH } /${ path } `
31- const res = await fetch ( url )
32- if ( ! res . ok ) {
33- if ( res . status === 404 ) return null
34- throw new Error ( `HTTP ${ res . status } for ${ path } ` )
72+ for ( let attempt = 1 ; attempt <= retries ; attempt ++ ) {
73+ try {
74+ const res = await fetch ( url )
75+ if ( res . ok ) return res . text ( )
76+ if ( res . status === 404 ) return null
77+
78+ // 服务端错误或限流,可重试
79+ if ( attempt < retries ) {
80+ const delay = RETRY_DELAY_MS * Math . pow ( 2 , attempt - 1 )
81+ console . warn ( ` HTTP ${ res . status } for ${ path } , retry ${ attempt } /${ retries } after ${ delay } ms` )
82+ await sleep ( delay )
83+ } else {
84+ console . warn ( ` HTTP ${ res . status } for ${ path } , exhausted retries, skipping` )
85+ }
86+ } catch ( e ) {
87+ if ( attempt < retries ) {
88+ const delay = RETRY_DELAY_MS * Math . pow ( 2 , attempt - 1 )
89+ console . warn ( ` Network error for ${ path } , retry ${ attempt } /${ retries } after ${ delay } ms` )
90+ await sleep ( delay )
91+ } else {
92+ console . warn ( ` Network error for ${ path } , exhausted retries, skipping` )
93+ }
94+ }
3595 }
36- return res . text ( )
96+ return null
3797}
3898
3999async function fetchJson ( path ) {
@@ -1069,6 +1129,14 @@ async function main() {
10691129 const usefulAttr = sortObjectByName ( parseUsefulAttr ( artisMarkText ) )
10701130 console . log ( ` usefulAttr: ${ Object . keys ( usefulAttr ) . length } characters` )
10711131
1132+ // 合入本地权重覆盖
1133+ for ( const [ name , patch ] of Object . entries ( LOCAL_OVERRIDES . weights ) ) {
1134+ if ( usefulAttr [ name ] ) {
1135+ Object . assign ( usefulAttr [ name ] , patch )
1136+ console . log ( ` 本地覆盖权重: ${ name } ${ JSON . stringify ( patch ) } ` )
1137+ }
1138+ }
1139+
10721140 const attrMapBase = parseExportValue ( extraText , 'attrMap' )
10731141 const attrPct = parseExportValue ( extraText , 'attrPct' )
10741142 const basicNum = parseExportValue ( extraText , 'basicNum' )
@@ -1092,19 +1160,23 @@ async function main() {
10921160 const encoded = encodeURIComponent ( name )
10931161 const result = { name, baseAttr : null , elem : '' , code : '' , defWeights : null }
10941162
1095- // Fetch data.json
1096- const dj = await fetchJson ( `resources/meta-gs/character/${ encoded } /data.json` )
1097- if ( dj ?. [ 'baseAttr' ] ) {
1098- result . baseAttr = dj [ 'baseAttr' ]
1099- result . elem = dj [ 'elem' ] || ''
1100- }
1163+ try {
1164+ // Fetch data.json
1165+ const dj = await fetchJson ( `resources/meta-gs/character/${ encoded } /data.json` )
1166+ if ( dj ?. [ 'baseAttr' ] ) {
1167+ result . baseAttr = dj [ 'baseAttr' ]
1168+ result . elem = dj [ 'elem' ] || ''
1169+ }
11011170
1102- // Fetch artis.js
1103- const artisSrc = await fetchRaw ( `resources/meta-gs/character/${ encoded } /artis.js` )
1104- if ( artisSrc ) {
1105- const { code, defWeights } = transformArtisJs ( artisSrc , name )
1106- result . code = code || ''
1107- result . defWeights = defWeights || null
1171+ // Fetch artis.js
1172+ const artisSrc = await fetchRaw ( `resources/meta-gs/character/${ encoded } /artis.js` )
1173+ if ( artisSrc ) {
1174+ const { code, defWeights } = transformArtisJs ( artisSrc , name )
1175+ result . code = code || ''
1176+ result . defWeights = defWeights || null
1177+ }
1178+ } catch ( e ) {
1179+ console . warn ( ` 角色 ${ name } 数据拉取/转换失败: ${ e . message } ,保留现有数据` )
11081180 }
11091181
11101182 return result
@@ -1129,7 +1201,15 @@ async function main() {
11291201 dataJsonFailed ++
11301202 }
11311203
1132- if ( result . code ) {
1204+ // 本地有覆盖就用本地的,否则用上游转换结果
1205+ const overrideRule = LOCAL_OVERRIDES . rules [ result . name ]
1206+ if ( overrideRule ) {
1207+ artisJsFetched ++
1208+ charRulesEntries . push ( overrideRule )
1209+ if ( result . defWeights ) {
1210+ artisDefaultWeights [ result . name ] = result . defWeights
1211+ }
1212+ } else if ( result . code ) {
11331213 artisJsFetched ++
11341214 charRulesEntries . push ( result . code )
11351215 if ( result . defWeights ) {
0 commit comments