Crystal Check

Combine dropped crystals after hunting

目前為 2014-08-24 提交的版本,檢視 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name            Crystal Check
// @match           http://dev.legacy-game.net/hunting3.php
// @match           http://*.legacy-game.net/inventory.php*
// @version         1.1
// @namespace       https://greasyfork.org/users/4585
// @description     Combine dropped crystals after hunting
// @grant           none
/*global $ */
// ==/UserScript==


/*****************************************************
                    General Methods                    
/****************************************************/
//Method to create elements on the fly without injecting html
function create(type, attr, text, parent) {
    var ele = document.createElement(type);
    for (var x in attr) {
        if (x.hasOwnProperty) {
            ele.setAttribute(x, attr[x]);
        }
    }
    if (text) {
        ele.innerHTML = text;
    }
    if (parent) {
        parent.appendChild(ele);
    }
    return ele;
}
//Return user cookies as object
function getCookies() {
    return document.cookie
        .split(/[;\s]+/g)
        .reduce(function(a, b) {
            a[b.split('=')[0]] = b.split('=')[1];
            return a;
        }, {});
}
/*****************************************************
                    Inventory                    
/****************************************************/

//Inventory API to manipulate inventory items
function Inventory(html) {
    this.items = getItems();
    this.key = $(html).find('form').attr('action').match(/[a-zA-Z]+$/g).pop();
    //Inventory items encapsulated to organise data
    function Item(name, slot, trades, id) {
        this.name = name;
        this.slot = slot;
        this.trades = trades;
        this.id = id;

        //Crystal merging method
        this.merge = function(that, callback) {
            var slot1 = this.slot;
            var slot2 = that.slot;
            $.ajax({
                type: "POST",
                url: "inventory10.php?i=" + slot1 + "&key=" + this.key,
                async: false,
                data: {
                    item: slot2
                },
                success: function(data) {
                    //If method available, use on response data to check for further crystal merging
                    //Response from merging should be the redirected inventory page, which can be used to
                    //refresh inventory data
                    if (callback)
                        callback.call(this, data);
                }
            });
        };
    }
    //captures all item data for the inventory & encapsulates as an object
    function getItems() {
        //Match javascript lines on inventory.php for item id & trades
        //Each resulting index corresponds to the slot in inventory
        var itemPreviews = html.match(/(itemPreviews\[)((.|\n)*?)(Default|table>');/g);
        var tempItems = [];
        $(html).find('img.item').each(function() {
            var slot = parseInt($(this).attr('name'));
            var dat = itemPreviews[slot].replace("Un-tradable", "0 Trade").match(/(\d+)(?=(&c=|\sTrade))/g);
            tempItems.push(new Item($(this).attr('title'), slot, dat[0], dat[1]));
        });
        return tempItems;
    }

    //Returns all crystals in the inventory, sorted by item id (newest first)
    this.getCrystals = function() {
        return this.items
            //check if item is a crystal (must be have some word before crystal Crystal to avoid Crstal Rings)
            .filter(function(a) {
                return a.name.match(/.+Crystal/) !== null;
            })
            //order from newest to oldest (item id descending)
            .sort(function(a, b) {
                return b.id - a.id;
            });
    };
    //Returns an array of matching size/colour crystals
    //Ordered by trades on given crystal ascending, then everything else descending
    this.matchCrystal = function(crystal) {
        return this.getCrystals().filter(function(a) {
            return a.id !== crystal.id && a.name == crystal.name;
        }).sort(function(a, b) {
            return (a.trades >= crystal.trades && b.trades >= crystal.trades) ? a.trades - b.trades : b.trades - a.trades;
        });
    };
}

/*****************************************************
                    Hunting Page Methods                    
/****************************************************/

function huntMerge() {
    //Once a crystal drop has been detected
    function checkDrop() {
        //Display 'found match' message on single line row
        function singleRow(crystal) {
            var r = create('tr', {}, false, $('tbody:contains("Item Found")')[0]);
            create('td', {
                    width: "100%",
                    class: "standardrow",
                    align: "center",
                    colspan: "2"
                },
                'You have a matching ' + crystal.name + ' (' + crystal.trades + 't) in your inventory. <span class="merge" style="cursor:pointer;">[Merge]</span>',
                r);
        }

        //create 'item drop' row displaying merged crystal image + result message
        function doubleRow(crystal) {
            var r = create('tr', {}, false, $('tbody:contains("Item Found")')[0]);
            create('td', {
                width: "10%",
                class: "standardrow",
                align: "center"
            }, "<img src='img-bin/items/" + crystal.name.toLowerCase() + ".png'/>", r);
            create('td', {
                width: "90%",
                class: "standardrow",
                align: "center"
            }, crystal.name + " added to inventory.", r);
        }
        $.ajax({
            url: 'inventory.php',
            async: true,
            success: function(data) {
                var inv = new Inventory(data);
                var drop = inv.getCrystals()[0];
                option(drop, inv.matchCrystal(drop));
                //recursive method - if crystal match, display option to merge - if merged, display result
                //if crystal match with previous result, offer to match again, etc
                function option(crystal, matches) {
                    if (matches.length > 0) {
                        singleRow(matches[0]);
                        $('span.merge').click(function() {
                            $(this).remove();
                            //merge newest crystal [0] with best matching crystal [1]
                            crystal.merge(matches[0],
                                //anonymous function as input for merge callback to display merge option on previously merged crystal
                                function(data) {
                                    var nextInv = new Inventory(data);
                                    var newCrystal = nextInv.getCrystals()[0];
                                    doubleRow(newCrystal);
                                    option(newCrystal, nextInv.matchCrystal(newCrystal));
                                }
                            );
                        });
                    }
                }
            }
        });
    }

    //Build a clickable button which allows the user to attack 
    //the same hunt group again without visiting the hunt page again
    //If there are buttons present, the fight is ongoing
    //If there is a page title, user is on error or multiattack screen
    if (!($('.button').length || $('.pagetitle').length)) {
        //Create Start Another Hunt button & position next to Back to Hunting page url, change row to fit
        //Button defaulted to disabled while waiting for attack string
        var td = $('td:has("#back-to-hunting")');
        td.prop("width", "50%");
        var newCell = create("td", {
                class: "standardrow",
                width: "50%",
                align: "center"
            }, false, td.parent()[0]),
            newButt = create("input", {
                type: "button",
                class: "button",
                value: "Start Another Hunt",
                disabled: "disabled"
            }, false, newCell);
        td.before(newCell);

        //Gather attack string from hunting.php && cookie info for next hunt form
        var form = create("form", {
                action: "hunting3.php",
                method: 'post',
                style: "display:none;"
            }, false, document.body),
            attackString,
            cookies = getCookies();
        create("input", {
            type: "hidden",
            name: "group",
            value: cookies.hunting_group
        }, false, form);
        create("input", {
            type: "hidden",
            name: "level",
            value: cookies.hunting_level
        }, false, form);
        create("input", {
            type: "submit",
            value: "Attack Target",
            class: "button",
            id: "hunt-" + cookies.hunting_group + "-" + cookies.hunting_level
        }, false, form);

        $.ajax({
            url: 'hunting.php',
            async: true,
            success: function(data) {
                attackString = data.match(/([a-zA-Z]{20})(?=">')/g).pop();
                create("input", {
                    type: "hidden",
                    name: "attackstring",
                    value: attackString
                }, false, form);
                newButt.disabled = false;
            }
        });
        //Button method - disable to prevent multi-attack & submit to proceed
        $(newButt).click(function() {
            $(this).prop("disabled", true);
            form.submit();
        });
        //Hunt Group 18 - Crystal Entities and crystal drop
        if (cookies.hunting_group == "18" && $('tbody:contains("Item Found")').length) {
            checkDrop();
        }
    }
}
/*****************************************************
                    Inventory Page Methods                    
/****************************************************/

function invMerge() {
    //Call current inventory as instance of inventory object defined above for managing crystals
    var inv = new Inventory(document.body.innerHTML);

    $('img[title*=" Crystal"]').each(function() {
        //Build selectbox containing matching crystal options & merge button
        var slot = $(this).attr('name');
        var row = create('tr', {}, false, false);
        var col1 = create('td', {
            colspan: '2'
        }, false, row);
        var select = create('select', {
            name: 'item',
            id: 'crystalSelect',
            class: 'selectbox',
            style: 'width:100%;'
        }, false, col1);
        var col2 = create('td', {}, false, row);
        var merge = create('input', {
            id: 'merge',
            type: 'button',
            class: 'button',
            style: 'width:100%',
            value: 'Merge'
        }, false, col2);
        var crystal = inv.getCrystals().reduce(function(a, b) {
            return b.slot == slot ? b : a;
        }, null);
        var matches = inv.matchCrystal(crystal);
        if (matches.length > 0) {
            matches.forEach(function(a) {
                create('option', {
                    value: a.slot
                }, a.trades + ' Trades | ID:' + a.id, select);
            });
        } else {
            create('option', {}, 'No Matching Crystals', select);
            select.disabled = true;
            merge.disabled = true;
        }
        select.selectedIndex = 0;
        //Add selectbox & button after crystal is selected in inventory
        $(this).click(function() {
            $('#ItemPreview tbody tbody:contains("Use")').append(row);
        });
        //Merging!
        $(merge).click(function() {
            //Re-create 'crystal to be merged' input as child of form
            create("input", {
                type: "hidden",
                name: "item",
                value: select.selectedOptions[0].value
            }, false, document.getElementsByTagName('form')[0]);
            //remove unneeded Save Changes input
            $('[name=itemarray]').remove();
            //Override Save Changes form for with merging url
            $('form').attr('action', 'inventory10.php?i=' + crystal.slot + '&key=' + inv.key).submit();
        });
    });
}
/*****************************************************
                    Misc                    
/****************************************************/
//call methods where relevant
switch (location.pathname) {
    case '/inventory.php':
        invMerge();
        break;
    case '/hunting3.php':
        huntMerge();
        break;
}