// ==UserScript==
// @name CBG Helper
// @namespace https://yys.zhebu.work/
// @version 0.1.1
// @description A helper tool for Onmyoji player to look for good account.
// @author CJ
// @match https://yys.cbg.163.com/*
// @grant none
// @run-at document-start
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
var acct_info = {};
var FRAC_N = 5
var url_match = "api/get_equip_detail";
var _open = XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function (method, URL) {
var _onreadystatechange = this.onreadystatechange,
_this = this;
_this.onreadystatechange = function () {
// catch only completed 'api/search/universal' requests
if (_this.readyState === 4 && _this.status === 200 && ~URL.indexOf(url_match)) {
try {
//////////////////////////////////////
// THIS IS ACTIONS FOR YOUR REQUEST //
// EXAMPLE: //
//////////////////////////////////////
var data = JSON.parse(_this.responseText); // {"fields": ["a","b"]}
data = floatify(data)
// rewrite responseText
Object.defineProperty(_this, 'responseText', {value: JSON.stringify(data)});
Object.defineProperty(_this, 'response', {value: JSON.stringify(data)});
/////////////// END //////////////////
} catch (e) {}
console.log('Caught! :)', method, URL/*, _this.responseText*/);
}
// call original callback
if (_onreadystatechange) _onreadystatechange.apply(this, arguments);
};
// detect any onreadystatechange changing
Object.defineProperty(this, "onreadystatechange", {
get: function () {
return _onreadystatechange;
},
set: function (value) {
_onreadystatechange = value;
}
});
return _open.apply(_this, arguments);
};
function addExtendedHighlight() {
if (document.getElementById('cbghelper_exthighlight') || !acct_info.hasOwnProperty("summary")) {
return;
}
let itms = [];
let { fastest, heads, feet } = acct_info.summary;
if(heads.length > 0 || feet.length > 0) {
let li = document.createElement('li');
let x = heads.length > 0 ? heads.length : '无';
let y = feet.length > 0? feet.length : '无';
li.innerText = `${x}头${y}脚`;
itms.push(li)
}
let fastest_spd = document.createElement('li');
fastest_spd.innerText = `最快一速${[1, 2, 3, 4, 5, 6].reduce((total, p) => total + fastest[p]['散件'], 0).toFixed(2)}`;
fastest_spd.id = 'cbghelper_exthighlight';
itms.push(fastest_spd);
let zc_spd = document.createElement('li');
let zc_spd_val = [1, 2, 3, 4, 5, 6].reduce((total, p) => total + fastest[p]['招财猫'], 0);
let spd_inc = [1, 2, 3, 4, 5, 6].map(p => fastest[p]['散件'] - fastest[p]['招财猫'], 0);
spd_inc.sort((a, b) => b - a);
zc_spd_val += spd_inc[0] + spd_inc[1];
zc_spd.innerText = `招财一速${zc_spd_val.toFixed(2)}`;
itms.push(zc_spd);
let highlight = document.getElementsByClassName('highlight')[0];
for (let li of itms) {
highlight.appendChild(li);
}
}
function summaryPage() {
let wrapper = document.createElement('div');
if (!acct_info.hasOwnProperty('summary')) {
wrapper.appendChild(document.createTextNode("数据加载出错,请尝试刷新页面"))
return wrapper;
}
let decimal = 2;
let { fastest, heads, feet, fullspd_cnt } = acct_info.summary;
fastest = JSON.parse(JSON.stringify(fastest)); // make a deep copy
let title = document.createElement('h3')
title.innerText = "御魂亮点"
let spd = document.createElement('section')
let sortByValue = function (a, b) { return b.value - a.value}
let headStr = heads.length > 0 ? heads.sort(sortByValue).map(itm => `${itm.name}: ${(itm.value).toFixed(decimal)}`.trim()).join(", ") : "无";
let feetStr = feet.length > 0 ? feet.sort(sortByValue).map(itm => `${itm.name}: ${(itm.value).toFixed(decimal)}`.trim()).join(", ") : "无";
let fastest_spd = [1, 2, 3, 4, 5, 6].reduce((total, p) => total + fastest[p]['散件'], 0);
let zc_spd_val = [1, 2, 3, 4, 5, 6].reduce((total, p) => total + fastest[p]['招财猫'], 0);
let spd_inc = [1, 2, 3, 4, 5, 6].map(p => fastest[p]['散件'] - fastest[p]['招财猫'], 0);
spd_inc.sort((a, b) => b - a);
zc_spd_val += spd_inc[0] + spd_inc[1];
let td_val = function (pos, name) {
let res = `${fastest[pos][name].toFixed(decimal)} `
if (fullspd_cnt[pos][name] > 0) {
res += `(${fullspd_cnt[pos][name]})`
}
return res;
}
Object.keys(fastest[2]).forEach(k => fastest[2][k] = fastest[2][k]-57 > 0 ? fastest[2][k] - 57 : 0)
let fastest_tbl = `
位置 | ${[1, 2, 3, 4, 5, 6].map(i => `${i} | `)} 4(命中) |
散件 | ${[1, 2, 3, 4, 5, 6, 7].map(i => `${td_val(i, '散件')} | `)}
招财猫 | ${[1, 2, 3, 4, 5, 6, 7].map(i => `${td_val(i, '招财猫')} | `)}
火灵 | ${[1, 2, 3, 4, 5, 6, 7].map(i => `${td_val(i, '火灵')} | `)}
蚌精 | ${[1, 2, 3, 4, 5, 6, 7].map(i => `${td_val(i, '蚌精')} | `)}
`;
spd.innerHTML = `头: ${headStr}
脚: ${feetStr}
散件一速: ${fastest_spd.toFixed(5)}
招财一速: ${zc_spd_val.toFixed(5)}
`
let title2 = document.createElement('h3')
title2.innerText = "各位置一速(满速个数)"
let fastest_sec = document.createElement('section')
fastest_sec.innerHTML = fastest_tbl
if(fastest_sec.firstChild.nodeType === Node.TEXT_NODE) {
fastest_sec.firstChild.textContent = '';
}
wrapper.appendChild(title)
wrapper.appendChild(spd)
wrapper.appendChild(title2)
wrapper.appendChild(fastest_sec)
return wrapper;
}
function addHighlightView() {
if (document.getElementById('cbghelper_highlight')) {
return;
}
let div = document.createElement('div');
div.id = 'cbghelper_highlight';
div.appendChild(summaryPage());
let wrapper = document.getElementsByClassName('content-pvp')[0];
wrapper.appendChild(div)
}
function addDownloadBtn() {
if (document.getElementById('cbghelper_download')) {
return;
}
let b = document.createElement('a');
b.innerText = "(💾保存为JSON)";
b.onclick = function () {
console.log("To save data!");
saveToJsonHelper();
}
b.id = "cbghelper_download"
b.style.cursor = "pointer";
let yuhun_list = document.getElementsByClassName('yuhun-list')[0];
yuhun_list.parentNode.childNodes[1].append(b)
}
function addDownloadBtnWrapper () {
if (document.getElementsByClassName('yuhun-list').length) {
addDownloadBtn();
}
}
function addExtHighlightWrapper () {
if (document.getElementsByClassName('highlight').length) {
addExtendedHighlight();
}
}
function addHighlightViewWrapper() {
if (document.getElementsByClassName('content-pvp').length && acct_info.ready) {
addHighlightView();
}
}
function init() {
let checkfn_list = {
'cbghelper_download': addDownloadBtnWrapper,
'cbghelper_exthighlight': addExtHighlightWrapper,
'cbghelper_highlight': addHighlightViewWrapper
};
let handlers = {};
let checkExist = setInterval(function () {
if (!document.URL.startsWith("https://yys.cbg.163.com/cgi/mweb/equip")) {
return;
}
for (let eid of Object.keys(checkfn_list)) {
if (document.getElementById(eid) && eid in handlers) {
clearInterval(handlers[eid]);
delete handlers[eid];
} else if (document.getElementById(eid) || eid in handlers) {
continue;
} else {
handlers[eid] = setInterval(checkfn_list[eid], 200);
}
}
}, 100);
};
init();
//----------------------
const floatify = function (data) {
let equip = data['equip'];
let acct_detail = JSON.parse(equip['equip_desc']);
let mitama_list = acct_detail['inventory'];
let hero_list = acct_detail['heroes'];
try {
var message = {
name: equip.seller_name,
roleid: equip.seller_roleid,
ordersn: equip.game_ordersn,
mitama_list
};
acct_info.latest = message;
} catch (error) {}
Object.entries(mitama_list).forEach(([key, value]) => {
mitama_list[key] = floatify_mitama(value)
});
Object.entries(hero_list).forEach(([key, value]) => {
hero_list[key] = floatify_hero(value, mitama_list)
});
acct_detail['inventory'] = mitama_list
equip['equip_desc'] = JSON.stringify(acct_detail)
data['equip'] = equip;
acctHighlight(mitama_list, hero_list);
return data
}
function getPropValue(mitama_set, mitama_list, propName) {
let res = 0;
for (let mitama_id of mitama_set) {
var { attrs, single_attr=[] } = mitama_list[mitama_id];
for (let [p, v] of attrs) {
if (p === propName) {
res += parseFloat(v);
}
}
if (single_attr.length > 0 && single_attr[0] === propName) {
res += parseFloat(single_attr[1])
}
}
return res
}
function floatify_hero(hero_data, mitama_list) {
var { attrs, equips } = hero_data
Object.keys(attrs).forEach(propName => {
if (propName === '速度' && parseFloat(attrs[propName].add_val) > 0) {
if(hero_data.heroId === 255 && hero_data.awake === 1) { //觉醒阎魔+10速度
attrs[propName].add_val = 10.0
} else {
attrs[propName].add_val = 0.0
}
attrs[propName].add_val += getPropValue(equips, mitama_list, propName);
attrs[propName].add_val = attrs[propName].add_val.toFixed(FRAC_N)
}
if (propName === '暴击' && parseFloat(attrs[propName].add_val) > 0) {
let suit_cp = ["针女","三味","网切","伤魂鸟","破势","镇墓兽","青女房"];
attrs[propName].add_val = getPropValue(equips, mitama_list, propName);
let suit_names = equips.map(x => mitama_list[x].name);
let suit_count = {};
for (let n of suit_names) {
if (n in suit_count) {
suit_count[n] += 1;
} else {
suit_count[n] = 1;
}
}
Object.keys(suit_count).forEach(n => {
if (suit_count[n] >= 2 && suit_cp.includes(n)) {
attrs[propName].add_val += suit_count[n] === 6? 30:15;
}
})
attrs[propName].add_val = attrs[propName].add_val.toFixed(2) + "%";
}
})
return hero_data;
}
function floatify_mitama(mitama) {
var { rattr, attrs } = mitama;
mitama["attrs"] = [attrs[0], ...calAttrs(rattr)];
return mitama;
}
function calAttrs(rattrs, format = true) {
var enAttrNames = ['attackAdditionRate',
'attackAdditionVal',
'critPowerAdditionVal',
'critRateAdditionVal',
'debuffEnhance',
'debuffResist',
'defenseAdditionRate',
'defenseAdditionVal',
'maxHpAdditionRate',
'maxHpAdditionVal',
'speedAdditionVal']
var cnAttrNames = ['攻击加成', '攻击', '暴击伤害', '暴击',
'效果命中', '效果抵抗', '防御加成',
'防御', '生命加成', '生命', '速度']
var basePropValue = {
'攻击加成': 3, '攻击': 27, '暴击伤害': 4, '暴击': 3,
'效果抵抗': 4, '效果命中': 4, '防御加成': 3,
'防御': 5, '生命加成': 3, '生命': 114, '速度': 3
}
var percentProp = {
'攻击加成': true, '攻击': false, '暴击伤害': true, '暴击': true,
'效果抵抗': true, '效果命中': true, '防御加成': true,
'防御': false, '生命加成': true, '生命': false, '速度': false
}
var e2cNameMap = Object.assign({}, ...enAttrNames.map((n, index) => ({ [n]: cnAttrNames[index] })));
var res = Object();
for (let rattr of rattrs) {
var [prop, v] = rattr;
prop = e2cNameMap[prop];
if (prop in res) {
res[prop] += v;
} else {
res[prop] = v;
}
}
return Object.keys(res).sort().map(p => {
var v = res[p] * basePropValue[p]
if (format) {
v = v.toFixed(FRAC_N);
if (percentProp[p]) {
v += "%";
}
}
return [p, v];
})
}
function soulToJson(soulItem) {
const { attrs, level, qua, rattr, uuid, name, pos, single_attr = [] } = soulItem;
var born = parseInt(uuid.substring(0, 8), 16);
let soulDict = {
'固有属性': single_attr.length ? single_attr[0] : null,
'生成时间': born,
'御魂等级': level,
'御魂星级': qua,
'御魂ID': uuid,
'御魂类型': name,
'位置': pos
};
let PROPNAMES = ['攻击', '攻击加成', '防御',
'防御加成', '暴击', '暴击伤害', '生命', '生命加成', '效果命中',
'效果抵抗', '速度'];
PROPNAMES.map(function (e, i) {
soulDict[e] = 0;
});
let percent = ['攻击加成', '防御加成', '暴击', '暴击伤害', '生命加成', '效果命中', '效果抵抗'];
for (let [p, v] of [attrs[0], ...calAttrs(rattr, false)]) {
v = parseFloat(v)
if (percent.includes(p)) {
v = v / 100;
}
soulDict[p] += v;
}
if (single_attr.length) {
const [p, v] = single_attr;
soulDict[p] += parseFloat(v) / 100;
}
return soulDict;
}
function saveToJson(soulLists) {
var fileContent = 'data:text/json;charset=utf-8,'
let soulListJson = Object.values(soulLists).map(soulToJson);
soulListJson.unshift('yuhun_ocr2.0');
fileContent += JSON.stringify(soulListJson);
var encodedUri = encodeURI(fileContent);
var link = document.createElement('a');
link.setAttribute('href', encodedUri);
link.setAttribute('download', 'yuhun.json');
link.innerHTML = 'Click Here to download your data';
document.body.appendChild(link); // Required for FF
link.click();
link.parentNode.removeChild(link);
}
function saveToJsonHelper() {
saveToJson(acct_info.latest.mitama_list);
}
function acctHighlight(mitama_list, hero_list) {
let fastest = {};
let fullspd_cnt = {}
let heads = [];
let feet = [];
let suit_imp = ["招财猫", "火灵", "蚌精"];
let all_pos = [1,2,3,4,5,6];
for(let p of [1,2,3,4,5,6,7]){ //7 for 命中@4
fastest[p] = {'散件': 0};
fullspd_cnt[p] = {'散件': 0}
for(let name of suit_imp) {
fastest[p][name] = 0;
fullspd_cnt[p][name] = 0;
}
}
Object.entries(mitama_list).forEach(([key, m]) => {
let {attrs, pos, name, qua, rattr} = m;
let spd = 0, spdpt = 0;
for (let [p, v] of attrs) {
if (p === '速度') {
spd += parseFloat(v);
}
}
for (let rattr_entry of rattr) {
var [prop, v] = rattr_entry;
if(prop === 'speedAdditionVal') {
spdpt += 1
}
}
if (spdpt === 6 && (pos !== 2 || spd > 70)) {
fullspd_cnt[pos]['散件'] += 1
if(suit_imp.includes(name)) {
fullspd_cnt[pos][name] += 1
}
if (pos === 2) {
heads.push({pos, name, value: spd-57});
} else if (pos === 4 && attrs[0][0] === '效果命中') {
fullspd_cnt[7]['散件'] += 1
if(suit_imp.includes(name)) {
fullspd_cnt[7][name] += 1
}
feet.push({pos, name, value: spd});
}
}
if(suit_imp.includes(name)) {
fastest[pos][name] = fastest[pos][name] > spd ? fastest[pos][name] : spd;
}
fastest[pos]['散件'] = fastest[pos]['散件'] > spd ? fastest[pos]['散件'] : spd;
if (pos === 4 && attrs[0][0] === '效果命中') {
pos = 7
if(suit_imp.includes(name)) {
fastest[pos][name] = fastest[pos][name] > spd ? fastest[pos][name] : spd;
}
fastest[pos]['散件'] = fastest[pos]['散件'] > spd ? fastest[pos]['散件'] : spd;
}
});
acct_info.summary = {
heads,
feet,
fastest,
fullspd_cnt
}
acct_info.ready = true;
}
})();