diff --git a/repo/js/ArtifactsAssistant/README.md b/repo/js/ArtifactsAssistant/README.md new file mode 100644 index 000000000..55e2a639d --- /dev/null +++ b/repo/js/ArtifactsAssistant/README.md @@ -0,0 +1,41 @@ + +# 圣遗物辅助 + +本脚本在后台定时运行,如果你在原神里面查看背包、角色身上的单个圣遗物,那么会在`BetterGI`的遮罩窗口显示文本,以告知这个圣遗物的评价、适用角色等等。 + +请先运行`overlay.exe`,它会不断读取`overlay.txt`并刷新到其窗口上,以解决`BetterGI`的遮罩窗口显示文本难以辨识的问题。 + +你也可以修改脚本自定义配置,`是否在遮罩窗口输出`取消勾选以避免在`BetterGI`的遮罩窗口显示信息。 + +输出信息说明: + +``` txt +==== 完全圣遗物 ==== +黄金剧团:生之花,金,等级+20 +总点数:76.3,主属性:生命值 4780 +副属性1:+0次,生命值+5.3% +副属性2:+0次,防御力+19 +副属性3:+4次,暴击伤害+31.1% +副属性4:+1次,暴击率+7.4% +适配:八重神子、千织、爱可菲、芙宁娜、菲谢尔、阿贝多 +词条点(61.1):*暴击*、*暴伤*、攻击、精通 +---------------- +``` + +- `==== 完全圣遗物 ====`,表示五星初始四个词条而四星初始三词条,可能因计算误判 +- `==== 圣遗物 ====`,表示五星初始三个词条而四星初始两词条,这个不会误判 +- `总点数`,是圣遗物副属性所有点数之和,每个词条随机`7`到`10`点 +- `副属性`後接其已强化次数和属性值,`词条点`後接于角色适用词条的总点数和名称(有则带`*`) +- `这个圣遗物不堪大用啊`,这个圣遗物对目前所有角色都不好用,可合成其他圣遗物或当狗粮喂掉 +- `适配:`,这个圣遗物可为角色的毕业圣遗物,如果属性点不好,可以考虑重塑一下 +- `可用:`,这个圣遗物可为角色的好用圣遗物,可以尝试看看属性点好不好 + + +关于`角色一览.md`的配置: + +1. 你可以自由修改或增加配置项,但必须符合原始语法 +2. 数字序号後必须接空格和角色名称,例如`108. 杜林` +3. 换行规则请务必满足,不要缺少换行符,否则解析会出问题,请确保结尾有两个换行符 +4. 一个搭配项的不同名称以`、`分隔,若角色有两种搭配,则每种搭配以`;`分隔,搭配项依次排列 +5. `圣遗物主词条:`配置`时之沙`、`空之杯`、`理之冠`的主属性,同一部件允许多个主属性则以`/`分隔 +6. 六种默认模板`生命输出`,`攻击输出`,`防御输出`,`生命治疗`,`攻击治疗`,`防御治疗`,最好不要修改 diff --git a/repo/js/ArtifactsAssistant/assets/0.png b/repo/js/ArtifactsAssistant/assets/0.png new file mode 100644 index 000000000..97b654b6d Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/0.png differ diff --git a/repo/js/ArtifactsAssistant/assets/1.png b/repo/js/ArtifactsAssistant/assets/1.png new file mode 100644 index 000000000..e52432936 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/1.png differ diff --git a/repo/js/ArtifactsAssistant/assets/10.png b/repo/js/ArtifactsAssistant/assets/10.png new file mode 100644 index 000000000..7d6b21c03 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/10.png differ diff --git a/repo/js/ArtifactsAssistant/assets/11.png b/repo/js/ArtifactsAssistant/assets/11.png new file mode 100644 index 000000000..d79b5d2f9 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/11.png differ diff --git a/repo/js/ArtifactsAssistant/assets/12.png b/repo/js/ArtifactsAssistant/assets/12.png new file mode 100644 index 000000000..7db76794d Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/12.png differ diff --git a/repo/js/ArtifactsAssistant/assets/13.png b/repo/js/ArtifactsAssistant/assets/13.png new file mode 100644 index 000000000..90e12649c Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/13.png differ diff --git a/repo/js/ArtifactsAssistant/assets/14.png b/repo/js/ArtifactsAssistant/assets/14.png new file mode 100644 index 000000000..c02b7f4c6 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/14.png differ diff --git a/repo/js/ArtifactsAssistant/assets/15.png b/repo/js/ArtifactsAssistant/assets/15.png new file mode 100644 index 000000000..3242f8c84 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/15.png differ diff --git a/repo/js/ArtifactsAssistant/assets/16.png b/repo/js/ArtifactsAssistant/assets/16.png new file mode 100644 index 000000000..2bf6087cf Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/16.png differ diff --git a/repo/js/ArtifactsAssistant/assets/17.png b/repo/js/ArtifactsAssistant/assets/17.png new file mode 100644 index 000000000..f0bbf7fd5 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/17.png differ diff --git a/repo/js/ArtifactsAssistant/assets/18.png b/repo/js/ArtifactsAssistant/assets/18.png new file mode 100644 index 000000000..87914cff2 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/18.png differ diff --git a/repo/js/ArtifactsAssistant/assets/19.png b/repo/js/ArtifactsAssistant/assets/19.png new file mode 100644 index 000000000..26fb8593e Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/19.png differ diff --git a/repo/js/ArtifactsAssistant/assets/2.png b/repo/js/ArtifactsAssistant/assets/2.png new file mode 100644 index 000000000..8b7adcb1d Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/2.png differ diff --git a/repo/js/ArtifactsAssistant/assets/20.png b/repo/js/ArtifactsAssistant/assets/20.png new file mode 100644 index 000000000..b0cfad377 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/20.png differ diff --git a/repo/js/ArtifactsAssistant/assets/3.png b/repo/js/ArtifactsAssistant/assets/3.png new file mode 100644 index 000000000..a44e37d26 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/3.png differ diff --git a/repo/js/ArtifactsAssistant/assets/4.png b/repo/js/ArtifactsAssistant/assets/4.png new file mode 100644 index 000000000..bdf0740f8 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/4.png differ diff --git a/repo/js/ArtifactsAssistant/assets/5.png b/repo/js/ArtifactsAssistant/assets/5.png new file mode 100644 index 000000000..77503a910 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/5.png differ diff --git a/repo/js/ArtifactsAssistant/assets/6.png b/repo/js/ArtifactsAssistant/assets/6.png new file mode 100644 index 000000000..a02f1441f Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/6.png differ diff --git a/repo/js/ArtifactsAssistant/assets/7.png b/repo/js/ArtifactsAssistant/assets/7.png new file mode 100644 index 000000000..f3e393cc8 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/7.png differ diff --git a/repo/js/ArtifactsAssistant/assets/8.png b/repo/js/ArtifactsAssistant/assets/8.png new file mode 100644 index 000000000..b85f522b8 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/8.png differ diff --git a/repo/js/ArtifactsAssistant/assets/9.png b/repo/js/ArtifactsAssistant/assets/9.png new file mode 100644 index 000000000..d43ddfc7f Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/9.png differ diff --git a/repo/js/ArtifactsAssistant/assets/star.png b/repo/js/ArtifactsAssistant/assets/star.png new file mode 100644 index 000000000..60184c4b6 Binary files /dev/null and b/repo/js/ArtifactsAssistant/assets/star.png differ diff --git a/repo/js/ArtifactsAssistant/main.js b/repo/js/ArtifactsAssistant/main.js new file mode 100644 index 000000000..9d820520f --- /dev/null +++ b/repo/js/ArtifactsAssistant/main.js @@ -0,0 +1,759 @@ + +/************************ logout.js ************************/ + +let enableLog = settings.enableLog; + +// 打印日志 +function logout(text, filename) { + if (logout.text === void 0) logout.text = text; + else logout.text = logout.text + "\n" + text; + if (filename) {// 写文件仅支持格式 .txt, .json, .log, .csv, .xml, .html, .css + for (let i = 0; i < 100 && !file.writeTextSync(filename, logout.text); ++i); + if (logout.last != logout.text) { + if (enableLog) { + const info = logout.text.split("\n"); + for (let i = 0; i < info.length; ++i) log.info(info[i]); + } + logout.last = logout.text; + } + logout.text = void 0; + } +} + +/************************ relics.js ************************/ + +// 所有词条名称 +const targetKeywords = [ + "生命值", "攻击力", "防御力", "元素精通",// 0-3 + "元素充能效率", "暴击率", "暴击伤害",// 4-6 + "治疗加成", "物理伤害加成", "火元素伤害加成",// 7-9 + "雷元素伤害加成", "水元素伤害加成", "草元素伤害加成",// 10-12 + "风元素伤害加成", "岩元素伤害加成", "冰元素伤害加成" // 13-15 +]; + +// 圣遗物部件名称 +const targetPartNames = ["生之花", "死之羽", "时之沙", "空之杯", "理之冠"]; + +class Relics { + /* 同等级五星与四星圣遗物主属性之比 10/9, 同点数副属性之比 5/4 */ + constructor() { + this.main = -1;// 主属性 index + this.level = -1;// 等级 + this.quality = -1;// 星级 + this.part = -1;// 部件 index + this.name = "";// 套装名称 + this.subAttr = [];// 副属性 index + this.subValue = [];// 副属性值 + this.subPoints = [];// 副属性点数 + this.subCounts = [];// 副属性加强次数 + } + set_main(str) { + for (let i = 0; i < targetKeywords.length; ++i) { + if (str.includes(targetKeywords[i])) { + this.main = i; + break; + } + } + } + set_level(lvl) { + this.level = lvl; + } + set_quality(qt) { + this.quality = qt; + } + set_part(str) { + for (let i = 0; i < targetPartNames.length; ++i) { + if (str.includes(targetPartNames[i])) { + this.part = i; + break; + } + } + } + set_name(str) { + this.name = str.replace(/[^\u4E00-\u9FA5]/g, ''); + } + set_subAttr(...attrs) { + if (attrs.length > 4) throw new Error("副属性解析参数错误"); + for (let i = 0; i < 4; ++i) { + if (!attrs[i]) { + this.subAttr[i] = -1; + this.subValue[i] = 0; + continue; + } + let id = 0; + for (; id < targetKeywords.length; ++id) { + if (attrs[i].includes(targetKeywords[id])) break; + } + if (id >= targetKeywords.length) { + this.subAttr[i] = -1; + this.subValue[i] = 0; + continue; + } + if (id < 3 && !attrs[i].includes("%")) id += targetKeywords.length; + this.subAttr[i] = id; + if (!attrs[i].includes("待激活")) { + const strval = attrs[i].match(/\d*\.?\d+/); + if (!strval) throw new Error("副属性数值解析错误"); + this.subValue[i] = parseFloat(strval[0]); + } + else this.subValue[i] = 0; + } + } + get_mainAttr() {// 由于数值精度, 可能计算不准确 + if (this.main == -1) return null; + let start, end; + if (this.part == 0) {// 生之花 + start = (this.quality == 5 ? 717 : 645); + end = (this.quality == 5 ? 4780 : 4303); + return "生命值 " + Math.round(start + (end - start) / 20.0 * this.level); + } + if (this.part == 1) {// 死之羽 + start = (this.quality == 5 ? 47 : 42); + end = (this.quality == 5 ? 311 : 280); + return "攻击力 " + Math.round(start + (end - start) / 20.0 * this.level); + } + switch (this.main) { + case 0:// 生命百分比 + case 1:// 攻击百分比 + case 9:// 火元素伤害加成 + case 10:// 雷元素伤害加成 + case 11:// 水元素伤害加成 + case 12:// 草元素伤害加成 + case 13:// 风元素伤害加成 + case 14:// 岩元素伤害加成 + case 15:// 冰元素伤害加成 + start = (this.quality == 5 ? 7.0 : 6.3); + end = (this.quality == 5 ? 46.6 : 41.9); + break; + case 2:// 防御百分比 + case 8:// 物理伤害加成 + start = (this.quality == 5 ? 8.7 : 7.9); + end = (this.quality == 5 ? 58.3 : 52.4); + break; + case 3:// 元素精通 + start = (this.quality == 5 ? 28 : 25.2); + end = (this.quality == 5 ? 186.5 : 167.8); + break; + case 4:// 元素充能效率 + start = (this.quality == 5 ? 7.8 : 7.0); + end = (this.quality == 5 ? 51.8 : 46.6); + break; + case 5:// 暴击率 + start = (this.quality == 5 ? 4.7 : 4.2); + end = (this.quality == 5 ? 31.1 : 28.0); + break; + case 6:// 暴击伤害 + start = (this.quality == 5 ? 9.3 : 8.4); + end = (this.quality == 5 ? 62.2 : 55.9); + break; + case 7:// 治疗加成 + start = (this.quality == 5 ? 5.4 : 4.8); + end = (this.quality == 5 ? 35.9 : 32.3); + break; + default: + throw new Error("圣遗物主属性错误"); + } + const value = start + (end - start) / 20.0 * this.level; + if (this.main == 3) return "元素精通 " + Math.round(value); + return targetKeywords[this.main] + " " + value.toFixed(1) + "%"; + } + + make_point() { + // 计算所有副属性的点数 + for (let i = 0; i < 4; ++i) { + let point, id = this.subAttr[i]; + if (id == -1) { + this.subPoints[i] = 0; + continue; + } + switch (id) { + case (targetKeywords.length):// 生命值 + point = 29.8444;// * 2.2533; + break; + case (targetKeywords.length + 1):// 攻击力 + point = 1.9430;// * 2.4300; + break; + case (targetKeywords.length + 2):// 防御力 + point = 2.3316;// * 2.2468; + break; + case 3:// 元素精通 + point = 2.3316; + break; + case 0:// 生命百分比 + case 1:// 攻击百分比 + point = 0.5829; + break; + case 2:// 防御百分比 + point = 0.7286; + break; + case 4:// 元素充能效率 + point = 0.6477; + break; + case 5:// 暴击率 + point = 0.3886; + break; + case 6:// 暴击伤害 + point = 0.7772; + break; + default: + throw new Error("圣遗物副属性错误"); + } + if (this.quality != 5) point *= 0.8;// 若四星圣遗物则都乘以 0.8 + this.subPoints[i] = this.subValue[i] / point; + } + // 计算所有副属性的词条数(计算不可能完全准确因而总词条数可能比实际多一个) + let sum = 0, allcounts = Math.trunc(this.level / 4) + (this.quality - 1); + for (let i = 0; i < 4; ++i) { + let points = this.subPoints[i]; + if (points == 0) { + this.subCounts[i] = 0; + continue; + } + this.subCounts[i] = -1;// 缺省未知词条数 + if (points < 20.5) this.subCounts[i] = (points < 12 ? 1 : 2); + else if (points < 27.9) this.subCounts[i] = 3; + else if (this.quality == 5) { + if (points > 30.1 && points < 34.9) this.subCounts[i] = 4; + else if (points > 40.1 && points < 41.9) this.subCounts[i] = 5; + else if (points > 50.1) this.subCounts[i] = 6; + } + else if (points > 30.1) this.subCounts[i] = 4; + if (this.subCounts[i] != -1) sum += this.subCounts[i]; + else sum |= 128; + } + if (sum > 128) { + sum = allcounts - (sum - 128);// 未确定的词条数 + const id = this.subCounts.indexOf(-1); + if (this.quality == 5) {// 五星圣遗物需猜测词条数 + const nid = this.subCounts.indexOf(-1, id + 1); + if (nid == -1) {// 仅需处理一个未确定词条 + this.subCounts[id] = Math.min(sum, Math.trunc(this.subPoints[id] * 0.1434)); + } + else {// 需处理两个未确定词条 + this.subCounts[nid] = this.subCounts[id] = 3; + if (sum == 7) { + if (this.subPoints[id] >= this.subPoints[nid]) this.subCounts[id] = 4; + else this.subCounts[nid] = 4; + } + } + } + else {// 四星圣遗物基本可确定 + this.subCounts[id] = sum;//(sum < 4 || this.subPoints[id] < 29 ? 3 : 4); + } + } + } + + /* 获取副属性总点数 */ + getAttrPoints() { + //if (this.subPoints.length == 0) this.make_point(); + return this.subPoints[0] + this.subPoints[1] + this.subPoints[2] + this.subPoints[3]; + } + + /* 获取副属性总词条数(对于已强化的圣遗物可能不准确) */ + getAttrCounts() { + //if (this.subCounts.length == 0) this.make_point(); + return this.subCounts[0] + this.subCounts[1] + this.subCounts[2] + this.subCounts[3]; + } + + /* 判断是否为初始完全词条圣遗物(对于已强化的圣遗物可能不准确) */ + isDominant() { + //if (this.subPoints.length == 0) this.make_point(); + return this.getAttrCounts() >= Math.trunc(this.level / 4) + (this.quality - 1); + } + + /* 打印圣遗物信息 */ + print() { + if (!this.isDominant()) logout("==== 圣遗物 ===="); + else logout("==== 完全圣遗物 ===="); + logout(this.name + ":" + targetPartNames[this.part] + "," + + (this.quality == 5 ? "金" : "紫") + ",等级+" + this.level); + logout("总点数:" + this.getAttrPoints().toFixed(1) + "," + "主属性:" + this.get_mainAttr()); + for (let i = 0; i < 4; ++i) { + if (this.subAttr[i] == -1) continue; + const val = this.subValue[i]; + if (val == 0) continue; + let str, id = this.subAttr[i]; + if (id < targetKeywords.length) str = targetKeywords[id]; + else str = targetKeywords[id - targetKeywords.length]; + if (id == 3 || id >= targetKeywords.length) str = str + "+" + Math.round(val); + else str = str + "+" + val.toFixed(1) + "%"; + logout("副属性" + (i + 1) + ":+" + (this.subCounts[i] - 1) + "次," + str); + } + } +}; + +/************************ characters.js ************************/ + +// 词条名称简写 +const abbrevKeywords = [ + "生命", "攻击", "防御", "精通",// 0-3 + "充能", "暴击", "暴伤",// 4-6 + "治疗", "物伤", "火伤",// 7-9 + "雷伤", "水伤", "草伤",// 10-12 + "风伤", "岩伤", "冰伤" // 13-15 +]; + +// 单个角色的套装 +class Actor { + constructor(str) { + if (!str) return;// 支持无参构造 + const actorName = str.match(/^\d+\. *(.*)$/m);// 匹配角色名字 + const weaponRe = /^- 武器:(.*)$/m; + const relicsRe = /^- 圣遗物:(.*)$/m; + const mainAttrRe = /^- 圣遗物主词条:(.*)$/m; + const subAttrRe = /^- 圣遗物副词条:(.*)$/m; + let result, weapon, relics, mainAttr, subAttr, lvl = 0; + if ((/^.*;.*;.*$/m).test(str)) lvl = 2; + else if((/^.*;.*$/m).test(str)) lvl = 1; + /* 武器 */ + result = str.match(weaponRe); + if (!result || !result[1]) weapon = ["", "", ""]; + else { + weapon = result[1].split(";"); + for (let i = weapon.length; i <= lvl; ++i) weapon[i] = weapon[0]; + } + /* 圣遗物 */ + result = str.match(relicsRe); + if (!result || !result[1]) relics = ["", "", ""]; + else { + relics = result[1].split(";"); + for (let i = relics.length; i <= lvl; ++i) relics[i] = relics[0]; + } + /* 圣遗物主词条 */ + result = str.match(mainAttrRe); + if (!result || !result[1]) mainAttr = ["", "", ""]; + else { + mainAttr = result[1].split(";"); + for (let i = mainAttr.length; i <= lvl; ++i) mainAttr[i] = mainAttr[0]; + } + /* 圣遗物副词条 */ + result = str.match(subAttrRe); + if (!result || !result[1]) subAttr = ["", "", ""]; + else { + subAttr = result[1].split(";"); + for (let i = subAttr.length; i <= lvl; ++i) subAttr[i] = subAttr[0]; + } + // 子配置 + let pos = this, objects = [this]; + for (let i = 0; i < lvl; ++i) { + pos = pos.next = new Actor(); + objects[i + 1] = pos; + } + pos.next = null; + // 解析配置 + for (let id = 0; id <= lvl; ++id) { + objects[id].name = actorName[1];// 角色名字 + objects[id].weaponName = weapon[id].split("、");// 武器名称 + objects[id].relicsName = relics[id].split("、");// 圣遗物名称 + objects[id].mainAttr = [(1 << 0), (1 << 1)];// 主属性采取位标志存储 + objects[id].subAttr = [];// 副属性 index + const mainAttrName = mainAttr[id].split("、"); + for (let i = mainAttrName.length; i < 3; ++i) mainAttrName[i] = mainAttrName[0]; + for (let i = 2; i < 5; ++i) { + let val = 0, keywords = mainAttrName[i - 2].split("/"); + for (let j = 0; j < keywords.length; ++j) { + val |= (1 << abbrevKeywords.indexOf(keywords[j])); + } + objects[id].mainAttr[i] = val; + } + const keywords = subAttr[id].split("、"); + for (let i = 0; i < keywords.length; ++i) { + objects[id].subAttr[i] = abbrevKeywords.indexOf(keywords[i]); + } + } + } + + print(index) { + logout((index + ".").padEnd(3) + " " + this.name); + logout(""); + logout("- 武器:" + this.weaponName.join("、")); + logout("- 圣遗物:" + this.relicsName.join("、")); + const mainAttrName = []; + for (let i = 2; i < 5; ++i) { + const attrName = []; + for (let j = 0; j < abbrevKeywords.length; ++j) { + if ((1 << j) & this.mainAttr[i]) attrName.push(abbrevKeywords[j]); + } + mainAttrName.push(attrName.join("/")); + } + logout("- 圣遗物主词条:" + mainAttrName.join("、")); + const subAttrName = []; + for (let i = 0; i < this.subAttr.length; ++i) { + subAttrName.push(abbrevKeywords[this.subAttr[i]]); + } + logout("- 圣遗物副词条:" + subAttrName.join("、")); + logout(""); + if (this.next) this.next.print(index); + } + + /* 圣遗物是否适合当前角色, 返回适合的配置(Actor类型) */ + getSuitable(relics) { + do { + // 套装名称必须匹配 + if (this.relicsName[0] && !this.relicsName.includes(relics.name)) break; + // 主属性必须匹配 + if (!((1 << relics.main) & this.mainAttr[relics.part])) break; + // 计算是否包含有效词条 + const hasAttr = []; + for (let i = 0; i < this.subAttr.length; ++i) { + if (relics.part >= 2 && this.subAttr[i] == relics.main) continue; + hasAttr.push(relics.subAttr.indexOf(this.subAttr[i]) != -1); + } + // 若初始完全词条且含前两类 + const dominant = relics.isDominant(); + const hasFirst = (hasAttr[0] === void 0 || hasAttr[0]); + const hasSecond = (hasAttr[1] === void 0 || hasAttr[1]); + if (dominant && hasFirst && hasSecond) return this; + // 若有效词条类型多于三种(不占用主词条)或四种(占用主词条) + if (hasAttr.length > 3) { + const hasThird = (hasAttr[2] === void 0 || hasAttr[2]); + const hasFourth = (hasAttr[3] === void 0 || hasAttr[3]); + if (!dominant) {// 非初始完全词条 + if (!hasFirst || !hasSecond) break;// 前两类必须都有 + if (hasThird && hasFourth) return this;// 前四类都有 + if (!hasThird && !hasFourth) break;// 前四类缺少两类 + for (let i = 4; i < hasAttr.length; ++i) { + if (hasAttr[i]) return this;// 四有效 + } + } + else {// 初始完全词条 + if (!hasFirst && !hasSecond) break;// 前两类必须有一类 + if (hasThird && hasFourth) return this;// 前四类有三类 + if (!hasThird && !hasFourth) break;// 前四类缺少三类 + for (let i = 4; i < hasAttr.length; ++i) { + if (hasAttr[i]) return this;// 三有效 + } + } + } + } while(false); + if (!this.next) return null; + else return this.next.getSuitable(relics); + } + + /* 圣遗物是否可用于当前角色, 返回适合的配置(Actor类型) */ + getAffable(relics) { + do { + // 套装名称必须匹配 + if (this.relicsName[0] && !this.relicsName.includes(relics.name)) break; + // 主属性必须匹配 + if (!((1 << relics.main) & this.mainAttr[relics.part])) break; + // 计算是否包含有效词条 + const hasAttr = []; + for (let i = 0; i < this.subAttr.length; ++i) { + if (relics.part >= 2 && this.subAttr[i] == relics.main) continue; + hasAttr.push(relics.subAttr.indexOf(this.subAttr[i]) != -1); + } + // 若含前两类词条 + const hasFirst = (hasAttr[0] === void 0 || hasAttr[0]); + const hasSecond = (hasAttr[1] === void 0 || hasAttr[1]); + if (hasFirst && hasSecond) return this; + // 若有效词条类型多于三种(不占用主词条)或四种(占用主词条) + if (hasAttr.length > 3) { + const hasThird = (hasAttr[2] === void 0 || hasAttr[2]); + const hasFourth = (hasAttr[3] === void 0 || hasAttr[3]); + if (!hasFirst && !hasSecond) break;// 前两类必须有一类 + if (hasThird && hasFourth) return this;// 前四类有三类 + if (!hasThird && !hasFourth) break;// 前四类缺少三类 + for (let i = 4; i < hasAttr.length; ++i) { + if (hasAttr[i]) return this;// 三有效 + } + } + } while(false); + if (!this.next) return null; + else return this.next.getAffable(relics); + } + + /* 获取圣遗物于当前角色的适用分数 */ + getScore(relics) { + let ret = this.relicsName.indexOf(relics.name); + if (ret != -1) { + if (ret == 0) ret = 2;// 专属套装加两分 + else if (ret > 1) ret = 0;// 非专属套装不加分 + } + const indexAttr = []; + for (let i = 0; i < this.subAttr.length; ++i) {// 要排除与主属性相同的副词条 + if (relics.part >= 2 && this.subAttr[i] == relics.main) continue; + indexAttr.push(relics.subAttr.indexOf(this.subAttr[i])); + } + for (let i = 0; i < indexAttr.length; ++i) { + const id = indexAttr[i]; + if (id == -1) continue; + if (i < 2) ret += relics.subPoints[id]; + else if (i < 4) ret += relics.subPoints[id] * 0.8; + else ret += relics.subPoints[id] * 0.56; + if (relics.subAttr[id] < 3) {// 计算数值生命值、攻击力、防御力 + const nid = relics.subAttr.indexOf(this.subAttr[i] + targetKeywords.length); + if (nid == -1) continue; + // 统一将点数除以 2.37 以转为百分比属性点 + if (i < 2) ret += relics.subPoints[nid] * (1.0 * 27 / 64); + else if (i < 4) ret += relics.subPoints[nid] * (0.8 * 27 / 64); + else ret += relics.subPoints[nid] * (0.56 * 27 / 64); + } + } + this.score = ret; + return ret; + } +}; + +// 所有角色的套装 +class Characters { + constructor(str) { + this.actors = []; + const general = str.match(/1\. .*\n\n(?:- .*\n)+\n/g);// 默认模板 + let pos = this.actors[0] = new Actor(general[0]); + for (let i = 1; i < general.length; ++i) { + pos = pos.next = new Actor(general[i]); + } + const specials = str.match(/\d+\..*\n\n(?:- .*\n)+\n/g);// 特定角色 + for (let i = general.length; i < specials.length; ++i) { + this.actors.push(new Actor(specials[i])); + } + //enableLog = false; + //this.print();// 调试信息 + //logout("", "characters.txt"); + //enableLog = settings.enableLog; + } + + print() { + for (let i = 0; i < this.actors.length; ++i) this.actors[i].print(i + 1); + } + + /* 评价圣遗物 */ + evaluate(relics) { + let text, objects = [];// 首先查找毕业配置 + for (let i = 1; i < this.actors.length; ++i) {// 先跳过默认模板 + const obj = this.actors[i].getSuitable(relics); + if (obj != null) { + obj.getScore(relics); + objects.push(obj); + } + } + objects.sort((x, y) => y.score - x.score);// 评分降序排列 + let obj = this.actors[0].getSuitable(relics); + if (obj != null) { + obj.getScore(relics); + objects.push(obj);// 确保默认模板排在最後 + } + if (objects.length == 0) {// 尝试查找可用配置 + for (let i = 1; i < this.actors.length; ++i) {// 先跳过默认模板 + const obj = this.actors[i].getAffable(relics); + if (obj != null) { + obj.getScore(relics); + objects.push(obj); + } + } + objects.sort((x, y) => y.score - x.score);// 评分降序排列 + let obj = this.actors[0].getAffable(relics); + if (obj != null) { + obj.getScore(relics); + objects.push(obj);// 确保默认模板排在最後 + } + if (objects.length == 0) {// 没有角色需要此圣遗物 + logout("**这个圣遗物不堪大用啊**"); + return; + } + else text = "可用:"; + } + else text = "适配:"; + /* 打印前六名适配角色 */ + text = text + objects[0].name; + for (let i = 1; i < 6 && i < objects.length; ++i) { + text = text + "、" + objects[i].name; + } + logout(text); + /* 打印首个适配角色所需副词条和评分 */ + obj = objects[0]; + text = "词条点(" + obj.score.toFixed(1) + "):"; + for (let i = 0; i < obj.subAttr.length; ++i) { + let midstr = ""; + if (relics.subAttr.indexOf(obj.subAttr[i]) != -1) midstr = "*"; + text = text + midstr + abbrevKeywords[obj.subAttr[i]] + midstr + "、"; + } + logout(text.slice(0, -1)); + } +}; + +/************************ genshin.js ************************/ + +// 辅助函数:检查圣遗物品质(使用模板匹配) +function checkQuality(image, templateObj, x, y, w, h) { + const crop = image.DeriveCrop(x, y, w, h); + const matchResult = crop.Find(templateObj); + const ret = !matchResult.IsEmpty(); + matchResult.Dispose(); + crop.Dispose(); + return ret; +} + +// 辅助函数:检查圣遗物等级(使用模板匹配,因为OCR识别不准确) +function checkLevel(image, templateObjs, x, y, w, h) { + const crop = image.DeriveCrop(x, y, w, h); + let ret = 0; + let res = crop.Find(templateObjs[ret]); + if (res.IsEmpty()) {// 尝试两遍 + ret = 20; + let i = 0; + while (true) { + res.Dispose(); + res = crop.Find(templateObjs[ret]); + if (!res.IsEmpty()) break; + if (--ret < 0) { + if (++i >= 2) break; + ret = 20; + } + } + } + res.Dispose(); + crop.Dispose(); + return ret; +} + +// 辅助函数:读取指定区域的文本内容(使用OCR识别) +function readTextRegion(image, KeywordObj) { + const textResult = image.Find(KeywordObj); + const ret = textResult.Text; + textResult.Dispose(); + return ret; +} + +function autoZoom(x, y, w, h) { + /* 注意, BetterGI 截图强制转为 1080P, 这里不用缩放 */ + const ratio = 1;//genshin.scaleTo1080PRatio; + return [x * ratio, y * ratio, w * ratio, h * ratio]; +} + +function readImageAutoZoom(path, width, height) { + const ratio = 1;//genshin.scaleTo1080PRatio; + return file.ReadImageMatWithResizeSync(path, width * ratio, height * ratio); +} + +function readTemplate() { + const template = []; + for (let i = 0; i <= 20; ++i) { + template[i] = readImageAutoZoom("assets/" + i.toFixed(0) + ".png", 47, 21); + } + template[21] = readImageAutoZoom("assets/star.png", 28, 26); + return template; +} + +function freeTemplate(template) { + for (let i = 0; i <= 21; ++i) template[i].Dispose(); +} + +// 入口函数 +(async function () { + const characters = new Characters(file.readTextSync("角色一览.md")); + const template = readTemplate(); + const templateQualityObj = RecognitionObject.TemplateMatch(template[21]); + const templateLevelObj = []; + for (let i = 0; i <= 20; ++i) { + let tempObj = RecognitionObject.TemplateMatch(template[i]); + tempObj.Threshold = 0.91;// 至少 0.91 + //tempObj.InitTemplate();// 这个不需要 + templateLevelObj[i] = tempObj; + } + + const backpackPartNameObj = RecognitionObject.Ocr(...autoZoom(1320, 180, 90, 40)); + const backpackMainAttrObj = RecognitionObject.Ocr(...autoZoom(1320, 268, 180, 32)); + const backpackSuitNameObj = RecognitionObject.Ocr(...autoZoom(1320, 630, 240, 35)); + const backpackSubAttrObj1 = RecognitionObject.Ocr(...autoZoom(1352, 470, 368, 40)); + const backpackSubAttrObj2 = RecognitionObject.Ocr(...autoZoom(1352, 510, 368, 40)); + const backpackSubAttrObj3 = RecognitionObject.Ocr(...autoZoom(1352, 550, 368, 40)); + const backpackSubAttrObj4 = RecognitionObject.Ocr(...autoZoom(1352, 590, 368, 40)); + // 祝圣之霜定义的圣遗物, 坐标轴需下移 38px + const ebackpackSuitNameObj = RecognitionObject.Ocr(...autoZoom(1320, 630 + 38, 240, 35)); + const ebackpackSubAttrObj1 = RecognitionObject.Ocr(...autoZoom(1352, 470 + 38, 368, 40)); + const ebackpackSubAttrObj2 = RecognitionObject.Ocr(...autoZoom(1352, 510 + 38, 368, 40)); + const ebackpackSubAttrObj3 = RecognitionObject.Ocr(...autoZoom(1352, 550 + 38, 368, 40)); + const ebackpackSubAttrObj4 = RecognitionObject.Ocr(...autoZoom(1352, 590 + 38, 368, 40)); + + const characterSuitNameObj = RecognitionObject.Ocr(...autoZoom(1458, 486, 242, 32)); + const characterPartNameObj = RecognitionObject.Ocr(...autoZoom(1458, 172, 90, 30)); + const characterMainAttrObj = RecognitionObject.Ocr(...autoZoom(1466, 214, 214, 38)); + const characterSubAttrObj1 = RecognitionObject.Ocr(...autoZoom(1480, 352, 368, 27)); + const characterSubAttrObj2 = RecognitionObject.Ocr(...autoZoom(1480, 385, 368, 27)); + const characterSubAttrObj3 = RecognitionObject.Ocr(...autoZoom(1480, 419, 368, 27)); + const characterSubAttrObj4 = RecognitionObject.Ocr(...autoZoom(1480, 453, 368, 27)); + + while (true) { + // 每次分析耗时约 100ms, 等待约 200ms + await sleep(200); + // 捕获游戏区域图像 + const gameImage = captureGameRegion(); + if (gameImage.IsEmpty()) { + log.error("无法捕获游戏画面"); + break; + } + try { + // 尝试背包界面匹配主词条 + let mainResult = gameImage.Find(backpackMainAttrObj); + const relics = new Relics(); + if (!mainResult.IsEmpty()) { + relics.set_main(mainResult.Text); + if (relics.main != -1) { + // 背包界面属性识别 + let name, part, quality, level, attr1, attr2, attr3, attr4; + part = readTextRegion(gameImage, backpackPartNameObj); + if (checkQuality(gameImage, templateQualityObj, ...autoZoom(1468, 356, 32, 32))) + quality = 5; + else quality = 4; + level = checkLevel(gameImage, templateLevelObj, ...autoZoom(1338, 429, 55, 27)); + if (level != -1) { + name = readTextRegion(gameImage, backpackSuitNameObj); + attr1 = readTextRegion(gameImage, backpackSubAttrObj1); + attr2 = readTextRegion(gameImage, backpackSubAttrObj2); + attr3 = readTextRegion(gameImage, backpackSubAttrObj3); + attr4 = readTextRegion(gameImage, backpackSubAttrObj4); + } + else { + level = checkLevel(gameImage, templateLevelObj, ...autoZoom(1338, 429 + 38, 55, 27)); + name = readTextRegion(gameImage, ebackpackSuitNameObj); + attr1 = readTextRegion(gameImage, ebackpackSubAttrObj1); + attr2 = readTextRegion(gameImage, ebackpackSubAttrObj2); + attr3 = readTextRegion(gameImage, ebackpackSubAttrObj3); + attr4 = readTextRegion(gameImage, ebackpackSubAttrObj4); + } + relics.set_name(name); + relics.set_part(part); + relics.set_quality(quality); + relics.set_level(level); + relics.set_subAttr(attr1, attr2, attr3, attr4); + } + } else { + mainResult.Dispose(); + // 尝试角色界面匹配主词条 + mainResult = gameImage.Find(characterMainAttrObj); + if (!mainResult.IsEmpty()) { + relics.set_main(mainResult.Text); + if (relics.main != -1) { + // 角色界面属性识别 + relics.set_level(checkLevel(gameImage, templateLevelObj, ...autoZoom(1465, 310, 56, 28))); + relics.set_quality(checkQuality(gameImage, templateQualityObj, ...autoZoom(1596, 265, 32, 32)) ? 5 : 4); + relics.set_part(readTextRegion(gameImage, characterPartNameObj)); + relics.set_name(readTextRegion(gameImage, characterSuitNameObj)); + relics.set_subAttr( + readTextRegion(gameImage, characterSubAttrObj1), + readTextRegion(gameImage, characterSubAttrObj2), + readTextRegion(gameImage, characterSubAttrObj3), + readTextRegion(gameImage, characterSubAttrObj4) + ); + } + } + } + mainResult.Dispose(); + /* 分析结果 */ + if (relics.main != -1) { + relics.make_point();// 计算副属性点数 + relics.print();// 打印圣遗物信息 + characters.evaluate(relics);// 评价圣遗物 + logout("----------------", "overlay.txt"); + } + } catch (error) { + log.error(`处理失败: ${error.message}`); + } + finally { + gameImage.Dispose(); + } + } + freeTemplate(template); +})(); diff --git a/repo/js/ArtifactsAssistant/manifest.json b/repo/js/ArtifactsAssistant/manifest.json new file mode 100644 index 000000000..d0a67a6f9 --- /dev/null +++ b/repo/js/ArtifactsAssistant/manifest.json @@ -0,0 +1,21 @@ +{ + "manifest_version": 1, + "name": "圣遗物辅助", + "version": "1.0", + "bgi_version": "0.54.0", + "description": "自动分析圣遗物价值", + "authors": [ + { + "name": "756yang", + "link": "https://github.com/756yang" + } + ], + "settings_ui": "settings.json", + "main": "main.js", + "saved_files": [ + "assets/*.png", + "角色一览.md", + "README.md", + "overlay.exe" + ] +} \ No newline at end of file diff --git a/repo/js/ArtifactsAssistant/overlay.c b/repo/js/ArtifactsAssistant/overlay.c new file mode 100644 index 000000000..c4614ceb9 --- /dev/null +++ b/repo/js/ArtifactsAssistant/overlay.c @@ -0,0 +1,278 @@ +#include +#include +#include +#include +#include +#include + +// 编译命令:gcc overlay.c -fexec-charset=UTF-8 -mcmodel=small -static -O3 -DNDEBUG -s -flto=auto -Wl,--gc-sections,--as-needed -o overlay.exe -lgdi32 + +BOOL RunAsAdmin() { + wchar_t szPath[MAX_PATH]; + if (GetModuleFileNameW(NULL, szPath, ARRAYSIZE(szPath))) { + SHELLEXECUTEINFOW sei = { sizeof(SHELLEXECUTEINFOW) }; + sei.lpVerb = L"runas"; // 关键:请求提升权限 + sei.lpFile = szPath; + sei.hwnd = NULL; + sei.nShow = SW_NORMAL; + if (ShellExecuteExW(&sei)) { + ExitProcess(0); // 成功启动新实例后退出当前进程 + return TRUE; + } + } + return FALSE; +} + +// 检查当前是否管理员权限 +BOOL IsUserAdmin() { + BOOL isAdmin = FALSE; + PSID pAdminGroup = NULL; + // 创建管理员组的SID + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + if (AllocateAndInitializeSid(&NtAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, &pAdminGroup)) { + // 检查令牌是否包含管理员组 + if (!CheckTokenMembership(NULL, pAdminGroup, &isAdmin)) isAdmin = FALSE; + FreeSid(pAdminGroup); + } + return isAdmin; +} + +BOOL PerformPrivilegedOperation() { + if (!IsUserAdmin()) { // 先检查当前权限 + if (RunAsAdmin()) { + return TRUE; // 已触发UAC提示,等待重启 + } else { + // 处理用户拒绝或错误 + MessageBoxW(NULL, L"需要管理员权限才能继续", L"权限错误", MB_ICONERROR); + return FALSE; + } + } + return TRUE; +} + + +char *utf16_to_utf8(const wchar_t *input) { + char *Buffer; + int BuffSize = 0, Result = 0; + BuffSize = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL); + Buffer = (char*) malloc(sizeof(char) * BuffSize); + if(Buffer) { + Result = WideCharToMultiByte(CP_UTF8, 0, input, -1, Buffer, BuffSize, NULL, NULL); + if((Result > 0) && (Result <= BuffSize)) return Buffer; + free(Buffer); + } + return NULL; +} + +wchar_t *utf8_to_utf16(const char *input) { + wchar_t *Buffer; + int BuffSize = 0, Result = 0; + BuffSize = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0); + Buffer = (wchar_t*) malloc(sizeof(wchar_t) * BuffSize); + if(Buffer) { + Result = MultiByteToWideChar(CP_UTF8, 0, input, -1, Buffer, BuffSize); + if((Result > 0) && (Result <= BuffSize)) return Buffer; + free(Buffer); + } + return NULL; +} + +int utf8_printf(const char *format, ...) { + DWORD NUM; + int ret = -1; + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + va_list args; + va_start(args, format); + if(hOut == NULL || hOut == INVALID_HANDLE_VALUE || !GetConsoleMode(hOut, &NUM)) { + ret = vprintf(format, args); + va_end(args); + return ret; + } + char *buffer = (char*)malloc(16384); + if(buffer == NULL) return ret; + va_list args_copy; + va_copy(args_copy, args); + ret = vsnprintf(buffer, 16384, format, args); + va_end(args); + if(ret >= 16384) { + free(buffer); + buffer = (char*)malloc(++ret); + ret = vsnprintf(buffer, ret, format, args_copy); + } + va_end(args_copy); + if(ret < 0) { + free(buffer); + return ret; + } + wchar_t *buffer_utf16 = utf8_to_utf16(buffer); + free(buffer); + if(buffer_utf16 == NULL) return -1; + BOOL VAL = WriteConsoleW(hOut, buffer_utf16, (DWORD)wcslen(buffer_utf16), &NUM, NULL); + free(buffer_utf16); + if(!VAL) return -1; + return NUM; +} + +// 设置控制台字体 +BOOL SetConsoleFont(HANDLE hOutput, COORD fontSize, const wchar_t* fontName) { + CONSOLE_FONT_INFOEX cfi; + ZeroMemory(&cfi, sizeof(cfi)); + cfi.cbSize = sizeof(cfi); + cfi.dwFontSize = fontSize; + wcscpy_s(cfi.FaceName, LF_FACESIZE, fontName); + + // 设置字体样式 + cfi.FontWeight = FW_NORMAL; + cfi.FontFamily = FF_DONTCARE; + + return SetCurrentConsoleFontEx(hOutput, FALSE, &cfi); +} + +#define INTERVAL_MS 50 +#define DELIMITER "----------------" + +// 全局变量用于控制程序运行状态 +volatile BOOL g_bRunning = TRUE; +HANDLE g_hExitEvent = NULL; + +// 控制台事件处理函数 +BOOL WINAPI ConsoleHandler(DWORD dwCtrlType) { + switch (dwCtrlType) { + case CTRL_CLOSE_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_SHUTDOWN_EVENT: + g_bRunning = FALSE; + if (g_hExitEvent) { + SetEvent(g_hExitEvent); + } + // 给主线程一点时间清理资源 + Sleep(50); + return TRUE; + default: + return FALSE; + } +} + +// 获取当前 exe 所在目录的路径 +char* get_exe_dir() { + static char path[MAX_PATH]; + GetModuleFileName(NULL, path, MAX_PATH); + // 去除文件名,只保留目录部分 + char* last_slash = strrchr(path, '\\'); + if (last_slash) { + *(last_slash + 1) = '\0'; + } + return path; +} + +// 读取整个文件内容,调用者需负责释放返回的字符串 +char* read_file(const char* filepath) { + FILE* file = fopen(filepath, "rb"); + if (!file) { + return NULL; + } + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); + char* content = (char*)malloc(size + 1); + if (content) { + fread(content, 1, size, file); + content[size] = '\0'; + } + fclose(file); + return content; +} + +// 检查字符串是否以指定的分隔符结尾 +int ends_with(const char* str, const char* suffix) { + if (!str || !suffix) return 0; + size_t len_str = strlen(str); + size_t len_suffix = strlen(suffix); + if (len_suffix > len_str) return 0; + return strncmp(str + len_str - len_suffix, suffix, len_suffix) == 0; +} + +int main() { + if(!PerformPrivilegedOperation()) return 1;// 以管理员模式启动 + if(!SetProcessDPIAware()) return 1;// 设置DPI感知 + // 设置窗口位置和大小 (x, y, width, height) + // 设置字体为宋体, 高度 25px + COORD fontSize = {0, 20}; + HDC hdc = GetDC(NULL); + if (!hdc) return 1; + fontSize.Y = fontSize.Y * 96.0 / GetDeviceCaps(hdc, LOGPIXELSY); + int posX = 1320 * GetDeviceCaps(hdc, DESKTOPHORZRES) / 1920;// 桌面水平分辨率 + int posY = 680 * GetDeviceCaps(hdc, DESKTOPVERTRES) / 1080;// 桌面垂直分辨率 + ReleaseDC(NULL, hdc); + DWORD console_mode; + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == NULL || hOut == INVALID_HANDLE_VALUE) return 1; + // 设置控制台字体大小 + if(!SetConsoleFont(hOut, fontSize, L"SimHei")) SetConsoleFont(hOut, fontSize, L"SimSun"); + if (!GetConsoleMode(hOut, &console_mode)) return 1; + // 设置控制台启用虚拟终端模式 + SetConsoleMode(hOut, console_mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + // 获取控制台窗口句柄 + HWND hwnd = GetConsoleWindow(); + SetWindowPos(hwnd, HWND_TOPMOST, posX, posY, 600, 300, SWP_SHOWWINDOW); + + // 创建退出事件对象 + g_hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!g_hExitEvent) { + return 2; + } + + // 注册控制台事件处理程序 + if (!SetConsoleCtrlHandler(ConsoleHandler, TRUE)) { + CloseHandle(g_hExitEvent); + return 3; + } + + // 1. 获取 overlay.txt 的绝对路径 + char filepath[MAX_PATH]; + snprintf(filepath, sizeof(filepath), "%soverlay.txt", get_exe_dir()); + + char* last_content = NULL; // 保存上一次以分隔符结尾的内容 + char* current_content = NULL; + + while (g_bRunning) { + // 2. 读取文件 + current_content = read_file(filepath); + if (current_content != NULL) { + // 3. 检查是否以分隔符结尾 + if (ends_with(current_content, DELIMITER)) { + // 4. 与上一次内容比较 + if (last_content == NULL || strcmp(current_content, last_content) != 0) { + // 5. 若不同,则输出 + char* last_break = strrchr(current_content, '\n'); + if (last_break) *last_break = '\0'; + utf8_printf("\033[2J\033[H%s\n", current_content);// 清屏并输出 + if (last_break) *last_break = '\n'; + // 更新 last_content + if (last_content) { + free(last_content); + } + last_content = current_content; + } else { + free(current_content); + } + } else { + free(current_content); + } + } + // 6. 睡眠 50ms,但可被退出事件唤醒 + WaitForSingleObject(g_hExitEvent, INTERVAL_MS); + } + + // 清理资源 + if (last_content) { + free(last_content); + } + if (g_hExitEvent) { + CloseHandle(g_hExitEvent); + } + + return 0; +} diff --git a/repo/js/ArtifactsAssistant/overlay.exe b/repo/js/ArtifactsAssistant/overlay.exe new file mode 100644 index 000000000..10c7145e7 Binary files /dev/null and b/repo/js/ArtifactsAssistant/overlay.exe differ diff --git a/repo/js/ArtifactsAssistant/settings.json b/repo/js/ArtifactsAssistant/settings.json new file mode 100644 index 000000000..6222a5ec4 --- /dev/null +++ b/repo/js/ArtifactsAssistant/settings.json @@ -0,0 +1,8 @@ +[ + { + "name": "enableLog", + "type": "checkbox", + "label": "是否在遮罩窗口输出", + "default": true + } +] diff --git a/repo/js/ArtifactsAssistant/角色一览.md b/repo/js/ArtifactsAssistant/角色一览.md new file mode 100644 index 000000000..2b0bf35af --- /dev/null +++ b/repo/js/ArtifactsAssistant/角色一览.md @@ -0,0 +1,779 @@ +1. 生命输出 + +- 圣遗物主词条:物伤/火伤/雷伤/水伤/草伤/风伤/岩伤/冰伤/暴击/暴伤/生命/精通/充能 +- 圣遗物副词条:暴击、暴伤、生命、充能、精通、攻击 + +1. 攻击输出 + +- 圣遗物主词条:物伤/火伤/雷伤/水伤/草伤/风伤/岩伤/冰伤/暴击/暴伤/攻击/精通/充能 +- 圣遗物副词条:暴击、暴伤、攻击、充能、精通 + +1. 防御输出 + +- 圣遗物主词条:物伤/火伤/雷伤/水伤/草伤/风伤/岩伤/冰伤/暴击/暴伤/防御/精通/充能 +- 圣遗物副词条:暴击、暴伤、防御、充能、精通、攻击 + +1. 生命治疗 + +- 圣遗物主词条:治疗/生命/充能 +- 圣遗物副词条:生命、充能 + +1. 攻击治疗 + +- 圣遗物主词条:治疗/攻击/充能 +- 圣遗物副词条:攻击、充能 + +1. 防御治疗 + +- 圣遗物主词条:治疗/防御/充能 +- 圣遗物副词条:防御、充能 + +2. 重云 + +- 武器:螭骨剑、焚曜千阳 +- 圣遗物:深廊终曲、饰金之梦 +- 圣遗物主词条:充能/攻击、冰伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能、精通 + +3. 凯亚 + +- 武器:西风剑、天空之刃 +- 圣遗物:昔日宗室之仪、绝缘之旗印、冰风迷途的勇士 +- 圣遗物主词条:充能/攻击、冰伤、暴击/暴伤 +- 圣遗物副词条:充能、暴击、暴伤、攻击 + +4. 诺艾尔 + +- 武器:赤角石溃杵、螭骨剑、白影剑 +- 圣遗物:华馆梦醒形骸记、逐影猎人、角斗士的终幕礼 +- 圣遗物主词条:防御、岩伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、防御、充能、攻击 + +5. 凝光 + +- 武器:溢彩心念、金流监督、尘世之锁、流浪乐章、匣里日月 +- 圣遗物:回声之林夜话、逐影猎人、昔日宗室之仪 +- 圣遗物主词条:攻击、岩伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +6. 行秋 + +- 武器:祭礼剑、西风剑、磐岩结绿;祭礼剑、苍古自由之誓、西福斯的月光 +- 圣遗物:绝缘之旗印、烬城勇者绘卷、昔日宗室之仪;乐园遗落之花、饰金之梦 +- 圣遗物主词条:攻击/充能、水伤、暴击/暴伤;充能/精通、精通、精通 +- 圣遗物副词条:暴击、暴伤、充能、攻击、精通;精通、充能 + +7. 芭芭拉 + +- 武器:讨龙英杰谭、试作金珀、西风秘典;千夜浮梦、祭星者之望、祭礼残章 +- 圣遗物:昔时之歌、海染砗磲、教官;乐园遗落之花、饰金之梦 +- 圣遗物主词条:生命/充能、生命、治疗;精通、精通、精通/治疗 +- 圣遗物副词条:生命、充能、暴击;精通、生命、暴击 + +8. 香菱 + +- 武器:薙草之稻光、渔获、赤沙之杖、峡湾长歌、西风长枪、匣里灭辰;喜多院十文字 +- 圣遗物:绝缘之旗印;饰金之梦 +- 圣遗物主词条:充能/攻击/精通、火伤、暴击/暴伤;充能/精通、精通、精通 +- 圣遗物副词条:暴击、暴伤、充能、攻击、精通;精通、充能、暴击 + +9. 班尼特 + +- 武器:天空之刃、风鹰剑、苍古自由之誓、原木刀;雾切之回光、磐岩结绿 +- 圣遗物:昔日宗室之仪、烬城勇者绘卷、教官;炽烈的炎之魔女、昔日宗室之仪 +- 圣遗物主词条:充能/生命、生命、治疗;攻击/精通/充能、火伤、暴击/暴伤 +- 圣遗物副词条:充能、生命、暴击;暴击、暴伤、精通、充能、攻击 + +10. 安柏 + +- 武器:终末嗟叹之诗、冬极白星、冬极白星、若水、阿莫斯之弓、西风猎弓 +- 圣遗物:昔日宗室之仪、教官、烬城勇者绘卷、饰金之梦、炽烈的炎之魔女 +- 圣遗物主词条:充能/攻击/精通、火伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、充能、攻击、精通 + +11. 雷泽 + +- 武器:焚曜千阳、苇海信标、狼的末路、螭骨剑、松籁响起之时;玛海菈的水色 +- 圣遗物:风起之时、逐影猎人、来歆余响、苍白之火;乐园遗落之花、饰金之梦 +- 圣遗物主词条:攻击、雷伤/物伤、暴击/暴伤;精通、精通、精通 +- 圣遗物副词条:暴击、暴伤、攻击、充能、精通;精通、充能、暴击 + +12. 北斗 + +- 武器:螭骨剑、西风大剑、苇海信标、恶王丸、雨裁 +- 圣遗物:绝缘之旗印、昔日宗室之仪 +- 圣遗物主词条:充能/攻击、雷伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通、充能 + +13. 丽莎 + +- 武器:白辰之环、试作金珀、西风秘典、神乐之真意;千夜浮梦、盈满之实 +- 圣遗物:昔日宗室之仪、教官、绝缘之旗印;乐园遗落之花、饰金之梦、如雷的盛怒 +- 圣遗物主词条:充能/攻击、雷伤、暴击/暴伤;精通、精通、精通 +- 圣遗物副词条:充能、暴击、暴伤、攻击、精通;精通、充能 + +14. 菲谢尔 + +- 武器:若水、最初的大魔术、猎人之径、天空之翼、绝弦、暗巷猎手 +- 圣遗物:风起之时、黄金剧团、如雷的盛怒 +- 圣遗物主词条:攻击/精通、雷伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通、充能 + +15. 砂糖 + +- 武器:西风秘典、讨龙英杰谭、试作金珀、祭礼残章 +- 圣遗物:翠绿之影、教官 +- 圣遗物主词条:精通/充能、精通、精通 +- 圣遗物副词条:精通、充能、暴击 + +16. 七七 + +- 武器:祭礼剑、西风剑;有乐御簾切、磐岩结绿 +- 圣遗物:海染砗磲、昔时之歌、烬城勇者绘卷;海染砗磲、逐影猎人、长夜之誓 +- 圣遗物主词条:充能、攻击、治疗;攻击/精通、物伤/增伤、暴击/暴伤 +- 圣遗物副词条:充能、攻击、暴击;暴击、暴伤、攻击、精通、充能 + +17. 莫娜 + +- 武器:讨龙英杰谭、西风秘典、试作金珀、真语秘匣、流浪乐章;千夜浮梦、祭礼残章 +- 圣遗物:昔日宗室之仪、教官、烬城勇者绘卷、风起之时、绝缘之旗印;乐园遗落之花 +- 圣遗物主词条:充能/攻击、水伤、暴击/暴伤;精通、精通、精通 +- 圣遗物副词条:充能、暴击、暴伤、攻击、精通;精通 + +18. 迪卢克 + +- 武器:焚曜千阳、硕果钩、狼的末路、螭骨剑、饰铁之花、试做古华 +- 圣遗物:炽烈的炎之魔女、长夜之誓 +- 圣遗物主词条:精通/攻击、火伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通、充能 + +19. 刻晴 + +- 武器:雾切之回光、磐岩结绿、狼牙、匣里龙吟、海渊终曲 +- 圣遗物:如雷的盛怒、逐影猎人 +- 圣遗物主词条:攻击/精通、雷伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通 + +20. 琴 + +- 武器:天空之刃、西风剑、天目影打刀、苍古自由之誓、祭礼剑 +- 圣遗物:翠绿之影 +- 圣遗物主词条:充能/攻击、攻击/风伤、治疗/暴击/暴伤 +- 圣遗物副词条:充能、攻击、暴击、暴伤 + +21. 温迪 + +- 武器:黎明破晓之史、冬极白星、终末嗟叹之诗、天空之翼、绝弦、西风猎弓 +- 圣遗物:风起之时、翠绿之影 +- 圣遗物主词条:攻击/充能、风伤、暴击/暴伤;精通、精通、精通 +- 圣遗物副词条:暴击、暴伤、攻击、充能、精通;精通、充能 + +22. 可莉 + +- 武器:金流监督、冲浪时光、溢彩心念、流浪乐章 +- 圣遗物:风起之时、逐影猎人、渡过烈火的贤人、炽烈的炎之魔女、追忆之注连 +- 圣遗物主词条:攻击、火伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通 + +23. 迪奥娜 + +- 武器:祭礼弓、终末嗟叹之诗、西风猎弓、反曲弓 +- 圣遗物:昔日宗室之仪、烬城勇者绘卷、昔时之歌 +- 圣遗物主词条:生命/充能、生命、生命 +- 圣遗物副词条:生命、充能 + +24. 达达利亚 + +- 武器:冬极白星、若水、飞雷之弦振、苍翠猎弓、碎链 +- 圣遗物:水仙之梦、逐影猎人、沉沦之心、角斗士的终幕礼 +- 圣遗物主词条:攻击、水伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通、充能 + +25. 辛焱 + +- 武器:桂木斩长正、西风大剑、白影剑、祭礼大剑;赤角石溃杵、苇海信标、螭骨剑 +- 圣遗物:千岩牢固、昔日宗室之仪;苍白之火、逐影猎人、染血的骑士道 +- 圣遗物主词条:防御/充能、防御、防御;攻击/防御、物伤、暴击/暴伤 +- 圣遗物副词条:防御、充能、暴击;暴击、暴伤、攻击、防御、充能 + +26. 钟离 + +- 武器:西风长枪、黑缨枪、护摩之杖 +- 圣遗物:千岩牢固、悠古的磐岩、昔日宗室之仪、教官 +- 圣遗物主词条:生命/攻击、生命/岩伤、生命/暴击/暴伤 +- 圣遗物副词条:生命、暴击、暴伤、攻击、充能 + +27. 阿贝多 + +- 武器:有乐御簾切、岩峰巡歌、辰砂之纺锤 +- 圣遗物:华馆梦醒形骸记、黄金剧团 +- 圣遗物主词条:防御、岩伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、防御 + +28. 甘雨 + +- 武器:星鹫赤羽、最初的大魔术、冬极白星、终末嗟叹之诗、阿莫斯之弓、碎链 +- 圣遗物:流浪大地的乐团、冰风迷途的勇士、绝缘之旗印、昔日宗室之仪 +- 圣遗物主词条:攻击/精通/充能、冰伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通、充能 + +29. 魈 + +- 武器:支离轮光、和璞鸢、护摩之杖、决斗之枪、西风长枪 +- 圣遗物:长夜之誓、逐影猎人、辰砂往生录 +- 圣遗物主词条:攻击、风伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +30. 胡桃 + +- 武器:护摩之杖、赤沙之杖、峡湾长歌、匣里灭辰 +- 圣遗物:炽烈的炎之魔女、追忆之注连、逐影猎人 +- 圣遗物主词条:精通/生命、火伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、精通、生命、攻击 + +31. 罗莎莉亚 + +- 武器:西风长枪、赤沙之杖、决斗之枪、薙草之稻光、断浪长鳍、峡湾长歌、渔获 +- 圣遗物:昔日宗室之仪、绝缘之旗印、烬城勇者绘卷 +- 圣遗物主词条:充能/攻击、冰伤、暴击/暴伤 +- 圣遗物副词条:暴击、充能、暴伤、攻击、精通 + +32. 烟绯 + +- 武器:金流监督、溢彩心念、冲浪时光、流浪乐章;祭星者之望、讨龙英杰谭、试作金珀 +- 圣遗物:炽烈的炎之魔女、逐影猎人、长夜之誓;昔日宗室之仪、烬城勇者绘卷 +- 圣遗物主词条:精通/攻击、火伤、暴击/暴伤;充能、生命、生命/治疗 +- 圣遗物副词条:暴击、暴伤、精通、攻击、充能;生命、充能、暴击 + +33. 优菈 + +- 武器:苇海信标、松籁响起之时、螭骨剑、天空之傲、雪葬的星银 +- 圣遗物:苍白之火 +- 圣遗物主词条:攻击、物伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +34. 枫原万叶 + +- 武器:苍古自由之誓、西福斯的月光、西风剑、铁蜂刺、祭礼剑 +- 圣遗物:翠绿之影 +- 圣遗物主词条:精通/充能、精通、精通;精通/攻击、精通/火伤/风伤、暴击/暴伤 +- 圣遗物副词条:精通、充能、暴击;暴击、暴伤、精通、攻击、充能 + +35. 神里绫华 + +- 武器:雾切之回光、赦罪、海渊终曲、天目影打刀 +- 圣遗物:冰风迷途的勇士、逐影猎人 +- 圣遗物主词条:攻击、冰伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +36. 早柚 + +- 武器:西风大剑、祭礼大剑、桂木斩长正;饰铁之花、玛海菈的水色、西风大剑 +- 圣遗物:翠绿之影 +- 圣遗物主词条:充能、攻击、治疗;充能/精通、精通、精通 +- 圣遗物副词条:充能、攻击、精通、暴击;精通、充能、攻击、暴击 + +37. 宵宫 + +- 武器:飞雷之弦振、若水、最初的大魔术、弓藏 +- 圣遗物:来歆余响、追忆之注连、逐影猎人 +- 圣遗物主词条:精通/攻击、火伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通 + +38. 九条裟罗 + +- 武器:终末嗟叹之诗、冬极白星、天空之翼、落霞、阿莫斯之弓 +- 圣遗物:绝缘之旗印、昔日宗室之仪、烬城勇者绘卷 +- 圣遗物主词条:充能/攻击、雷伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、充能、攻击 + +39. 雷电将军 + +- 武器:薙草之稻光、赤沙之杖、决斗之枪、渔获;峡湾长歌、匣里灭辰 +- 圣遗物:绝缘之旗印、逐影猎人;乐园遗落之花、饰金之梦 +- 圣遗物主词条:充能/攻击、雷伤、暴击/暴伤;精通、精通、精通 +- 圣遗物副词条:暴击、暴伤、攻击、充能;精通 + +40. 埃洛伊 + +- 武器:飞雷之弦振、若水、冬极白星、曚云之月、弓藏 +- 圣遗物:追忆之注连、来歆余响、绝缘之旗印、冰风迷途的勇士 +- 圣遗物主词条:攻击/精通/充能、冰伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能、精通 + +41. 珊瑚宫心海 + +- 武器:不灭月华、讨龙英杰谭、试作金珀;千夜浮梦、祭礼残章 +- 圣遗物:海染砗磲、千岩牢固、烬城勇者绘卷;乐园遗落之花、饰金之梦 +- 圣遗物主词条:生命/充能、水伤/生命、治疗/生命;精通、精通、精通/治疗 +- 圣遗物副词条:生命、充能、攻击、精通;精通、生命、充能 + +42. 托马 + +- 武器:薙草之稻光、西风长枪、喜多院十文字、峡湾长歌 +- 圣遗物:昔日宗室之仪、烬城勇者绘卷;乐园遗落之花、饰金之梦、深林的记忆 +- 圣遗物主词条:充能/生命、生命、生命;精通/充能、精通、精通 +- 圣遗物副词条:充能、生命、暴击;精通、充能、生命、暴击 + +43. 五郎 + +- 武器:西风猎弓、终末嗟叹之诗、祭礼弓 +- 圣遗物:流放者、烬城勇者绘卷、昔日宗室之仪 +- 圣遗物主词条:充能、防御、治疗/暴击 +- 圣遗物副词条:充能、防御、暴击 + +44. 荒泷一斗 + +- 武器:赤角石溃杵、螭骨剑、白影剑 +- 圣遗物:华馆梦醒形骸记、逐影猎人 +- 圣遗物主词条:防御/充能、岩伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、防御、充能、攻击 + +45. 云堇 + +- 武器:虹的行迹、薙草之稻光、喜多院十文字、西风长枪 +- 圣遗物:悠古的磐岩、华馆梦醒形骸记、昔日宗室之仪 +- 圣遗物主词条:充能/防御、防御、防御/暴击 +- 圣遗物副词条:防御、充能、暴击 + +46. 申鹤 + +- 武器:息灾、西风长枪、薙草之稻光 +- 圣遗物:昔日宗室之仪、角斗士的终幕礼 +- 圣遗物主词条:攻击/充能、攻击、攻击 +- 圣遗物副词条:攻击、充能、暴击 + +47. 八重神子 + +- 武器:神乐之真意、溢彩心念、流浪乐章 +- 圣遗物:黄金剧团、逐影猎人、饰金之梦;乐园遗落之花、饰金之梦 +- 圣遗物主词条:攻击/精通、雷伤/精通、暴击/暴伤;精通、精通、精通 +- 圣遗物副词条:暴击、暴伤、攻击、精通;精通 + +48. 神里绫人 + +- 武器:波乱月白经津、磐岩结绿、海渊终曲;苍古自由之誓、西福斯的月光、铁蜂刺 +- 圣遗物:来歆余响、逐影猎人;乐园遗落之花、饰金之梦 +- 圣遗物主词条:攻击、水伤、暴击/暴伤;精通、精通、精通 +- 圣遗物副词条:暴击、暴伤、攻击、生命、充能;精通、充能 + +49. 夜兰 + +- 武器:若水、终末嗟叹之诗、西风猎弓、猎人之径、祭礼弓 +- 圣遗物:绝缘之旗印、昔日宗室之仪、逐影猎人 +- 圣遗物主词条:充能/生命、生命/水伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、充能、生命 + +50. 久岐忍 + +- 武器:圣显之钥、苍古自由之誓、西福斯的月光、铁蜂刺 +- 圣遗物:乐园遗落之花、饰金之梦;千岩牢固 +- 圣遗物主词条:精通、精通、精通/治疗;生命、生命、治疗 +- 圣遗物副词条:精通、生命;生命、精通 + +51. 鹿野院平藏 + +- 武器:神乐之真意、试作金珀、白辰之环 +- 圣遗物:翠绿之影、逐影猎人 +- 圣遗物主词条:攻击/充能、风伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +52. 柯莱 + +- 武器:终末嗟叹之诗、祭礼弓、冬极白星、西风猎弓、若水 +- 圣遗物:深林的记忆、昔日宗室之仪、教官 +- 圣遗物主词条:充能/攻击、草伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能、精通 + +53. 提纳里 + +- 武器:猎人之径、星鹫赤羽、弹弓、阿莫斯之弓 +- 圣遗物:饰金之梦、流浪大地的乐团、逐影猎人 +- 圣遗物主词条:精通/攻击、草伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、精通、攻击 + +54. 多莉 + +- 武器:西风大剑、便携动力锯;玛海菈的水色;苇海信标、螭骨剑 +- 圣遗物:昔时之歌、昔日宗室之仪;乐园遗落之花、饰金之梦;如雷的盛怒 +- 圣遗物主词条:充能、生命、治疗;精通/充能、精通、精通;攻击、雷伤、暴击/暴伤 +- 圣遗物副词条:充能、生命、暴击;精通、充能、生命;暴击、暴伤、精通、攻击、充能 + +55. 坎蒂丝 + +- 武器:西风长枪、薙草之稻光、公义的酬报;喜多院十文字、匣里灭辰 +- 圣遗物:昔日宗室之仪、烬城勇者绘卷、教官;乐园遗落之花、饰金之梦 +- 圣遗物主词条:充能/生命、生命、生命;精通/充能、精通、精通 +- 圣遗物副词条:充能、生命、暴击;精通、充能、暴击 + +56. 赛诺 + +- 武器:赤沙之杖、峡湾长歌、和璞鸢、喜多院十文字、白缨枪 +- 圣遗物:如雷的盛怒、饰金之梦、逐影猎人、角斗士的终幕礼 +- 圣遗物主词条:精通/充能/攻击、雷伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、充能、攻击、精通 + +57. 妮露 + +- 武器:圣显之钥、船坞长剑;圣显之钥、磐岩结绿 +- 圣遗物:花海甘露之光、千岩牢固、流浪大地的乐团;逐影猎人、沉沦之心、千岩牢固 +- 圣遗物主词条:生命、生命、生命;生命、水伤/生命、暴击/暴伤/生命 +- 圣遗物副词条:生命、精通;暴击、暴伤、生命、精通、充能 + +58. 纳西妲 + +- 武器:千夜浮梦、神乐之真意、流浪的晚星、祭礼残章、流浪乐章 +- 圣遗物:深林的记忆、教官、饰金之梦 +- 圣遗物主词条:精通、草伤/精通、暴击/暴伤/精通 +- 圣遗物副词条:精通、暴击、暴伤、充能 + +59. 莱依拉 + +- 武器:圣显之钥、西风剑、船坞长剑 +- 圣遗物:千岩牢固、昔日宗室之仪、冰风迷途的勇士 +- 圣遗物主词条:生命、生命/冰伤、生命/暴击/暴伤 +- 圣遗物副词条:生命、充能、暴击、暴伤 + +60. 珐露珊 + +- 武器:西风猎弓、终末嗟叹之诗、若水 +- 圣遗物:昔日宗室之仪、烬城勇者绘卷、翠绿之影、千岩牢固、逐影猎人 +- 圣遗物主词条:充能/攻击、风伤、暴击/暴伤 +- 圣遗物副词条:充能、暴击、暴伤、攻击 + +61. 流浪者 + +- 武器:图莱杜拉的回忆、金流监督、冲浪时光、神乐之真意、流浪乐章、嘟嘟可故事集 +- 圣遗物:沙上楼阁史话、来歆余响、追忆之注连、逐影猎人 +- 圣遗物主词条:攻击、风伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +62. 瑶瑶 + +- 武器:贯月矢、沙中伟贤的对答、西风长枪、公义的酬报、黑缨枪 +- 圣遗物:千岩牢固、深林的记忆、昔时之歌;乐园遗落之花、深林的记忆、饰金之梦 +- 圣遗物主词条:生命/充能、生命、治疗;精通/充能、精通、精通/治疗 +- 圣遗物副词条:生命、充能、暴击;精通、充能、生命、暴击 + +63. 艾尔海森 + +- 武器:裁叶萃光、有乐御簾切、黎明神剑、狼牙 +- 圣遗物:饰金之梦、逐影猎人 +- 圣遗物主词条:精通/攻击、草伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、精通、攻击、充能 + +64. 迪希雅 + +- 武器:苇海信标、赤角石溃杵、祭礼大剑、玛海菈的水色、西风大剑、雨裁、森林王器 +- 圣遗物:千岩牢固、烬城勇者绘卷、教官、花海甘露之光、绝缘之旗印、乐园遗落之花 +- 圣遗物主词条:生命/充能/精通、火伤、暴击/暴伤;精通/充能、精通、精通 +- 圣遗物副词条:暴击、暴伤、生命、精通、攻击、充能;精通、生命、充能、暴击 + +65. 米卡 + +- 武器:沙中伟贤的对答、香韵奏者、西风长枪、公义的酬报 +- 圣遗物:昔日宗室之仪、昔时之歌、海染砗磲 +- 圣遗物主词条:充能、生命、治疗 +- 圣遗物副词条:充能、生命、暴击 + +66. 卡维 + +- 武器:西风大剑、祭礼大剑、森林王器;天空之傲、西风大剑、螭骨剑 +- 圣遗物:海染砗磲、深林的记忆;逐影猎人、深林的记忆 +- 圣遗物主词条:精通/充能、精通、精通/治疗;充能/攻击、草伤、暴击/暴伤 +- 圣遗物副词条:精通、充能、暴击;暴击、暴伤、精通、攻击、充能 + +67. 白术 + +- 武器:西风秘典、讨龙英杰谭、祭星者之望、碧落之珑、试作金珀 +- 圣遗物:教官、深林的记忆、乐园遗落之花、海染砗磲、昔时之歌、烬城勇者绘卷 +- 圣遗物主词条:充能/生命/精通、生命/精通、生命/治疗/精通 +- 圣遗物副词条:充能、生命、精通、暴击 + +68. 绮良良 + +- 武器:圣显之钥、船坞长剑、苍古自由之誓、祭礼剑 +- 圣遗物:深林的记忆、千岩牢固、花海甘露之光、烬城勇者绘卷 +- 圣遗物主词条:生命/充能、生命、生命 +- 圣遗物副词条:生命、充能、暴击 + +69. 琳妮特 + +- 武器:苍古自由之誓、西风剑、西福斯的月光;雾切之回光、苍耀、狼牙、海渊终曲 +- 圣遗物:翠绿之影;逐影猎人 +- 圣遗物主词条:充能/精通、风伤/精通、暴击/暴伤/精通;攻击、风伤、暴击/暴伤 +- 圣遗物副词条:充能、精通、暴击、暴伤、攻击;暴击、暴伤、攻击、充能 + +70. 林尼 + +- 武器:最初的大魔术、星鹫赤羽、烈阳之嗣、阿莫斯之弓、静谧之曲 +- 圣遗物:逐影猎人 +- 圣遗物主词条:攻击/精通、火伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能、精通 + +71. 菲米尼 + +- 武器:山王长牙、焚曜千阳、狼的末路、苇海信标、螭骨剑、究极霸王超级魔剑 +- 圣遗物:冰风迷途的勇士、逐影猎人、苍白之火 +- 圣遗物主词条:攻击、冰伤/物伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +72. 那维莱特 + +- 武器:万世流涌大典、遗祀玉珑、冲浪时光、试作金珀 +- 圣遗物:逐影猎人 +- 圣遗物主词条:生命、水伤/生命、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、生命 + +73. 莱欧斯利 + +- 武器:金流监督、图莱杜拉的回忆、万世流涌大典、流浪乐章 +- 圣遗物:逐影猎人、追忆之注连 +- 圣遗物主词条:攻击/精通、冰伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能、精通 + +74. 夏洛蒂 + +- 武器:试作金珀、西风秘典、讨龙英杰谭 +- 圣遗物:昔时之歌、海染砗磲、昔日宗室之仪 +- 圣遗物主词条:充能、攻击、治疗 +- 圣遗物副词条:充能、暴击、攻击 + +75. 芙宁娜 + +- 武器:西风剑、腐殖之剑、静水流涌之辉、灰河渡手、圣显之钥 +- 圣遗物:黄金剧团、逐影猎人 +- 圣遗物主词条:充能/生命、生命/水伤、暴击/暴伤 +- 圣遗物副词条:充能、暴击、暴伤、生命、精通 + +76. 娜维娅 + +- 武器:裁断、焚曜千阳、螭骨剑、黑岩斩刀、撼地者 +- 圣遗物:回声之林夜话、逐影猎人、黄金剧团 +- 圣遗物主词条:攻击/充能、岩伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +77. 夏沃蕾 + +- 武器:香韵奏者、西风长枪 +- 圣遗物:昔日宗室之仪、烬城勇者绘卷、昔时之歌;昔日宗室之仪、绝缘之旗印 +- 圣遗物主词条:生命、生命、生命/治疗;充能/攻击、火伤、暴击/暴伤 +- 圣遗物副词条:生命、暴击、充能;暴击、暴伤、充能、攻击 + +78. 嘉明 + +- 武器:硕果钩、螭骨剑、饰铁之花、狼的末路 +- 圣遗物:炽烈的炎之魔女、逐影猎人、长夜之誓 +- 圣遗物主词条:精通、火伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、精通、攻击、充能 + +79. 闲云 + +- 武器:鹤鸣余音、证誓之明瞳、天空之卷、西风秘典;溢彩心念、鹤鸣余音、流浪乐章 +- 圣遗物:翠绿之影、昔时之歌、昔日宗室之仪;长夜之誓、沙上楼阁史话 +- 圣遗物主词条:充能/攻击、攻击、攻击;攻击、风伤、暴击/暴伤 +- 圣遗物副词条:攻击、充能、暴击;暴击、暴伤、攻击 + +80. 千织 + +- 武器:有乐御簾切、岩峰巡歌、黎明神剑、息燧之笛 +- 圣遗物:黄金剧团、华馆梦醒形骸记、逐影猎人 +- 圣遗物主词条:防御、岩伤/防御、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、防御、攻击 + +81. 阿蕾奇诺 + +- 武器:赤月之形、和璞鸢、峡湾长歌、白缨枪 +- 圣遗物:谐律异想断章、角斗士的终幕礼 +- 圣遗物主词条:攻击/精通、火伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通 + +82. 赛索斯 + +- 武器:猎人之径、星鹫赤羽、弹弓、若水 +- 圣遗物:流浪大地的乐团、沙上楼阁史话、饰金之梦 +- 圣遗物主词条:精通、雷伤/精通、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、精通、攻击、充能 + +83. 克洛琳德 + +- 武器:赦罪、波乱月白经津、厄水之祸、海渊终曲 +- 圣遗物:来歆余响、谐律异想断章、角斗士的终幕礼 +- 圣遗物主词条:攻击、雷伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能、精通 + +84. 希格雯 + +- 武器:白雨心弦、终末嗟叹之诗、西风猎弓;白雨心弦、若水 +- 圣遗物:昔时之歌、海染砗磲;绝缘之旗印、逐影猎人 +- 圣遗物主词条:生命、生命、生命;生命/充能、生命/水伤、暴击/暴伤 +- 圣遗物副词条:生命、暴击;暴击、暴伤、生命、充能 + +85. 艾梅莉埃 + +- 武器:柔灯挽歌、香韵奏者、决斗之枪、风信之锋 +- 圣遗物:未竟的遐思、深林的记忆 +- 圣遗物主词条:攻击、草伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +86. 卡齐娜 + +- 武器:西风长枪、虹的行迹、决斗之枪 +- 圣遗物:烬城勇者绘卷 +- 圣遗物主词条:防御/充能、岩伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、防御、充能 + +87. 玛拉妮 + +- 武器:冲浪时光、遗祀玉珑、万世流涌大典、木棉之环 +- 圣遗物:黑曜秘典 +- 圣遗物主词条:生命/精通、水伤/生命、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、生命、精通 + +88. 基尼奇 + +- 武器:山王长牙、焚曜千阳、螭骨、撼地者 +- 圣遗物:黑曜秘典、未竟的遐思 +- 圣遗物主词条:攻击、草伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击 + +89. 希诺宁 + +- 武器:岩峰巡歌、西风剑、苍古自由之誓、息燧之笛;岩峰巡歌、有乐御簾切 +- 圣遗物:烬城勇者绘卷、教官、悠古的磐岩;黑曜秘典 +- 圣遗物主词条:充能/防御、防御、防御/治疗;防御/充能、岩伤/防御、暴击/暴伤 +- 圣遗物副词条:充能、防御、暴击率;暴击、暴伤、防御、充能 + +90. 欧洛伦 + +- 武器:终末嗟叹之诗、冬极白星、若水、碎链、西风猎弓 +- 圣遗物:烬城勇者绘卷 +- 圣遗物主词条:攻击/充能、雷伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +91. 恰斯卡 + +- 武器:星鹫赤羽、最初的大魔术、若水、静谧之曲 +- 圣遗物:黑曜秘典、逐影猎人 +- 圣遗物主词条:攻击、攻击/火伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通、充能 + +92. 茜特菈莉 + +- 武器:祭星者之望、祭礼残章、讨龙英杰谭;祭星者之望、千夜浮梦 +- 圣遗物:烬城勇者绘卷、教官;黑曜秘典、烬城勇者绘卷 +- 圣遗物主词条:精通/充能、精通、精通;攻击/精通、冰伤/精通、暴击/暴伤 +- 圣遗物副词条:精通、充能、暴击;暴击、暴伤、攻击、精通、充能 + +93. 玛薇卡 + +- 武器:焚曜千阳、螭骨剑、赤角石溃杵、黑岩斩刀 +- 圣遗物:黑曜秘典、逐影猎人;烬城勇者绘卷 +- 圣遗物主词条:精通/攻击、火伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、精通、攻击 + +94. 蓝砚 + +- 武器:祭星者之望、讨龙英杰谭、尘世之锁 +- 圣遗物:翠绿之影、黄金剧团 +- 圣遗物主词条:攻击/充能、攻击/风伤、攻击/暴 +- 圣遗物副词条:攻击、充能、暴击、暴伤、精通 + +95. 梦见月瑞希 + +- 武器:寝正月初晴、白辰之环、流浪的晚星 +- 圣遗物:翠绿之影、饰金之梦 +- 圣遗物主词条:精通/充能、精通、精通 +- 圣遗物副词条:精通、充能、暴击 + +96. 伊安珊 + +- 武器:薙草之稻光、香韵奏者、西风长枪、天空之脊 +- 圣遗物:烬城勇者绘卷、昔日宗室之仪 +- 圣遗物主词条:充能/攻击、攻击、攻击 +- 圣遗物副词条:攻击、充能、暴击 + +97. 瓦雷莎 + +- 武器:溢彩心念、神乐之真意、四风原典、流浪乐章 +- 圣遗物:长夜之誓、黑曜秘典、逐影猎人 +- 圣遗物主词条:攻击、雷伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +98. 伊法 + +- 武器:寝正月初晴、千夜浮梦、流浪的晚星、白辰之环;图莱杜拉的回忆、流浪乐章 +- 圣遗物:翠绿之影;黑曜秘典 +- 圣遗物主词条:精通、精通、精通;攻击、风伤、暴击/暴伤 +- 圣遗物副词条:精通、充能、暴击;暴击、暴伤、攻击、充能 + +99. 爱可菲 + +- 武器:香韵奏者、息灾、圣祭者的辉杖、西风长枪 +- 圣遗物:黄金剧团 +- 圣遗物主词条:攻击、冰伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能 + +100. 塔利雅 + +- 武器:西风剑、圣显之钥、祭礼剑、船坞长剑 +- 圣遗物:昔日宗室之仪、烬城勇者绘卷 +- 圣遗物主词条:生命/充能、生命、生命/暴 +- 圣遗物副词条:生命、充能、暴击 + +101. 丝柯克 + +- 武器:苍耀、波乱月白经津、厄水之祸、海渊终曲 +- 圣遗物:深廊终曲、逐影猎人、冰风迷途的勇士 +- 圣遗物主词条:攻击、冰伤、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击 + +102. 伊涅芙 + +- 武器:支离轮光、赤沙之杖、峡湾长歌、掘金之锹 +- 圣遗物:纺月的夜歌、饰金之梦 +- 圣遗物主词条:攻击/精通、攻击/精通、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通 + +103. 爱诺 + +- 武器:西风大剑、玛海菈的水色、拾慧铸熔、万能钥匙 +- 圣遗物:纺月的夜歌、昔日宗室之仪 +- 圣遗物主词条:充能/精通、精通、暴击/精通 +- 圣遗物副词条:充能、精通、暴击 + +104. 菈乌玛 + +- 武器:纺夜天镜、千夜浮梦、天光的纺琴、乌髓孑灯 +- 圣遗物:纺月的夜歌、 +- 圣遗物主词条:精通/充能、精通、精通 +- 圣遗物副词条:精通、充能、暴击、暴伤 + +105. 菲林斯 + +- 武器:血染荒城、赤沙之杖、决斗之枪、掘金之锹 +- 圣遗物:穹境示现之夜 +- 圣遗物主词条:攻击、攻击、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、精通、充能 + +106. 奈芙尔 + +- 武器:真语秘匣、纺夜天镜、霜辰、乌髓孑灯 +- 圣遗物:穹境示现之夜 +- 圣遗物主词条:精通、精通、暴击/暴伤/精通 +- 圣遗物副词条:暴击、暴伤、精通、攻击 + +107. 雅珂达 + +- 武器:西风猎弓、终末嗟叹之诗、祭礼弓 +- 圣遗物:翠绿之影、纺月的夜歌 +- 圣遗物主词条:充能、攻击、治疗 +- 圣遗物副词条:充能、攻击、暴击、精通、暴伤 + +108. 杜林 + +- 武器:黑蚀、苍古自由之誓、狼牙 +- 圣遗物:风起之时、昔日宗室之仪、绝缘之旗印 +- 圣遗物主词条:攻击、火伤/攻击、暴击/暴伤 +- 圣遗物副词条:暴击、暴伤、攻击、充能、精通 +