- // ==UserScript==
- // @name CBG Helper 2024.12 修复版
- // @namespace https://yys.zhebu.work/
- // @version 0.1.7.b
- // @description 修复 CBG Helper 无法使用的问题,原项目为 https://gf.qytechs.cn/zh-CN/scripts/406264-cbg-helper, 有问题找 LingErEd (https://space.bilibili.com/103021226)
- // @author Jie Chu (原作者) & LingErEd (修复)
- // @match https://yys.cbg.163.com/*
- // @grant none
- // @run-at document-start
- // @require https://update.gf.qytechs.cn/scripts/465643/1421695/ajaxHookerLatest.js
- // ==/UserScript==
- (function() {
- 'use strict';
- let panel_class_name = 'content-overview';
- let acct_info = {
- ready: false
- };
- let FRAC_N = 5;
- let url_get_equip_detail = "/cgi/api/get_equip_detail";
- let url_get_equip_desc = "/cgi/api/get_equip_desc"; // added by LingErEd
- let suit_imp = ["散件", "招财猫", "火灵", "蚌精", "共潜", '遗念火']; // 重要套装,可自行添加
- let suit_by_props = {
- '暴击伤害': ["无刀取"],
- '暴击': ["针女", "三味", "网切", "伤魂鸟", "破势", "镇墓兽", "青女房", "海月火玉"],
- '攻击加成': ["蝠翼", "轮入道", "狰", "鸣屋", "心眼", "阴摩罗", "狂骨", "兵主部", "贝吹坊"],
- '防御加成': ["珍珠", "魅妖", "雪幽魂", "招财猫", "反枕", "日女巳时", "木魅", "出世螺", "奉海图"],
- '生命加成': ["地藏像", "涅槃之火", "被服", "镜姬", "钟灵", "薙魂", "树妖", "涂佛", "恶楼"],
- '效果抵抗': ["骰子鬼", "返魂香", "魍魉之匣", "幽谷响", "共潜"],
- '效果命中': ["蚌精", "火灵", "飞缘魔", "遗念火"],
- '首领御魂': ["土蜘蛛", "胧车", "荒骷髅", "地震鲶", "蜃气楼", "鬼灵歌伎", "夜荒魂"]
- }
- // eslint-disable-next-line no-undef
- ajaxHooker.hook(request => {
- let originalResponse = request.response;
- if (request.url.startsWith(url_get_equip_detail)) {
- console.log('ajaxHooker url_get_equip_detail');
- request.response = res => {
- if (res.status == 200) {
- // console.log(res.responseText);
- const data = JSON.parse(res.responseText)
- floatify(data, 'url_get_equip_detail')
- }
- if(originalResponse) try {originalResponse.apply(this, [res]);} catch (error) {}
- };
- }
- if (request.url.startsWith(url_get_equip_desc)) {
- console.log('ajaxHooker url_get_equip_desc');
- request.response = res => {
- if (res.status == 200) {
- // const data = JSON.parse(res.responseText)
- floatify({
- equip: res.json
- }, 'url_get_equip_desc')
- }
- if(originalResponse) try {originalResponse.apply(this, [res]);} catch (error) {}
- }
- }
- })
-
- function nowrapText(textLabel) {
- return `<span class="cbghelper_nowrap">${textLabel}</span>`
- }
-
- function addExtendedHighlight() {
- if (document.getElementById('cbghelper_exthighlight') || !acct_info.hasOwnProperty("summary")) {
- return;
- }
- let {
- fastest,
- heads,
- feet,
- hero_info
- } = acct_info.summary;
- let itms = [];
- let build_item = function(label, id) {
- let li = document.createElement('li');
- li.innerText = label;
- return li
- };
- //collection of heros
- let total = hero_info['ssr']['all'] + hero_info['sp']['all'];
- let got_total = hero_info['ssr']['got'] + hero_info['sp']['got'];
- if (total === got_total) {
- itms.push(build_item('SSR/SP全收集'));
- } else if (hero_info['ssr']['all'] === hero_info['ssr']['got']) {
- itms.push(build_item('SSR全收集'));
- }
- if (hero_info['x']['all'] === hero_info['x']['got']) {
- itms.push(build_item('联动全收集'));
- }
- //number of heads and feet
- if (heads.length > 0 || feet.length > 0) {
- let x = heads.length > 0 ? heads.length : '无';
- let y = feet.length > 0 ? feet.length : '无';
- let label = `${x}头${y}脚`;
- itms.push(build_item(label))
- }
- //fastest speed
- let fastest_spd_label = `最快一速${[1, 2, 3, 4, 5, 6].reduce((total, p) => total + fastest[p]['散件'], 0).toFixed(2)}`;
- let fastest_spd = build_item(fastest_spd_label)
- fastest_spd.id = 'cbghelper_exthighlight';
- itms.push(fastest_spd);
- //fastest zhaocai speed
- 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 zc_spd_label = `招财一速${zc_spd_val.toFixed(2)}`;
- itms.push(build_item(zc_spd_label));
-
- let highlight = document.getElementsByClassName('highlight')[0];
- for (let li of itms) {
- highlight.appendChild(li);
- }
- }
-
- function summaryPage() {
- let wrapper = document.createElement('div');
- wrapper.classList.add('module');
- if (!acct_info.hasOwnProperty('summary')) {
- wrapper.appendChild(document.createTextNode("数据加载出错,请尝试刷新页面"))
- return wrapper;
- }
- let decimal = 2;
- let {
- fastest,
- heads,
- feet,
- fullspd_cnt
- } = acct_info.summary;
- let fullspd_suit = Object.fromEntries(suit_imp.map(name => [name, 0]));
- fastest = JSON.parse(JSON.stringify(fastest)); // make a deep copy
- let suit_stats = {};
- for (let p of [1, 2, 3, 4, 5, 6]) {
- for (let name in fullspd_cnt[p]) {
- if (fullspd_suit[name] === 0) {
- continue;
- }
- if (name in suit_stats) {
- suit_stats[name].push(p);
- } else {
- suit_stats[name] = [p];
- }
- }
- }
- for (let name in suit_stats) {
- if (suit_stats[name].length >= 4) {
- if (name in fullspd_suit) {
- continue;
- } else {
- fullspd_suit[name] = 0;
- }
- }
- }
- let fast_suit_speed = function(name) {
- let suit_fastest = Object.fromEntries([1, 2, 3, 4, 5, 6].map(p => [p, name in fastest[p] ? fastest[p][name] : 0]));
- let suit_spd_val = [1, 2, 3, 4, 5, 6].reduce((total, p) => total + suit_fastest[p], 0);
- let spd_inc = [1, 2, 3, 4, 5, 6].map(p => fastest[p]['散件'] - suit_fastest[p]);
- spd_inc.sort((a, b) => b - a);
- suit_spd_val += spd_inc[0] + spd_inc[1];
- return suit_spd_val;
- }
- Object.keys(fullspd_suit).forEach(name => {
- fullspd_suit[name] = fast_suit_speed(name);
- })
-
- let sortByValue = function(a, b) {
- return b.value - a.value
- }
- let headStr = heads.length > 0 ? heads.sort(sortByValue).map(itm => `<span class="data-value">${itm.name}: ${(itm.value).toFixed(decimal)}</span>`.trim()).join(", ") : "无";
- let feetStr = feet.length > 0 ? feet.sort(sortByValue).map(itm => `<span class="data-value">${itm.name}: ${(itm.value).toFixed(decimal)}</span>`.trim()).join(", ") : "无";
- let td_val = function(pos, name) {
- let fullspd = fullspd_cnt[pos][name] > 0;
- let spd = name in fastest[pos] ? fastest[pos][name].toFixed(decimal) : 0;
- let res = `<span${fullspd? "":" class=disabled"}>${spd}</span> `
- if (fullspd) {
- res += nowrapText(`(${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 speed_summary = function(name) {
- return `<tr> <td>${name}</td> ${[1, 2, 3, 4, 5, 6, 7].map(i => `<td>${td_val(i, name)}</td>`)} </tr>`;
- }
- let fastest_tbl = `<table width="100%">
- <tr> <th>位置</th> ${[1, 2, 3, 4, 5, 6].map(i => `<th>${i}</th>`)} <th>4${nowrapText("(命中)")}</th> </tr>
- ${ Object.keys(fullspd_suit).map(name => speed_summary(name)).join(" ") }
- </table>`;
- let suit_table = `<table width="100%">
- <tr> <th>御魂名称</th> <th>套装一速</th></tr>
- ${ Object.keys(fullspd_suit).map(name => `<tr> <th>${name}</th> <td>${fullspd_suit[name].toFixed(5)}</td></tr>\n`).join("") }
- </table>`;
-
- let title = document.createElement('div')
- title.classList.add('title');
- title.innerText = "御魂亮点"
- let spd = document.createElement('section')
- spd.innerHTML = `<div><span class="data-name">头:</span> ${headStr} </div>
- <div><span class="data-name">脚:</span> ${feetStr} </div>`;
- let title2 = document.createElement('div');
- title2.innerText = "套装一速(非独立)";
- title2.classList.add('title');
- let suit = document.createElement('section');
- suit.innerHTML = suit_table;
-
- let title3 = document.createElement('div');
- title3.innerText = "各位置一速(满速个数)";
- title3.classList.add('title');
-
- 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(suit);
- wrapper.appendChild(title3);
- wrapper.appendChild(fastest_sec);
- return wrapper;
- }
-
- function addHighlightView() {
- if (document.getElementById('cbghelper_highlight')) {
- return;
- }
- let div = document.createElement('div');
- div.id = 'cbghelper_highlight';
- div.class = 'module';
- div.appendChild(summaryPage());
- let wrapper = document.getElementsByClassName(panel_class_name)[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('content-top-left')[0];
- yuhun_list.getElementsByTagName('h3')[1].appendChild(b);
- }
-
- function addDownloadBtnWrapper() {
- if (document.getElementsByClassName('yuhun-list').length) {
- addDownloadBtn();
- }
- }
-
- function addExtHighlightWrapper() {
- if (document.getElementsByClassName('highlight').length) {
- addExtendedHighlight();
- }
- }
-
- function addHighlightViewWrapper() {
- if (document.getElementsByClassName(panel_class_name).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, type) {
- console.log('floatify', data);
- let equip = data['equip'];
- // 如果没有 equip_desc 则返回
- if (!equip.hasOwnProperty('equip_desc')) {
- console.log('No equip_desc, from', type);
- return data;
- }
- let acct_detail = JSON.parse(equip['equip_desc']);
- let mitama_list = acct_detail['inventory'];
- let hero_list = acct_detail['heroes'];
- let hero_info = acct_detail['hero_history'];
-
- try {
- var message = {
- name: equip.seller_name,
- roleid: equip.seller_roleid,
- ordersn: equip.game_ordersn,
- mitama_list
- };
- var event = new CustomEvent("SaveLastAccount", {
- detail: message
- });
- window.dispatchEvent(event);
- 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_info);
-
- 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 = suit_by_props['暴击'];
- 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 acctHighlight(mitama_list, hero_info) {
- let fastest = {};
- let fullspd_cnt = {}
- let heads = [];
- let feet = [];
- 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] = {};
- fullspd_cnt[p] = {};
- 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 < 1 || (pos === 2 && spd < 57)) {
- return;
- }
- if (spdpt === 6 && (pos !== 2 || spd > 70)) {
- fullspd_cnt[pos]['散件'] += 1
- if (name in fullspd_cnt[pos]) {
- fullspd_cnt[pos][name] += 1;
- } else {
- 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 (name in fullspd_cnt[pos]) {
- fullspd_cnt[7][name] += 1;
- } else {
- fullspd_cnt[7][name] = 1;
- }
- feet.push({
- pos,
- name,
- value: spd
- });
- }
- }
- if (name in fastest[pos]) {
- fastest[pos][name] = fastest[pos][name] > spd ? fastest[pos][name] : spd;
- } else {
- fastest[pos][name] = spd;
- }
- fastest[pos]['散件'] = fastest[pos]['散件'] > spd ? fastest[pos]['散件'] : spd;
- if (pos === 4 && attrs[0][0] === '效果命中') {
- pos = 7;
- if (name in fastest[pos]) {
- fastest[pos][name] = fastest[pos][name] > spd ? fastest[pos][name] : spd;
- } else {
- fastest[pos][name] = spd;
- }
- fastest[pos]['散件'] = fastest[pos]['散件'] > spd ? fastest[pos]['散件'] : spd;
- }
- });
- acct_info.summary = {
- heads,
- feet,
- fastest,
- fullspd_cnt,
- hero_info
- }
- acct_info.ready = true;
- }
-
- function saveToJsonHelper() {
- // var event = new CustomEvent("LoadLastAccount", {});
- // window.dispatchEvent(event);
- // console.log("Account data requested!");
- saveToJson(acct_info.latest.mitama_list);
- }
- // function needed that is not included from chrome extension
- var cssRules = `
- .cbghelper_nowrap {
- white-space: nowrap;
- }
- `
-
- function injectCSS() {
- var style = document.createElement('style');
- style.innerHTML = cssRules;
- document.getElementsByTagName('head')[0].appendChild(style);
- }
-
- injectCSS();
- })()