TMVN Match Compare

Trophymanager: compare power of two clubs on: Age, ASI, Bank Price, Wage, RERECb, Routine and RatingR5 (include sharing routine base on position in match)

安裝腳本?
作者推薦腳本

您可能也會喜歡 TMVN League Lineup

安裝腳本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        TMVN Match Compare
// @version     7
// @description Trophymanager: compare power of two clubs on: Age, ASI, Bank Price, Wage, RERECb, Routine and RatingR5 (include sharing routine base on position in match)
// @namespace   https://trophymanager.com
// @match       https://trophymanager.com/matches/*
// ==/UserScript==

(function () {
    'use strict';

    const APP_COLOR = {
        LEVEL_1: "Darkred",
        LEVEL_2: "Black",
        LEVEL_3: "Orange",
        LEVEL_4: "Yellow",
        LEVEL_5: "Blue",
        LEVEL_6: "Aqua",
        LEVEL_7: "White"
    };

    const R5_CLASS = {
        LEVEL_1: 110,
        LEVEL_2: 100,
        LEVEL_3: 90,
        LEVEL_4: 80,
        LEVEL_5: 70,
        LEVEL_6: 60,
        LEVEL_7: 0
    };

    const share_bonus = 0.25;
    const routine_cap = 40.0;

    var matchUrl = location.href.split('/');
    var matchId,
    url;
    if (isNaN(matchUrl[4])) {
        matchId = matchUrl[5];
        url = 'https://trophymanager.com/ajax/match.ajax.php?id=nt' + matchId;
    } else {
        matchId = matchUrl[4];
        url = 'https://trophymanager.com/ajax/match.ajax.php?id=' + matchId;
    }

    var r5Map = new Map();
    var playerNameMap = new Map();
    var positionMap = new Map();
    var routineMap = new Map();
    var clubDatas = new Map();
    var xhr = new XMLHttpRequest();
    var homeClubId,
    awayClubId;

    xhr.open('GET', url, true);
    xhr.send();
    xhr.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
            let data = JSON.parse(this.responseText);

            homeClubId = data.club.home.id;
            awayClubId = data.club.away.id;

            let homeLineup = data.lineup.home;
            let awayLineup = data.lineup.away;
            let homePlayerIds = Object.getOwnPropertyNames(homeLineup);
            let awayPlayerIds = Object.getOwnPropertyNames(awayLineup);

            let homeD = [];
            let homeM = [];
            let homeF = [];
            let awayD = [];
            let awayM = [];
            let awayF = [];

            homePlayerIds.forEach((playerId, index) => {
                let position = homeLineup[playerId].position;
                if (position.indexOf("sub") > -1)
                    return;
                positionMap.set(playerId, position);
                routineMap.set(playerId, Number(homeLineup[playerId].routine));
                playerNameMap.set(playerId, homeLineup[playerId].no + '. ' + homeLineup[playerId].name);
                if (position == "gk" || position == "dl" || position == "dr" || position == "dc" || position == "dcl" || position == "dcr") {
                    homeD.push({
                        "ID": playerId,
                        "ROLE": position,
                        "ROU": Number(homeLineup[playerId].routine)
                    });
                } else if (position == "dml" || position == "dmr" || position == "dmc" || position == "dmcl" || position == "dmcr" || position == "ml" || position == "mr" || position == "mc" || position == "mcl" || position == "mcr") {
                    homeM.push({
                        "ID": playerId,
                        "ROLE": position,
                        "ROU": Number(homeLineup[playerId].routine)
                    });
                } else {
                    homeF.push({
                        "ID": playerId,
                        "ROLE": position,
                        "ROU": Number(homeLineup[playerId].routine)
                    });
                }
            });

            awayPlayerIds.forEach((playerId, index) => {
                let position = awayLineup[playerId].position;
                if (position.indexOf("sub") > -1)
                    return;
                positionMap.set(playerId, position);
                routineMap.set(playerId, Number(awayLineup[playerId].routine));
                playerNameMap.set(playerId, awayLineup[playerId].no + '. ' + awayLineup[playerId].name);
                if (position == "gk" || position == "dl" || position == "dr" || position == "dc" || position == "dcl" || position == "dcr") {
                    awayD.push({
                        "ID": playerId,
                        "ROLE": position,
                        "ROU": Number(awayLineup[playerId].routine)
                    });
                } else if (position == "dml" || position == "dmr" || position == "dmc" || position == "dmcl" || position == "dmcr" || position == "ml" || position == "mr" || position == "mc" || position == "mcl" || position == "mcr") {
                    awayM.push({
                        "ID": playerId,
                        "ROLE": position,
                        "ROU": Number(awayLineup[playerId].routine)
                    });
                } else {
                    awayF.push({
                        "ID": playerId,
                        "ROLE": position,
                        "ROU": Number(awayLineup[playerId].routine)
                    });
                }
            });

            let line_size;
            line_size = homeD.length;
            if (line_size > 1) {
                homeD.sort(compareByRoutineAsc);
                var min = homeD[0].ROU;
                if (min < routine_cap) {
                    var max = homeD[line_size - 1].ROU;
                    var min2 = homeD[1].ROU;
                    var bonus = max * share_bonus;
                    var new_routine = min + bonus;
                    new_routine = (new_routine < min2 ? new_routine : min2);
                    new_routine = (new_routine < routine_cap ? new_routine : routine_cap);
                    new_routine = parseFloat(new_routine.toFixed(1));
                    routineMap.set(homeD[0].ID, new_routine);
                }
            }
            line_size = homeM.length;
            if (line_size > 1) {
                homeM.sort(compareByRoutineAsc);
                min = homeM[0].ROU;
                if (min < routine_cap) {
                    max = homeM[line_size - 1].ROU;
                    min2 = homeM[1].ROU;
                    bonus = max * share_bonus;
                    new_routine = min + bonus;
                    new_routine = (new_routine < min2 ? new_routine : min2);
                    new_routine = (new_routine < routine_cap ? new_routine : routine_cap);
                    new_routine = parseFloat(new_routine.toFixed(1));
                    routineMap.set(homeM[0].ID, new_routine);
                }
            }
            line_size = homeF.length;
            if (line_size > 1) {
                homeF.sort(compareByRoutineAsc);
                min = homeF[0].ROU;
                if (min < routine_cap) {
                    max = homeF[line_size - 1].ROU;
                    min2 = homeF[1].ROU;
                    bonus = max * share_bonus;
                    new_routine = min + bonus;
                    new_routine = (new_routine < min2 ? new_routine : min2);
                    new_routine = (new_routine < routine_cap ? new_routine : routine_cap);
                    new_routine = parseFloat(new_routine.toFixed(1));
                    routineMap.set(homeF[0].ID, new_routine);
                }
            }
            line_size = awayD.length;
            if (line_size > 1) {
                awayD.sort(compareByRoutineAsc);
                min = awayD[0].ROU;
                if (min < routine_cap) {
                    max = awayD[line_size - 1].ROU;
                    min2 = awayD[1].ROU;
                    bonus = max * share_bonus;
                    new_routine = min + bonus;
                    new_routine = (new_routine < min2 ? new_routine : min2);
                    new_routine = (new_routine < routine_cap ? new_routine : routine_cap);
                    new_routine = parseFloat(new_routine.toFixed(1));
                    routineMap.set(awayD[0].ID, new_routine);
                }
            }
            line_size = awayM.length;
            if (line_size > 1) {
                awayM.sort(compareByRoutineAsc);
                min = awayM[0].ROU;
                if (min < routine_cap) {
                    max = awayM[line_size - 1].ROU;
                    min2 = awayM[1].ROU;
                    bonus = max * share_bonus;
                    new_routine = min + bonus;
                    new_routine = (new_routine < min2 ? new_routine : min2);
                    new_routine = (new_routine < routine_cap ? new_routine : routine_cap);
                    new_routine = parseFloat(new_routine.toFixed(1));
                    routineMap.set(awayM[0].ID, new_routine);
                }
            }
            line_size = awayF.length;
            if (line_size > 1) {
                awayF.sort(compareByRoutineAsc);
                min = awayF[0].ROU;
                if (min < routine_cap) {
                    max = awayF[line_size - 1].ROU;
                    min2 = awayF[1].ROU;
                    bonus = max * share_bonus;
                    new_routine = min + bonus;
                    new_routine = (new_routine < min2 ? new_routine : min2);
                    new_routine = (new_routine < routine_cap ? new_routine : routine_cap);
                    new_routine = parseFloat(new_routine.toFixed(1));
                    routineMap.set(awayF[0].ID, new_routine);
                }
            }

            statistic(homePlayerIds, homeLineup).then((clubStatistic) => {
                clubDatas.set(homeClubId, {
                    "XP": clubStatistic.XP,
                    "Age": clubStatistic.Age,
                    "ASI": clubStatistic.ASI,
                    "Wage": clubStatistic.Wage,
                    "BP": clubStatistic.BP,
                    "REC": clubStatistic.REC,
                    "R5": clubStatistic.R5
                });
            }).catch((error) => {});

            statistic(awayPlayerIds, awayLineup).then((clubStatistic) => {
                clubDatas.set(awayClubId, {
                    "XP": clubStatistic.XP,
                    "Age": clubStatistic.Age,
                    "ASI": clubStatistic.ASI,
                    "Wage": clubStatistic.Wage,
                    "BP": clubStatistic.BP,
                    "REC": clubStatistic.REC,
                    "R5": clubStatistic.R5
                });
            }).catch((error) => {});
        }
    };

    var displayInterval = setInterval(display, 1000);
    var updateNameInterval = setInterval(updateName, 100);
    var showInfoInterval = setInterval(showInfo, 1000);

    var newdiv;
    var pause = false;

    function showInfo() {
        if (clubDatas.size < 2) {
            return;
        }
        clearInterval(showInfoInterval);

        newdiv = document.createElement("div");
        newdiv.innerHTML =
            "<br><div id=\"mystarbox\" class=\"home color\" style=\"background-color:rgb(127,127,127)\"><b style=\"color: gold;\">" + "REC:" + (clubDatas.get(homeClubId).REC / 11).toFixed(4) + " R5:" + (clubDatas.get(homeClubId).R5 / 11).toFixed(2) + "</b></div><div class=\"away color\"  style=\"background-color:rgb(10,5,76)\"><b style=\"color: gold;\">" + "REC:" + (clubDatas.get(awayClubId).REC / 11).toFixed(4) + " R5:" + (clubDatas.get(awayClubId).R5 / 11).toFixed(2) + "</b></div>" +

            "<br><div id=\"myxp\" class=\"home color\" style=\"background-color:rgb(127,127,127)\"><b style=\"color: gold;\">" + "XP:" + (clubDatas.get(homeClubId).XP / 11).toFixed(1) + " Age:" + (clubDatas.get(homeClubId).Age / 11 / 12).toFixed(1) + " ASI:" + (clubDatas.get(homeClubId).ASI / 11).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + "</b></div><div class=\"away color\"  style=\"background-color:rgb(10,5,76)\"><b style=\"color: gold;\">" + "XP:" + (clubDatas.get(awayClubId).XP / 11).toFixed(1) + " Age:" + (clubDatas.get(awayClubId).Age / 11 / 12).toFixed(1) + " ASI:" + (clubDatas.get(awayClubId).ASI / 11).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + "</b></div>" +

            "<br><div id=\"myxp\" class=\"home color\" style=\"background-color:rgb(127,127,127)\"><b style=\"color: gold;\">" + "BP:" + (clubDatas.get(homeClubId).BP / 11 / 1000).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " Wage:" + (clubDatas.get(homeClubId).Wage / 11 / 1000).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + "</b></div><div class=\"away color\"  style=\"background-color:rgb(10,5,76)\"><b style=\"color: gold;\">" + "BP:" + (clubDatas.get(awayClubId).BP / 11 / 1000).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " Wage:" + (clubDatas.get(awayClubId).Wage / 11 / 1000).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + "</b></div>";

        r5Map.forEach((value, key) => {
            $('div .quarter a[href$="/' + key + '"] .icons').text(value);
            if (value >= R5_CLASS.LEVEL_1) {
                $('div .quarter a[href$="/' + key + '"] .icons').css('color', APP_COLOR.LEVEL_1);
            } else if (value >= R5_CLASS.LEVEL_2) {
                $('div .quarter a[href$="/' + key + '"] .icons').css('color', APP_COLOR.LEVEL_2);
            } else if (value >= R5_CLASS.LEVEL_3) {
                $('div .quarter a[href$="/' + key + '"] .icons').css('color', APP_COLOR.LEVEL_3);
            } else if (value >= R5_CLASS.LEVEL_4) {
                $('div .quarter a[href$="/' + key + '"] .icons').css('color', APP_COLOR.LEVEL_4);
            } else if (value >= R5_CLASS.LEVEL_5) {
                $('div .quarter a[href$="/' + key + '"] .icons').css('color', APP_COLOR.LEVEL_5);
            } else if (value >= R5_CLASS.LEVEL_6) {
                $('div .quarter a[href$="/' + key + '"] .icons').css('color', APP_COLOR.LEVEL_6);
            } else {
                $('div .quarter a[href$="/' + key + '"] .icons').css('color', APP_COLOR.LEVEL_7);
            }
        });
    }

    function display() {
        if (newdiv !== undefined && $('.nameplate').length > 0) {
            if ($('.post_report').length > 0) {
                document.getElementsByClassName("nameplate")[0].appendChild(newdiv);
                clearInterval(displayInterval);
            } else if (!pause) {
                document.getElementsByClassName("nameplate")[0].appendChild(newdiv);
                pause = true;
            }
        }
    }

    function updateName() {
        if (playerNameMap.size < 22) {
            return;
        }
        playerNameMap.forEach((value, key) => {
            $('div .quarter a[href$="/' + key + '"] .name').text(value);
        });
        if ($('.post_report').length > 0) {
            clearInterval(updateNameInterval);
        }
    }

    function getPlayerData(playerId) {
        return new Promise((resolve, reject) => {
            $.post("/ajax/tooltip.ajax.php", {
                "player_id": playerId
            }).done((responseText) => {
                var data = JSON.parse(responseText);

                if (routineMap.has(playerId)) {
                    data.player.routine = routineMap.get(playerId).toString();
                }
                if (positionMap.has(playerId)) {
                    data.player.favposition = positionMap.get(playerId);
                }

                var rrValue = calculateRR(data.player);
                var rec;
                if (rrValue[0].length == 2) {
                    rec = Number(rrValue[0][0]) >= Number(rrValue[0][1]) ? Number(rrValue[0][0]) : Number(rrValue[0][1]);
                } else {
                    rec = Number(rrValue[0][0]);
                }
                var r5;
                if (rrValue[1].length == 2) {
                    r5 = Number(rrValue[1][0]) >= Number(rrValue[1][1]) ? Number(rrValue[1][0]) : Number(rrValue[1][1]);
                } else {
                    r5 = Number(rrValue[1][0]);
                }
                r5Map.set(playerId, r5);

                resolve({
                    XP: Number(data.player.routine.split(',').join('')),
                    Age: Number(data.player.age) * 12 + Number(data.player.months),
                    ASI: Number(data.player.skill_index.split(',').join('')),
                    Wage: Number(data.player.wage.replace("<span class='coin'>", "").replace("<\/span>", "").split(',').join('')),
                    BP: BP.compute(Number(data.player.skill_index.split(',').join('')), Number(data.player.age), Number(data.player.months), data.player.fp),
                    REC: rec,
                    R5: r5
                });
            }).fail((error) => {
                reject(error);
            });
        });
    }

    let BP = {
        compute: function (asi, age, month, position) {
            let pow = Math.pow;
            if (position === "GK") {
                return Math.round((asi * 500 * pow((300 / (age * 12 + month)), 2.5)) * 0.75);
            } else {
                return Math.round(asi * 500 * pow((300 / (age * 12 + month)), 2.5));
            }
        }
    };

    function statistic(playerIds, lineup) {
        return new Promise((resolve, reject) => {
            let clubStatistic = {
                XP: 0,
                Age: 0,
                ASI: 0,
                Wage: 0,
                BP: 0,
                REC: 0,
                R5: 0
            };
            let countLoop = 0;
            let countAjaxCall = 0;
            let countAjaxAnswer = 0;

            playerIds.forEach((playerId, index) => {
                if (lineup[playerId].position.indexOf("sub") < 0) {
                    countAjaxCall++;
                    countLoop++; //guarantee plus ajaxcall before
                    getPlayerData(playerId).then((player) => {
                        clubStatistic.XP += player.XP;
                        clubStatistic.Age += player.Age;
                        clubStatistic.ASI += player.ASI;
                        clubStatistic.Wage += player.Wage;
                        clubStatistic.BP += player.BP;
                        clubStatistic.REC += player.REC;
                        clubStatistic.R5 += player.R5;
                        countAjaxAnswer++;
                    }).catch((error) => {});
                } else {
                    countLoop++
                }
            });

            function check() {
                if (!(countLoop == playerIds.length && countAjaxCall == countAjaxAnswer)) {
                    setTimeout(check, 100);
                } else {
                    resolve(clubStatistic);
                };
            }

            check();
        });
    }

    // R5 weights
    //		Str				Sta				Pac				Mar				Tac				Wor				Pos				Pas				Cro				Tec				Hea				Fin				Lon				Set
    var weightR5 = [[0.41029304, 0.18048062, 0.56730138, 1.06344654, 1.02312672, 0.40831256, 0.58235457, 0.12717479, 0.05454137, 0.09089830, 0.42381693, 0.04626272, 0.02199046, 0.00000000], // DC
        [0.42126371, 0.18293193, 0.60567629, 0.91904794, 0.89070915, 0.40038476, 0.56146633, 0.15053902, 0.15955429, 0.15682932, 0.42109742, 0.09460329, 0.03589655, 0.00000000], // DL/R
        [0.23412419, 0.32032289, 0.62194779, 0.63162534, 0.63143081, 0.45218831, 0.47370658, 0.55054737, 0.17744915, 0.39932519, 0.26915814, 0.16413124, 0.07404301, 0.00000000], // DMC
        [0.27276905, 0.26814289, 0.61104798, 0.39865092, 0.42862643, 0.43582015, 0.46617076, 0.44931076, 0.25175412, 0.46446692, 0.29986350, 0.43843061, 0.21494592, 0.00000000], // DML/R
        [0.25219260, 0.25112993, 0.56090649, 0.18230261, 0.18376490, 0.45928749, 0.53498118, 0.59461481, 0.09851189, 0.61601950, 0.31243959, 0.65402884, 0.29982016, 0.00000000], // MC
        [0.28155678, 0.24090675, 0.60680245, 0.19068879, 0.20018012, 0.45148647, 0.48230007, 0.42982389, 0.26268609, 0.57933805, 0.31712419, 0.65824985, 0.29885649, 0.00000000], // ML/R
        [0.22029884, 0.29229690, 0.63248227, 0.09904394, 0.10043602, 0.47469498, 0.52919791, 0.77555880, 0.10531819, 0.71048302, 0.27667115, 0.56813972, 0.21537826, 0.00000000], // OMC
        [0.21151292, 0.35804710, 0.88688492, 0.14391236, 0.13769621, 0.46586605, 0.34446036, 0.51377701, 0.59723919, 0.75126119, 0.16550722, 0.29966502, 0.12417045, 0.00000000], // OML/R
        [0.35479780, 0.14887553, 0.43273380, 0.00023928, 0.00021111, 0.46931131, 0.57731335, 0.41686333, 0.05607604, 0.62121195, 0.45370457, 1.03660702, 0.43205492, 0.00000000], // F
        [0.45462811, 0.30278232, 0.45462811, 0.90925623, 0.45462811, 0.90925623, 0.45462811, 0.45462811, 0.30278232, 0.15139116, 0.15139116]]; // GK

    // RECb weights		Str				Sta				Pac				Mar				Tac				Wor				Pos				Pas				Cro				Tec				Hea				Fin				Lon				Set
    var weightRb = [[0.10493615, 0.05208547, 0.07934211, 0.14448971, 0.13159554, 0.06553072, 0.07778375, 0.06669303, 0.05158306, 0.02753168, 0.12055170, 0.01350989, 0.02549169, 0.03887550], // DC
        [0.07715535, 0.04943315, 0.11627229, 0.11638685, 0.12893778, 0.07747251, 0.06370799, 0.03830611, 0.10361093, 0.06253997, 0.09128094, 0.01314110, 0.02449199, 0.03726305], // DL/R
        [0.08219824, 0.08668831, 0.07434242, 0.09661001, 0.08894242, 0.08998026, 0.09281287, 0.08868309, 0.04753574, 0.06042619, 0.05396986, 0.05059984, 0.05660203, 0.03060871], // DMC
        [0.06744248, 0.06641401, 0.09977251, 0.08253749, 0.09709316, 0.09241026, 0.08513703, 0.06127851, 0.10275520, 0.07985941, 0.04618960, 0.03927270, 0.05285911, 0.02697852], // DML/R
        [0.07304213, 0.08174111, 0.07248656, 0.08482334, 0.07078726, 0.09568392, 0.09464529, 0.09580381, 0.04746231, 0.07093008, 0.04595281, 0.05955544, 0.07161249, 0.03547345], // MC
        [0.06527363, 0.06410270, 0.09701305, 0.07406706, 0.08563595, 0.09648566, 0.08651209, 0.06357183, 0.10819222, 0.07386495, 0.03245554, 0.05430668, 0.06572005, 0.03279859], // ML/R
        [0.07842736, 0.07744888, 0.07201150, 0.06734457, 0.05002348, 0.08350204, 0.08207655, 0.11181914, 0.03756112, 0.07486004, 0.06533972, 0.07457344, 0.09781475, 0.02719742], // OMC
        [0.06545375, 0.06145378, 0.10503536, 0.06421508, 0.07627526, 0.09232981, 0.07763931, 0.07001035, 0.11307331, 0.07298351, 0.04248486, 0.06462713, 0.07038293, 0.02403557], // OML/R
        [0.07738289, 0.05022488, 0.07790481, 0.01356516, 0.01038191, 0.06495444, 0.07721954, 0.07701905, 0.02680715, 0.07759692, 0.12701687, 0.15378395, 0.12808992, 0.03805251], // F
        [0.07466384, 0.07466384, 0.07466384, 0.14932769, 0.10452938, 0.14932769, 0.10452938, 0.10344411, 0.07512610, 0.04492581, 0.04479831]]; // GK

    var posNames = ["DC", "DCL", "DCR", "DL", "DR", "DMC", "DMCL", "DMCR", "DML", "DMR", "MC", "MCL", "MCR", "ML", "MR", "OMC", "OMCL", "OMCR", "OML", "OMR", "F", "FC", "FCL", "FCR", "GK"];
    var pos = [0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9];

    function funFix1(i) {
        i = (Math.round(i * 10) / 10).toFixed(1);
        return i;
    }

    function funFix2(i) {
        i = (Math.round(i * 100) / 100).toFixed(2);
        return i;
    }

    function funFix3(i) {
        i = (Math.round(i * 1000) / 1000).toFixed(3);
        return i;
    }

    function calculate(weightRb, weightR5, skills, posGain, posKeep, fp, rou, remainder, allBonus) {
        var rec = 0; // RERECb
        var ratingR = 0; // RatingR5
        var ratingR5 = 0; // RatingR5 + routine
        var ratingR5Bonus = 0; // RatingR5 + routine + bonus
        var remainderWeight = 0; // REREC remainder weight sum
        var remainderWeight2 = 0; // RatingR5 remainder weight sum
        var not20 = 0; // 20以外のスキル数
        for (var i = 0; i < weightRb[fp].length; i++) { // weightR[fp].length = n.pesi[pos] cioè le skill: 14 o 11
            rec += skills[i] * weightRb[fp][i];
            ratingR += skills[i] * weightR5[fp][i];
            if (skills[i] != 20) {
                remainderWeight += weightRb[fp][i];
                remainderWeight2 += weightR5[fp][i];
                not20++;
            }
        }
        if (remainder / not20 > 0.9 || not20 == 0) {
            if (fp == 9)
                not20 = 11;
            else
                not20 = 14;
            remainderWeight = 1;
            remainderWeight2 = 5;
        }
        rec = funFix3((rec + remainder * remainderWeight / not20 - 2) / 3);
        ratingR += remainder * remainderWeight2 / not20;
        ratingR5 = funFix2(ratingR * 1 + rou * 5);

        if (skills.length == 11) {
            ratingR5Bonus = funFix2(ratingR5 * 1 + allBonus * 1);
        } else {
            ratingR5Bonus = funFix2(ratingR5 * 1 + allBonus * 1 + posGain[fp] * 1 + posKeep[fp] * 1);
        }
        return [rec, ratingR5Bonus];
    }

    function calculateRR(current_player_info) {
        var skillArray = current_player_info.skills;
        var STR,
        STA,
        PAC,
        MAR,
        TAC,
        WOR,
        POS,
        PAS,
        CRO,
        TEC,
        HEA,
        FIN,
        LON,
        SET,
        HAN,
        ONE,
        REF,
        AER,
        JUM,
        COM,
        KIC,
        THR;
        var skillValue;
        for (var i = 0; i < skillArray.length; i++) {
            if (skillArray[i].key == 'null')
                continue;
            skillValue = skillArray[i].value;
            if (isNaN(skillValue)) {
                if (skillValue.indexOf('19') != -1) {
                    skillValue = 19;
                } else if (skillValue.indexOf('20') != -1) {
                    skillValue = 20;
                } else {
                    throw 'Error skillValue: ' + skillValue;
                }
            }

            switch (skillArray[i].key) {
            case 'strength':
                STR = skillValue;
                break;
            case 'stamina':
                STA = skillValue;
                break;
            case 'pace':
                PAC = skillValue;
                break;
            case 'marking':
                MAR = skillValue;
                break;
            case 'tackling':
                TAC = skillValue;
                break;
            case 'workrate':
                WOR = skillValue;
                break;
            case 'positioning':
                POS = skillValue;
                break;
            case 'passing':
                PAS = skillValue;
                break;
            case 'crossing':
                CRO = skillValue;
                break;
            case 'technique':
                TEC = skillValue;
                break;
            case 'heading':
                HEA = skillValue;
                break;
            case 'finishing':
                FIN = skillValue;
                break;
            case 'longshots':
                LON = skillValue;
                break;
            case 'set_pieces':
                SET = skillValue;
                break;
            case 'handling':
                HAN = skillValue;
                break;
            case 'one_on_ones':
                ONE = skillValue;
                break;
            case 'reflexes':
                REF = skillValue;
                break;
            case 'aerial_ability':
                AER = skillValue;
                break;
            case 'jumping':
                JUM = skillValue;
                break;
            case 'communication':
                COM = skillValue;
                break;
            case 'kicking':
                KIC = skillValue;
                break;
            case 'throwing':
                THR = skillValue;
                break;
            default:
                throw 'Error skillArray[i].key: ' + skillArray[i].key;
            }
        }

        var ROLE = current_player_info.favposition.toUpperCase();
        var ROU = Number(current_player_info.routine.split(',').join(''));
        var ASI = Number(current_player_info.skill_index.split(',').join(''));

        var ROLE1,
        ROLE2;
        var role = ROLE.split(',');
        if (role.length == 2) {
            ROLE1 = role[0];
            ROLE2 = role[1];
        } else {
            ROLE1 = role[0];
            ROLE2 = -1;
        }

        var fp,
        fp2 = -1;
        for (var i = 0; i < posNames.length; i++) {
            if (posNames[i] == ROLE1)
                fp = pos[i];
            if (ROLE2 != -1 && posNames[i] == ROLE2)
                fp2 = pos[i];
        }
        if (fp == 9) {
            var weight = 48717927500;
            var skills = [STR, STA, PAC, HAN, ONE, REF, AER, JUM, COM, KIC, THR];
        } else {
            weight = 263533760000;
            skills = [STR, STA, PAC, MAR, TAC, WOR, POS, PAS, CRO, TEC, HEA, FIN, LON, SET];
        }

        var goldstar = 0;
        var skillSum = 0;
        var skillsB = [];
        for (i = 0; i < skills.length; i++) {
            skillSum += parseInt(skills[i]);
        }
        var remainder = Math.round((Math.pow(2, Math.log(weight * ASI) / Math.log(Math.pow(2, 7))) - skillSum) * 10) / 10; // RatingR5 remainder
        for (var j = 0; j < 2; j++) {
            for (i = 0; i < 14; i++) {
                if (j == 0 && skills[i] == 20)
                    goldstar++;
                if (j == 1) {
                    if (skills[i] != 20)
                        skillsB[i] = skills[i] * 1 + remainder / (14 - goldstar);
                    else
                        skillsB[i] = skills[i];
                }
            }
        }

        var routine = (3 / 100) * (100 - (100) * Math.pow(Math.E, -ROU * 0.035));
        var strRou = skillsB[0] * 1 + routine;
        var staRou = skillsB[1] * 1;
        var pacRou = skillsB[2] * 1 + routine;
        var marRou = skillsB[3] * 1 + routine;
        var tacRou = skillsB[4] * 1 + routine;
        var worRou = skillsB[5] * 1 + routine;
        var posRou = skillsB[6] * 1 + routine;
        var pasRou = skillsB[7] * 1 + routine;
        var croRou = skillsB[8] * 1 + routine;
        var tecRou = skillsB[9] * 1 + routine;
        var heaRou = skillsB[10] * 1 + routine;
        var finRou = skillsB[11] * 1 + routine;
        var lonRou = skillsB[12] * 1 + routine;
        var setRou = skillsB[13] * 1 + routine;

        var headerBonus;
        if (heaRou > 12)
            headerBonus = funFix2((Math.pow(Math.E, (heaRou - 10) ** 3 / 1584.77) - 1) * 0.8 + Math.pow(Math.E, (strRou * strRou * 0.007) / 8.73021) * 0.15 + Math.pow(Math.E, (posRou * posRou * 0.007) / 8.73021) * 0.05);
        else
            headerBonus = 0;

        var fkBonus = funFix2(Math.pow(Math.E, Math.pow(setRou + lonRou + tecRou * 0.5, 2) * 0.002) / 327.92526);
        var ckBonus = funFix2(Math.pow(Math.E, Math.pow(setRou + croRou + tecRou * 0.5, 2) * 0.002) / 983.65770);
        var pkBonus = funFix2(Math.pow(Math.E, Math.pow(setRou + finRou + tecRou * 0.5, 2) * 0.002) / 1967.31409);

        var allBonus = 0;
        if (skills.length == 11)
            allBonus = 0;
        else
            allBonus = headerBonus * 1 + fkBonus * 1 + ckBonus * 1 + pkBonus * 1;

        var gainBase = funFix2((strRou ** 2 + staRou ** 2 * 0.5 + pacRou ** 2 * 0.5 + marRou ** 2 + tacRou ** 2 + worRou ** 2 + posRou ** 2) / 6 / 22.9 ** 2);
        var keepBase = funFix2((strRou ** 2 * 0.5 + staRou ** 2 * 0.5 + pacRou ** 2 + marRou ** 2 + tacRou ** 2 + worRou ** 2 + posRou ** 2) / 6 / 22.9 ** 2);
        //	0:DC			1:DL/R			2:DMC			3:DML/R			4:MC			5:ML/R			6:OMC			7:OML/R			8:F
        var posGain = [gainBase * 0.3, gainBase * 0.3, gainBase * 0.9, gainBase * 0.6, gainBase * 1.5, gainBase * 0.9, gainBase * 0.9, gainBase * 0.6, gainBase * 0.3];
        var posKeep = [keepBase * 0.3, keepBase * 0.3, keepBase * 0.9, keepBase * 0.6, keepBase * 1.5, keepBase * 0.9, keepBase * 0.9, keepBase * 0.6, keepBase * 0.3];

        var valueFp = calculate(weightRb, weightR5, skills, posGain, posKeep, fp, routine, remainder, allBonus);
        var rec = [valueFp[0]];
        var r5 = [valueFp[1]];

        if (fp2 != -1 && fp2 != fp) {
            var valueFp2 = calculate(weightRb, weightR5, skills, posGain, posKeep, fp2, routine, remainder, allBonus);
            rec.push(valueFp2[0]);
            r5.push(valueFp2[1]);
        }

        return [rec, r5];
    }

    function compareByRoutineAsc(a, b) {
        var rou_a = parseFloat(a.ROU);
        var rou_b = parseFloat(b.ROU);
        return (rou_a - rou_b);
    }
})();