Greasy Fork

Greasy Fork is available in English.

CBG Helper

A helper tool for Onmyoji player to look for good account.

当前为 2020-10-05 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         CBG Helper
// @namespace    https://yys.zhebu.work/
// @version      0.0.9
// @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
// ==/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 addHighlightBtn() {
        if (document.getElementById('cbghelper_showhighlight')) {
            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_showhighlight';
        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 decimal = 2;
        let { fastest, heads, feet, fullspd_cnt } = acct_info.summary;
        fastest = JSON.parse(JSON.stringify(fastest)); // make a deep copy
        let wrapper = document.createElement('div');
        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 => `<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 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 = `<table width="100%">
            <tr> <td>位置</td> ${[1, 2, 3, 4, 5, 6].map(i => `<td>${i}</td>`)} <td>4(命中)</td> </tr>
            <tr> <td>散件</td> ${[1, 2, 3, 4, 5, 6, 7].map(i => `<td>${td_val(i, '散件')}</td>`)} </tr>
            <tr> <td>招财猫</td> ${[1, 2, 3, 4, 5, 6, 7].map(i => `<td>${td_val(i, '招财猫')}</td>`)} </tr>
            <tr> <td>火灵</td> ${[1, 2, 3, 4, 5, 6, 7].map(i => `<td>${td_val(i, '火灵')}</td>`)} </tr>
            <tr> <td>蚌精</td> ${[1, 2, 3, 4, 5, 6, 7].map(i => `<td>${td_val(i, '蚌精')}</td>`)} </tr>
        </table>`;
        spd.innerHTML = `<div><span class="data-name">头:</span> ${headStr} </div>
        <div><span class="data-name">脚:</span> ${feetStr} </div>
        <div><span class="data-name">散件一速:</span> <span class="data-value">${fastest_spd.toFixed(5)}</span></div>
        <div><span class="data-name">招财一速:</span> <span class="data-value">${zc_spd_val.toFixed(5)}</span></div>`
    
        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)
    }

    let checkExist = setInterval(function () {
        let addDownloadBtnWrapper = function () {
            if (document.getElementsByClassName('yuhun-list').length) {
                addDownloadBtn()
            }
        }
        let addHighlightBtnWrapper = function () {
            if (document.getElementsByClassName('highlight').length) {
                addHighlightBtn()
            }
        }
        let addHighlightViewWrapper = function () {
            if (document.getElementsByClassName('content-pvp').length && acct_info.ready) {
                addHighlightView();
            }
        }
        let checkfn_list = {
            'cbghelper_download': addDownloadBtnWrapper,
            'cbghelper_showhighlight': addHighlightBtnWrapper,
            'cbghelper_highlight': addHighlightViewWrapper
        };
        let handlers = {}

        for (let eid of Object.keys(checkfn_list)) {
            if (document.getElementById(eid)) {
                if (eid in handlers) {
                    clearInterval(handlers[eid])
                    delete handlers[eid]
                }
                continue;
            } else {
                handlers[eid] = setInterval(checkfn_list[eid], 200)
            }
        }
    }, 100);


    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;
    }
})();