Greasy Fork

Greasy Fork is available in English.

CBG Helper

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

目前为 2020-07-01 提交的版本。查看 最新版本

// ==UserScript==
// @name         CBG Helper
// @namespace    https://yys.zhebu.work/
// @version      0.0.5
// @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 addDownloadBtn() {
        if(document.getElementById('cbghelper_download')) {
            return;
        }
        var b = document.createElement('a');
        b.innerText = "(💾保存为JSON)";
        b.onclick = function () {
            console.log("To save data!");
            saveToJsonHelper();
        }
        b.id = "cbghelper_download"
        b.style.cursor = "pointer";
        var yuhun_list = document.getElementsByClassName('yuhun-list')[0];
        yuhun_list.parentNode.childNodes[1].append(b)
    }

    var checkExist = setInterval(function () {
        if (!document.getElementById('cbghelper_download')) {
            var ready = setInterval(function () {
                if (document.getElementsByClassName('yuhun-list').length) {
                    console.log("Exists!");
                    clearInterval(ready);
                    addDownloadBtn()
                }
            }, 100)
            }
    }, 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;

        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) {
                attrs[propName].add_val = getPropValue(equips, mitama_list, propName).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 += 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);
    }
})();