1/* 2 * æè½æ¨¹è³æè¼å ¥/æ£è¦åã 3 * 4 * å·è¡æç´æ¥ fetch data/grf/zero/*.luaï¼Big5 編碼ï¼ç¨ lua_table_parser.js 5 * è§£æï¼ä¸åé¢ç·è½æªââ宿¹æ¹çå¢å æè½æï¼ç´æ¥è¦èéå¹¾å lua æªéæ°æ´ç 6 * é é¢å³å¯çæã 7 * 8 * å°å¤åªæä¾é份 SkillTreeData ç©ä»¶ï¼æè½æ¨¹ dialogï¼skill_tree_view.jsï¼ 9 * éé宿¿è³æï¼ä¸ç´æ¥ç¢° lua_table_parserã 10 */ 11(function (global) { 12 'use strict'; 13 14 var COLS_PER_ROW = 7; 15 var COLOR_CODE_RE = /\^[0-9A-Fa-f]{6}/g; 16 17 // çç´å¾ 1 éå§ï¼Lv1 ä¸ç¨è±é»æ¸ï¼ï¼æä»¥è½å¯¦éè±ç黿¸ = 該è½è·æé«çç´ - 1ã 18 // ä¸é¢å ©ä»½è¡¨å ç §ãæé«çç´ã寫ï¼åçµ±ä¸ç¨éåå½å¼è½æãå¯è±é»æ¸ãï¼é樣ä¹å¾ 19 // å°ç §å®æ¹çç´è¡¨ä¿®æ£æ¸åæï¼ç´æ¥æ¹æé«çç´å°±å¥½ï¼ä¸ç¨æ¯æ¬¡æå忏 1ã 20 function levelToPoints(map) { 21 var out = {}; 22 Object.keys(map).forEach(function (k) { out[k] = map[k] - 1; }); 23 return out; 24 } 25 26 // ==================== Zero è³æä¾æº ==================== 27 // æ¯å JOBID é çãæé«çç´ãï¼è¦ docs/SKILLTREE_DIALOG_PLAN.md 第ä¸ç¯ï¼ 28 var ZERO_MAX_LEVEL = { 29 JT_SWORDMAN: 50, JT_MAGICIAN: 50, JT_ARCHER: 50, JT_ACOLYTE: 50, JT_MERCHANT: 50, JT_THIEF: 50, 30 JT_KNIGHT: 70, JT_PRIEST: 70, JT_WIZARD: 70, JT_BLACKSMITH: 70, JT_HUNTER: 70, JT_ASSASSIN: 70, 31 JT_CRUSADER: 70, JT_MONK: 70, JT_SAGE: 70, JT_ROGUE: 70, JT_ALCHEMIST: 70, JT_BARD: 70, JT_DANCER: 70, 32 JT_REBELLION: 70, JT_KAGEROU: 70, JT_OBORO: 70, JT_NINJA: 70, JT_GUNSLINGER: 70, 33 JT_SUPERNOVICE: 99, JT_SUPERNOVICE2: 70 34 }; 35 var ZERO_POINT_CAP = levelToPoints(ZERO_MAX_LEVEL); 36 37 // 1è½é -> 2è½é -> è¨ç®æ© classes.json çæçµè·æ¥ä»£ç¢¼ã 38 // å·²ç¨ classes.json 實éç labelï¼å¦ RK="é¨å£«/ç§æ©é¨å£«/ç§æ©é¾çµ"ï¼æ ¸å°éï¼ 39 // è¦ docs/SKILLTREE_DIALOG_PLAN.md 第äºç¯ã 40 // SE/SL/SUM/DR æ¯å¤ªæ¥µ/å¬å師/ç¹æ®è·ç³»ï¼Zero è³ææ²æå°æå §å®¹ï¼ä¸åå ¥ã 41 var ZERO_LINEAGE = [ 42 { finalClass: 'RK', stages: ['JT_SWORDMAN', 'JT_KNIGHT'] }, 43 { finalClass: 'RG', stages: ['JT_SWORDMAN', 'JT_CRUSADER'] }, 44 { finalClass: 'WL', stages: ['JT_MAGICIAN', 'JT_WIZARD'] }, 45 { finalClass: 'SO', stages: ['JT_MAGICIAN', 'JT_SAGE'] }, 46 { finalClass: 'RA', stages: ['JT_ARCHER', 'JT_HUNTER'] }, 47 { finalClass: 'MI', stages: ['JT_ARCHER', 'JT_BARD'] }, 48 { finalClass: 'WA', stages: ['JT_ARCHER', 'JT_DANCER'] }, 49 { finalClass: 'AB', stages: ['JT_ACOLYTE', 'JT_PRIEST'] }, 50 { finalClass: 'SU', stages: ['JT_ACOLYTE', 'JT_MONK'] }, 51 { finalClass: 'ME', stages: ['JT_MERCHANT', 'JT_BLACKSMITH'] }, 52 { finalClass: 'GE', stages: ['JT_MERCHANT', 'JT_ALCHEMIST'] }, 53 { finalClass: 'GX', stages: ['JT_THIEF', 'JT_ASSASSIN'] }, 54 { finalClass: 'SC', stages: ['JT_THIEF', 'JT_ROGUE'] }, 55 { finalClass: 'RE', stages: ['JT_GUNSLINGER'/*, 'JT_REBELLION'*/] }, 56 { finalClass: 'KO', stages: ['JT_NINJA'/*, 'JT_KAGEROU'*/] }, 57 { finalClass: 'OB', stages: ['JT_NINJA'/*, 'JT_OBORO'*/] }, 58 { finalClass: 'SN', stages: ['JT_SUPERNOVICE'/*, 'JT_SUPERNOVICE2'*/] } 59 ]; 60 61 // ==================== æ¨æºçï¼è½è·å¶/å«ä¸è½åè½ï¼è³æä¾æº ==================== 62 // 以䏿¸åæ¯ã該è½è·æé«çç´ãï¼ä½¿ç¨è 確èªï¼ï¼1è½/2è½(éè½ç) 50ï¼3è½ 70ï¼4è½ 60ï¼ 63 // å¿è /æ ¼é¬¥æ§æç³» 70/70/60ï¼æ³èç³»ãæéç³» 50/50/70/60ï¼è·å å¤§è·æ¥åä¸å¥æ¸åï¼ï¼ 64 // å¬å師系ï¼éé師ï¼åªæ 60/60ãè½çäºè½ï¼_H çµå°¾é£äºé ï¼ä¸ä½¿ç¨ãä¸åå ¥ lineageã 65 // 太極系ï¼ä½¿ç¨è 確èªï¼ï¼JT_TAEKWON=50ï¼ä¹å¾ä¸ç®¡é¸æ³èç·éæ¯æéç·ï¼ç¬¬äºé 齿¯50
66 // ï¼JT_STAR=50ãJT_LINKER=50ï¼ï¼ä¹å¾æ³èç·ç JT_STAR_EMPEROR 沿ç¨60ï¼æ²æç¬¬ä¸é å¯ç¨70ï¼ã 67 // 實éå¯è±çæè½é» = æé«çç´ - 1ï¼Lv1 ä¸ç¨è±é»ï¼ï¼ç±ä¸é¢ levelToPoints() çµ±ä¸è½æã 68 var STANDARD_MAX_LEVEL = { 69 // å å¤§è·æ¥ï¼1è½/2è½ 50ï¼3è½ 70ï¼4è½ 60 70 JT_SWORDMAN: 50, JT_MAGICIAN: 50, JT_ARCHER: 50, JT_ACOLYTE: 50, JT_MERCHANT: 50, JT_THIEF: 50, 71 JT_KNIGHT: 50, JT_CRUSADER: 50, JT_WIZARD: 50, JT_SAGE: 50, JT_HUNTER: 50, JT_BARD: 50, JT_DANCER: 50, 72 JT_PRIEST: 50, JT_MONK: 50, JT_BLACKSMITH: 50, JT_ALCHEMIST: 50, JT_ASSASSIN: 50, JT_ROGUE: 50, 73 JT_RUNE_KNIGHT: 70, JT_ROYAL_GUARD: 70, JT_WARLOCK: 70, JT_SORCERER: 70, JT_RANGER: 70, 74 JT_MINSTREL: 70, JT_WANDERER: 70, JT_ARCHBISHOP: 70, JT_SURA: 70, JT_MECHANIC: 70, JT_GENETIC: 70, 75 JT_GUILLOTINE_CROSS: 70, JT_SHADOW_CHASER: 70, 76 JT_DRAGON_KNIGHT: 60, JT_IMPERIAL_GUARD: 60, JT_ARCH_MAGE: 60, JT_ELEMENTAL_MASTER: 60, 77 JT_WINDHAWK: 60, JT_TROUBADOUR: 60, JT_TROUVERE: 60, JT_CARDINAL: 60, JT_INQUISITOR: 60, 78 JT_MEISTER: 60, JT_BIOLO: 60, JT_SHADOW_CROSS: 60, JT_ABYSS_CHASER: 60, 79 // å¿è / ç¥æ§æç³»ï¼70/70/60 80 JT_NINJA: 70, JT_KAGEROU: 70, JT_SHINKIRO: 60, 81 JT_OBORO: 70, JT_SHIRANUI: 60, 82 JT_GUNSLINGER: 70, JT_REBELLION: 70, JT_NIGHT_WATCH: 60, 83 // è·æ³ï¼JT_TAEKWON=50 å ©æ¢ç·å ±ç¨ï¼æ³èç· JT_STAR=50ãJT_STAR_EMPEROR=60ï¼ 84 // æéç· JT_LINKER=50ãJT_SOUL_ASCETIC=70ãJT_SOUL_REAPER=60 85 JT_TAEKWON: 50, 86 JT_STAR: 50, JT_STAR_EMPEROR: 60, 87 JT_LINKER: 50, JT_SOUL_ASCETIC: 70, JT_SOUL_REAPER: 60, 88 // å¬å師系ï¼éé師ï¼ï¼60/60 89 JT_DO_SUMMONER: 60, JT_SPIRIT_HANDLER: 60, 90 // SuperNovice 91 JT_SUPERNOVICE: 99, JT_SUPERNOVICE2: 70, JT_HYPER_NOVICE: 60 92 }; 93 var STANDARD_POINT_CAP = levelToPoints(STANDARD_MAX_LEVEL); 94 95 var STANDARD_LINEAGE = [ 96 { finalClass: 'RK', stages: ['JT_SWORDMAN', 'JT_KNIGHT', 'JT_RUNE_KNIGHT', 'JT_DRAGON_KNIGHT'] }, 97 { finalClass: 'RG', stages: ['JT_SWORDMAN', 'JT_CRUSADER', 'JT_ROYAL_GUARD', 'JT_IMPERIAL_GUARD'] }, 98 { finalClass: 'WL', stages: ['JT_MAGICIAN', 'JT_WIZARD', 'JT_WARLOCK', 'JT_ARCH_MAGE'] }, 99 { finalClass: 'SO', stages: ['JT_MAGICIAN', 'JT_SAGE', 'JT_SORCERER', 'JT_ELEMENTAL_MASTER'] }, 100 { finalClass: 'RA', stages: ['JT_ARCHER', 'JT_HUNTER', 'JT_RANGER', 'JT_WINDHAWK'] }, 101 { finalClass: 'MI', stages: ['JT_ARCHER', 'JT_BARD', 'JT_MINSTREL', 'JT_TROUBADOUR'] }, 102 { finalClass: 'WA', stages: ['JT_ARCHER', 'JT_DANCER', 'JT_WANDERER', 'JT_TROUVERE'] }, 103 { finalClass: 'AB', stages: ['JT_ACOLYTE', 'JT_PRIEST', 'JT_ARCHBISHOP', 'JT_CARDINAL'] }, 104 { finalClass: 'SU', stages: ['JT_ACOLYTE', 'JT_MONK', 'JT_SURA', 'JT_INQUISITOR'] }, 105 { finalClass: 'ME', stages: ['JT_MERCHANT', 'JT_BLACKSMITH', 'JT_MECHANIC', 'JT_MEISTER'] }, 106 { finalClass: 'GE', stages: ['JT_MERCHANT', 'JT_ALCHEMIST', 'JT_GENETIC', 'JT_BIOLO'] }, 107 { finalClass: 'GX', stages: ['JT_THIEF', 'JT_ASSASSIN', 'JT_GUILLOTINE_CROSS', 'JT_SHADOW_CROSS'] }, 108 { finalClass: 'SC', stages: ['JT_THIEF', 'JT_ROGUE', 'JT_SHADOW_CHASER', 'JT_ABYSS_CHASER'] }, 109 { finalClass: 'RE', stages: ['JT_GUNSLINGER', 'JT_REBELLION', 'JT_NIGHT_WATCH'] }, 110 { finalClass: 'KO', stages: ['JT_NINJA', 'JT_KAGEROU', 'JT_SHINKIRO'] }, 111 { finalClass: 'OB', stages: ['JT_NINJA', 'JT_OBORO', 'JT_SHIRANUI'] }, 112 { finalClass: 'SN', stages: ['JT_SUPERNOVICE', 'JT_SUPERNOVICE2', 'JT_HYPER_NOVICE'] }, 113 { finalClass: 'SE', stages: ['JT_TAEKWON', 'JT_STAR', 'JT_STAR_EMPEROR'] }, 114 { finalClass: 'SL', stages: ['JT_TAEKWON', 'JT_LINKER', 'JT_SOUL_ASCETIC', 'JT_SOUL_REAPER'] }, 115 { finalClass: 'SUM', stages: ['JT_DO_SUMMONER', 'JT_SPIRIT_HANDLER'] } 116 ]; 117 118 // å ©ä»½è³æä¾æºç總表ï¼basePath å°æ data/grf/<xxx>/ çè³æå¤¾å稱 119 var DATASETS = { 120 zero: { key: 'zero', label: 'Zero', basePath: 'grf/zero/', pointCap: ZERO_POINT_CAP, lineage: ZERO_LINEAGE }, 121 standard: { key: 'standard', label: 'åè½æçæ¬', basePath: 'grf/standard/', pointCap: STANDARD_POINT_CAP, lineage: STANDARD_LINEAGE } 122 }; 123 124 function stripColorCodes(s) { 125 return typeof s === 'string' ? s.replace(COLOR_CODE_RE, '').trim() : s; 126 } 127 128 // hybrid arrayï¼å ·åå±¬æ§ + æ¸å indexï¼-> ç´ JS é£åï¼åªåé£çºæ¸å index é¨å 129 function toCleanArray(raw) { 130 if (!raw) return []; 131 var out = []; 132 var i = 0; 133 while (raw[i] !== undefined) { out.push(raw[i]); i++; } 134 return out; 135 } 136 137 // NeedSkillList çåå§ pair é£å -> [{skid, level}]ï¼æ²çµ¦çç´çè¦çº 1ï¼ 138 function normalizeNeedList(raw) { 139 return toCleanArray(raw).map(function (p) { 140 var arr = toCleanArray(p); 141 return { skid: arr[0], level: (arr.length > 1 ? arr[1] : 1) }; 142 }); 143 } 144 145 async function fetchBig5Text(url) { 146 var res = await fetch(url); 147 if (!res.ok) throw new Error('HTTP ' + res.status + ' ' + url); 148 var buf = await res.arrayBuffer(); 149 return new TextDecoder('big5').decode(buf); 150 } 151
152 // delayRawï¼LuaTableParser.parse() åºä¾ç SKILL_DELAY_LISTï¼{skid: {SkillCastFixedDelay, ...}} 153 // -> {skid: {fixedDelay[], statDelay[], singlePostDelay[], globalPostDelay[]}}ï¼å®ä½ msï¼é£å index 0 å°æ Lv1ï¼ 154 function parseSkillDelay(delayRaw) { 155 var skillDelay = {}; 156 Object.keys(delayRaw).forEach(function (skid) { 157 var raw = delayRaw[skid]; 158 skillDelay[skid] = { 159 fixedDelay: toCleanArray(raw.SkillCastFixedDelay), 160 statDelay: toCleanArray(raw.SkillCastStatDelay), 161 singlePostDelay: toCleanArray(raw.SkillSinglePostDelay), 162 globalPostDelay: toCleanArray(raw.SkillGlobalPostDelay) 163 }; 164 }); 165 return skillDelay; 166 } 167 168 // åªè¼å ¥/è§£æ skilldelaylist.luaï¼çµ¦ä¸»ç«é¢æ¿å®æ¹è© å±å»¶é²ç¨ï¼ä¸éè¦æè½æ¨¹çå ¶ä»ä¸åæªæ¡ï¼ã 169 // ç¼ºæªæ¡ï¼ä¾å¦æ¨æºç/åè½æ²ææä¾ï¼åå³ç©ºç©ä»¶ï¼å¼å«ç«¯éåæå¡«ç FCT/VCT/CD/GCD å³å¯ã 170 async function loadSkillDelayList(basePath) { 171 try { 172 var text = await fetchBig5Text(basePath + 'skilldelaylist.lua'); 173 var delayRaw = LuaTableParser.parse(text, {}).SKILL_DELAY_LIST || []; 174 return parseSkillDelay(delayRaw); 175 } catch (e) { 176 console.warn('[SkillTreeData] skilldelaylist.lua ç¡æ³è¼å ¥ï¼' + e.message + 'ï¼', e); 177 return {}; 178 } 179 } 180 181 async function loadSkillTreeData(basePath) { 182 var warnings = []; 183 var onWarning = function (msg) { warnings.push(msg); console.warn('[SkillTreeData]', msg); }; 184 185 // skilldelaylist.lua 䏿¯æ¯åè³æä¾æºé½æï¼ä¾å¦æ¨æºç/åè½æ²ææä¾ï¼ï¼ 186 // ç¼ºæªæ¡å°±ç¶ä½æ²æè© å±å»¶é²è³æï¼ä¸è¦è®æ´ä»½æè½æ¨¹é½è¼å ¥å¤±æã 187 var texts = await Promise.all([ 188 fetchBig5Text(basePath + 'skilltreeview.lua'), 189 fetchBig5Text(basePath + 'skillinfolist.lua'), 190 fetchBig5Text(basePath + 'skilldescript.lua'), 191 fetchBig5Text(basePath + 'skilldelaylist.lua').catch(function (e) { 192 onWarning('skilldelaylist.lua ç¡æ³è¼å ¥ï¼' + e.message + 'ï¼ï¼æ¤è³æä¾æºå°ç¥éè© å±å»¶é²è³è¨'); 193 return ''; 194 }) 195 ]); 196 197 var treeRaw = (LuaTableParser.parse(texts[0], { onWarning: onWarning }).SKILL_TREEVIEW_FOR_JOB) || []; 198 var infoRaw = (LuaTableParser.parse(texts[1], { onWarning: onWarning }).SKILL_INFO_LIST) || []; 199 var descRaw = (LuaTableParser.parse(texts[2], { onWarning: onWarning }).SKILL_DESCRIPT) || []; 200 var delayRaw = (LuaTableParser.parse(texts[3], { onWarning: onWarning }).SKILL_DELAY_LIST) || []; 201 202 // ---- skillTreeByJob: {jobKey: [{col,row,colInRow,skid}]}ï¼ä¾ col æåºï¼å»é¤ holeï¼---- 203 var skillTreeByJob = {}; 204 Object.keys(treeRaw).forEach(function (jobKey) { 205 if (jobKey === 'JT_NOVICE') return; // ä¸ä½¿ç¨æ°æè·æ¥é 206 var cols = treeRaw[jobKey]; 207 var entries = []; 208 Object.keys(cols).forEach(function (colStr) { 209 var col = Number(colStr); 210 if (isNaN(col) || cols[col] === undefined) return; 211 entries.push({ col: col, row: Math.floor(col / COLS_PER_ROW), colInRow: col % COLS_PER_ROW, skid: cols[col] }); 212 }); 213 entries.sort(function (a, b) { return a.col - b.col; }); 214 skillTreeByJob[jobKey] = entries; 215 }); 216 217 // ---- skillInfo: {skid: {maxLv, spAmount[], needSkillListDefault, needSkillListOverrides}} ---- 218 var skillInfo = {}; 219 Object.keys(infoRaw).forEach(function (skid) { 220 var raw = infoRaw[skid]; 221 var overrides = {}; 222 if (raw.NeedSkillList) { 223 Object.keys(raw.NeedSkillList).forEach(function (jobKey) { 224 overrides[jobKey] = normalizeNeedList(raw.NeedSkillList[jobKey]); 225 }); 226 } 227 skillInfo[skid] = { 228 skid: skid, 229 rawName: raw[0], 230 maxLv: raw.MaxLv || 1, 231 spAmount: toCleanArray(raw.SpAmount), 232 needSkillListDefault: normalizeNeedList(raw._NeedSkillList), 233 needSkillListOverrides: overrides 234 }; 235 }); 236 237 // ---- skillDescript: {skid: {nameZh, nameEn, lines[]}}ï¼å»è²ç¢¼ï¼ç¬¬ä¸è¡æä¸è±åï¼---- 238 var skillDescript = {};
239 Object.keys(descRaw).forEach(function (skid) { 240 var lines = toCleanArray(descRaw[skid]).map(stripColorCodes); 241 var nameLine = lines[0] || skid; 242 var m = /^(.*?)[ï¼(](.*)[ï¼)]\s*$/.exec(nameLine); 243 var nameZh = nameLine, nameEn = ''; 244 if (m) { nameZh = m[1].trim(); nameEn = m[2].trim(); } 245 skillDescript[skid] = { nameZh: nameZh, nameEn: nameEn, lines: lines.slice(1) }; 246 }); 247 248 // ---- skillDelay: {skid: {fixedDelay[], statDelay[], singlePostDelay[], globalPostDelay[]}} ---- 249 var skillDelay = parseSkillDelay(delayRaw); 250 251 return { 252 skillTreeByJob: skillTreeByJob, 253 skillInfo: skillInfo, 254 skillDescript: skillDescript, 255 skillDelay: skillDelay, 256 warnings: warnings 257 }; 258 } 259 260 // ç®åéé JOBID å°æåæè½çæçåç½®éæ±ï¼æ override ç¨ overrideï¼å¦åç¨é è¨ï¼ 261 function getEffectiveNeedList(skillInfo, jobKey, skid) { 262 var entry = skillInfo[skid]; 263 if (!entry) return []; 264 if (entry.needSkillListOverrides && entry.needSkillListOverrides[jobKey]) { 265 return entry.needSkillListOverrides[jobKey]; 266 } 267 return entry.needSkillListDefault || []; 268 } 269 270 // ç°¡æ CSV éè¡åå²ï¼æ¯æ´éå¼èå æ¬ä½è "" é¸åºï¼éå¹¾åæªæ¡ä¸æææ´è¤éçæ æ³ï¼ 271 function parseCsvLine(line) { 272 var out = []; 273 var cur = ''; 274 var inQuotes = false; 275 for (var i = 0; i < line.length; i++) { 276 var c = line[i]; 277 if (inQuotes) { 278 if (c === '"') { 279 if (line[i + 1] === '"') { cur += '"'; i++; } else { inQuotes = false; } 280 } else { cur += c; } 281 } else { 282 if (c === '"') inQuotes = true; 283 else if (c === ',') { out.push(cur); cur = ''; } 284 else cur += c; 285 } 286 } 287 out.push(cur); 288 return out; 289 } 290 291 // åå³ {skid: {locale: name}}ï¼æ¯åèªç³»ä¸æ¬ï¼ï¼è®å¼å«ç«¯ä¾ç®å UI èªç³»èªå·±é¸è¦é¡¯ç¤ºåªåï¼ 292 // ä¸å¨éè£¡å¯«æ» zh-TWââè·è¨ç®æ©ä¸»ç¨å¼ uiTranslations/skillTranslations åä¸ç¨®æ ¼å¼ã 293 async function loadSkillI18n(url) { 294 var res = await fetch(url); 295 var text = await res.text(); 296 var lines = text.split(/\r?\n/).filter(function (l) { return l.length > 0; }); 297 if (lines.length === 0) return {}; 298 var header = parseCsvLine(lines[0]); 299 var map = {}; 300 for (var i = 1; i < lines.length; i++) { 301 var cols = parseCsvLine(lines[i]); 302 if (!cols[0]) continue; 303 var entry = {}; 304 for (var c = 1; c < header.length; c++) { 305 if (cols[c]) entry[header[c]] = cols[c]; 306 } 307 map[cols[0]] = entry; 308 } 309 return map; 310 } 311 312 async function loadSkillIconMap(url) { 313 try { 314 var res = await fetch(url); 315 if (!res.ok) return {}; 316 var text = await res.text(); 317 var lines = text.split(/\r?\n/).filter(function (l) { return l.length > 0; }); 318 var map = {}; 319 for (var i = 1; i < lines.length; i++) { 320 var cols = parseCsvLine(lines[i]); 321 if (cols[0] && cols[1]) map[cols[0]] = cols[1]; 322 } 323 return map; 324 } catch (e) { 325 return {}; 326 } 327 } 328 329 // ä»»ååå¾æè½æ¸ å®ï¼ç´æåæªï¼ä¸è¡ä¸åæè½ä»£ç¢¼ï¼ä¸å« SKID. åç¶´ï¼ï¼ 330 // 空è¡ã# éé ç註解è¡å¿½ç¥ãéäºæè½ä¸è½è¢«ä½¿ç¨è 調æ´çç´ï¼ä¹ä¸è¨å ¥å·²ç¨é»æ¸ã 331 async function loadQuestSkillSet(url) { 332 try { 333 var res = await fetch(url); 334 if (!res.ok) return {}; 335 var text = await res.text(); 336 var set = {}; 337 text.split(/\r?\n/).forEach(function (line) { 338 var s = line.trim(); 339 if (!s || s.charAt(0) === '#') return; 340 set[s] = true; 341 }); 342 return set; 343 } catch (e) { 344 return {}; 345 } 346 } 347 348 global.SkillTreeData = { 349 DATASETS: DATASETS, 350 COLS_PER_ROW: COLS_PER_ROW, 351 loadSkillTreeData: loadSkillTreeData, 352 loadSkillDelayList: loadSkillDelayList, 353 getEffectiveNeedList: getEffectiveNeedList, 354 loadSkillI18n: loadSkillI18n, 355 loadSkillIconMap: loadSkillIconMap,
356 loadQuestSkillSet: loadQuestSkillSet, 357 parseCsvLine: parseCsvLine 358 }; 359})(typeof window !== 'undefined' ? window : this);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.