Pendoria - Chance to die

Implements some extra information per finished fight in the battle view on pendoria.

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Pendoria - Chance to die
// @description  Implements some extra information per finished fight in the battle view on pendoria.
// @namespace    http://pendoria.net/
// @version      0.0.33
// @author       Xortrox
// @contributor  Tester: euphone
// @contributor  Tester: fourkade
// @contributor  Coder: Kidel
// @match        http://pendoria.net/game
// @match        https://pendoria.net/game
// @match        http://www.pendoria.net/game
// @match        https://www.pendoria.net/game
// @grant        none
// ==/UserScript==

(function () {
    function showChanceToDie(data) {
        let playerHealth = $('#php-value')[0];

        if (!playerHealth) {
            return console.log('playerHealth element was not found.');
        }

        let fightDiv = $('#fight')[0];

        if (!fightDiv) {
            return console.log('fightDiv element was not found.');
        }

        let playerHPBackground = $('#php-background')[0];

        if (!playerHPBackground) {
            return console.log('playerHPBackground element was not found.');
        }

        function createHistoricallyLowestHealthTrackerIfNotExists() {
            let historicallyLowestHPTracker = $('#ctdInfoHistoriallyLowestDiv')[0];

            if(!historicallyLowestHPTracker) {
                let infoDiv = document.createElement('div');
                infoDiv.id = 'ctdInfoHistoriallyLowestDiv';
                playerHPBackground.parentElement.prepend(infoDiv);
            }

            return historicallyLowestHPTracker;
        }

        function createInfoDivIfNotExists() {
            let ctdInfoDiv = $('#ctdInfoDiv')[0];

            if (!ctdInfoDiv) {
                let infoDiv = document.createElement('div');
                infoDiv.id = 'ctdInfoDiv';
                fightDiv.parentElement.appendChild(infoDiv);
            }

            return ctdInfoDiv;
        }

        let enemyHitChanceFactor = data.monsterChanceToHit / 100.0;

        let playerMaxHealth = data.playerMaxLife;
        let enemyAverageDamage = data.damageTaken / data.hitsTaken;

        let enemyHitsToKill = Math.ceil(playerMaxHealth / enemyAverageDamage);

        let playerChanceFactorDeath = enemyHitChanceFactor ** enemyHitsToKill;

        let playerHitChanceFactor = data.playerChanceToHit / 100.0;


        let playerAverageDamage = data.damageDone / data.timesHit;

        let enemyMaxHealth = data.monsterMaxLife;

        let enemyMaxHealthWithChanceFactor = (enemyMaxHealth / playerHitChanceFactor);
        let playerHitsToKillEnemy = Math.ceil(enemyMaxHealthWithChanceFactor / playerAverageDamage);
        let playerHitsToKillEnemyNoChanceFactor = Math.ceil(enemyMaxHealth / playerAverageDamage);

        // console.log('enemyHitsToKill:', enemyHitsToKill);
        // console.log('playerHitChanceFactor:', playerHitChanceFactor);
        // console.log('(1 - (1 - playerHitChanceFactor) ** enemyHitsToKill) * 100');
        // let playerWinChance = (1 - (1 - playerHitChanceFactor) ** playerHitsToKillEnemyNoChanceFactor) * 100

        let enemyChanceFactorDeath = playerHitChanceFactor ** playerHitsToKillEnemy;

        let likelinessPrediction = ``;

        // TODO: Make this based on some amount of % within proximity of max healths and damages.
        if (playerHitsToKillEnemy < enemyHitsToKill) {
            likelinessPrediction +=
                `It seems <b style="color:lightgreen">unlikely</b> that you will die as the enemy is required to hit you ${enemyHitsToKill} times, but you need to hit it roughly ${playerHitsToKillEnemy} times.<br>
                <br>
                `;
        } else if (playerHitsToKillEnemy >= enemyHitsToKill){
            likelinessPrediction +=
                `It seems <b style="color:red">likely</b> that you will die as the enemy needs to hit you ${enemyHitsToKill} times, but you need to hit it roughly ${playerHitsToKillEnemy} times.<br>
                <br>
                `;
        }

        let ctdInfoDiv = createInfoDivIfNotExists();

        let diffHits = enemyHitsToKill - playerHitsToKillEnemy;

        if (ctdInfoDiv){
            ctdInfoDiv.innerHTML =
                `
                <div style="border-bottom: 1px solid white; margin-bottom: 3px;">
                  Chance to die<button id="ctdClearHistoricalTracker" style="padding-top: 0; padding-bottom: 0; height:21px; border: 1px solid white; background: transparent; border-radius: 0; float:right; line-height: 21px;">Reset lowest health tracker</button>
                </div>
                ${likelinessPrediction}
                You would die after <b style="color:red;">${enemyHitsToKill}</b>~ rounds of battle if the enemy is still alive.<br>
                You would win after <b style="color:lightgreen;">${playerHitsToKillEnemy}</b>~ rounds of battle.<br>
                <br>
                You have a chance of ${(((1 - playerHitChanceFactor) ** diffHits) * 100).toPrecision(3)}%~ to miss enough in order to receive ${enemyHitsToKill} hits from this enemy, which would kill you.<br>
                `
        }

        /**
         * Bind click event of ctdClearHistoricalTracker
         * */
        $('#ctdClearHistoricalTracker').click(() => {
            let pendCtdSettings = window.localStorage.getItem('PendoriaChanceToDie');
            if (pendCtdSettings) {
               pendCtdSettings = JSON.parse(pendCtdSettings);
            }

            delete(pendCtdSettings.historiallyLowestHealth);

            localStorage.setItem('PendoriaChanceToDie', JSON.stringify(pendCtdSettings));
        });

        playerCurrentHealth = data.playerLife;
        if (playerCurrentHealth < 0) {
            playerCurrentHealth = 0;
        }

        // console.log('playerCurrentHealth:', playerCurrentHealth);

        let ctdInfoHistoriallyLowestDiv = createHistoricallyLowestHealthTrackerIfNotExists();

        // TODO: Also show how many hits you took and for what average damage the hits were, in the tooltip
        if(ctdInfoHistoriallyLowestDiv) {
            let pendCtdSettings = window.localStorage.getItem('PendoriaChanceToDie');
            if (!pendCtdSettings) {
                pendCtdSettings = {
                    historiallyLowestHealth: playerCurrentHealth
                };

                localStorage.setItem('PendoriaChanceToDie', JSON.stringify(pendCtdSettings));
            } else {
                pendCtdSettings = JSON.parse(pendCtdSettings);

                if (pendCtdSettings.historiallyLowestHealth === undefined) {
                    pendCtdSettings.historiallyLowestHealth = playerCurrentHealth
                    localStorage.setItem('PendoriaChanceToDie', JSON.stringify(pendCtdSettings));
                }
            }

            if (playerCurrentHealth < pendCtdSettings.historiallyLowestHealth) {
                pendCtdSettings.historiallyLowestHealth = playerCurrentHealth;
                localStorage.setItem('PendoriaChanceToDie', JSON.stringify(pendCtdSettings))
            }

            let playerHealthPercentage = (pendCtdSettings.historiallyLowestHealth / playerMaxHealth * 100);

            /**
             * Only update qtip if the contents changed.
             **/
            if (ctdInfoHistoriallyLowestDiv.innerText !== commarize(pendCtdSettings.historiallyLowestHealth)) {
                setTimeout(() => {
                    $('#php-value').qtip({
                        style: {
                            classes: 'qtip-dark'
                        },
                        position: {
                            target: $('#ctdInfoHistoriallyLowestDiv'),
                            at: 'bottom left'
                        },
                        content: {
                            text: pendCtdSettings.historiallyLowestHealth,
                            // title: 'Historically lowest health'
                        }
                    });
                })
            }

            ctdInfoHistoriallyLowestDiv.innerText = commarize(pendCtdSettings.historiallyLowestHealth);
            ctdInfoHistoriallyLowestDiv.style =
                `
                z-index: 9999;
                position: absolute;
                background-color: #14723c;
                height: 25px;
                line-height: 26px;
                padding-left: 5px;
                border-radius: 10px;
                font-size: 12px;
                width: ${playerHealthPercentage}%;
                `;

            playerHealth.style.zIndex = '9999';
        }
    }

    $(function () {
        function load() {
            if(window.socket) {
                socket.on('battle data', (data) => {
                    showChanceToDie(data);
                });
            } else {
                setTimeout(load, 500);
            }
        }
        load();
    });


    /**
     * Appends a suffix to the number and shortens it generally.
     * */
    function commarize(number) {
        if(number < 999999) { return number; }
        var sizes = [
            '',
            '',
            'Mill',
            'Bill',
            'Trill',
            'Quad',
            'Quint',
            'Sext',
            'Sept',
            'Oct'
        ];
        if (number == 0) return '0 Byte';
        var i = parseInt(Math.floor(Math.log(number) / Math.log(1000)));
        return (number / Math.pow(1000, i)).toPrecision(4) + ' ' + sizes[i];
    }

    /**
     * Shows the full precision of a number up to 100 digits.
     * */
    function fullZeroes(number) {
        return number.toFixed(100).replace(/.?0+$/,"")     // 0.0000005
    }

    /**
     * Takes a number string
     * */
    function fourLastDigits(numberString) {
        let index = 0;
        let theValAfterDot = numberString.substr(numberString.indexOf('.') + 1);

        while(theValAfterDot[index] === '0'){
            index++;
        }
        return '0.' + theValAfterDot.substr(0, index + 3)
    }
}());