Files
bettergi-scripts-list/repo/js/ArtifactsAssistant/main.js
756yang 97e2bead13 增加js脚本-圣遗物辅助 (#2565)
* 增加js脚本-圣遗物辅助

* 修复圣遗物辅助的问题
2025-12-26 20:48:02 +08:00

760 lines
32 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/************************ 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);
})();