Battle Checker

To help monitor the hippy/frat war; adds a notice to the charpane in KOL of the current image numbers, updating after each charpane refresh.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name           Battle Checker
// @namespace      kol.interface.unfinished
// @description    To help monitor the hippy/frat war; adds a notice to the charpane in KOL of the current image numbers, updating after each charpane refresh.
// @include        http://*kingdomofloathing.com/charpane.php*
// @include        http://127.0.0.1:*/charpane.php*
// @version        1.11
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_xmlhttpRequest
// ==/UserScript==

// version 1.11
// - add @grant, replace GM_log with console.log
// version 1.1
// - minor fix to make it show up more consistently below the character data
// version 1.0

// id for font element
var TPIDFONT='battlechecker_font';
// id for number fields
var TPIDNUMF='battlechecker_frat';
var TPIDNUMH='battlechecker_hippy';
// id for off/on toggle
var TPIDOFFON='battlechecker_offon';
// id for event handlers
var TPIDEF='battlechecker_efrat';
var TPIDEH='battlechecker_ehippy';
// suffix (with player name_) of name of global variable for storing state
var TPONOFF='onoff';
// suffix (with player name_) of name of global variable for storing last value
var TPLAST='last';
// suffix (with player name_) of name of global variable for storing last displayed value
var TPLASTDISPLAY='display';
// url pathname to check
var TPURL='/bigisland.php';
// text to use when in the process of checking
var TPCHECK='checking...';
// text to use when in the process of not checking
var TPNOTCHECK='(combat)';
// disabled text
var TPDISABLE='';
// mouseover text for clickable frat/hippy text
var TPMOSTR='Click to force a check now.';
var TPMOBUSYSTR='Busy checking...';

// colours for different entries.  p is a priority
var colours={'31':{c:'green',p:1,s:1},'32':{c:'red',p:1,s:2},'last':{c:'orangered',p:2,s:1}};
colours[TPNOTCHECK]={c:'gray',p:0,s:1};
colours[TPCHECK]={c:'gray',p:0,s:1};
colours[TPDISABLE]={c:'silver',p:1,s:1};

// main function to find the anchor point and create
// the notification
function createNotice(numf,numh) {
    if (updateActualNotice(numf,numh))
        return;
    var spot =  document.getElementById('rollover');
    if (spot && spot.nextSibling) {
        var parent = spot.nextSibling.firstChild;
        if (parent && parent.tagName=='TABLE' && parent.firstChild && parent.firstChild.tagName=='TBODY')
            parent = parent.firstChild;
        if (parent)
            createActualNotice(parent,numf,numh);
    }
}

// actually creates the notice assuming it's not there already
function createActualNotice(parent,numf,numh) {
    // create a new row for a table with 2 columns
    var x = document.createElement('tr');
    var y = document.createElement('td');
    // we'll just span both rows
    y.setAttribute("colspan","2");
    y.setAttribute("align","center");
    x.appendChild(y);
   
    // surround it all with a font so it's small and we can change colour
    var v = document.createElement('font');
    v.setAttribute('id',TPIDFONT);
    v.setAttribute('size','1');
    y.appendChild(v);

    // create the checkbox
    var offon=document.createElement('input');
    offon.setAttribute('style','vertical-align:middle;');
    offon.setAttribute('id',TPIDOFFON);
    offon.setAttribute('type','checkbox');
    offon.setAttribute('padding','0');
    var pn = GM_getValue("playername");
    if (pn && GM_getValue(pn+'_'+TPONOFF,'true')=='true')
        offon.setAttribute('checked','true');
    offon.addEventListener('click',offonhandler,true);
    v.appendChild(offon);

    // next is a label with everything in it
    var s = document.createElement('label');
    s.setAttribute('style','vertical-align:middle;');
    v.appendChild(s);

    var ts = document.createElement('span');
    ts.appendChild(document.createTextNode('Frat: '));
    ts.addEventListener('click',update,true);
    ts.setAttribute('id',TPIDEF);
    ts.setAttribute('title',TPMOSTR);
    s.appendChild(ts);

    // text we intend to modify later is in a bold
    var w = document.createElement('span');
    w.setAttribute('id',TPIDNUMF);
    s.appendChild(w);
    // set colour
    var cf=colours[numf];
    if (cf) {
        v.setAttribute('color',cf.c);
        v.setAttribute('size',cf.s);
    }
    if (pn && numf==TPNOTCHECK) {
        var lastdisplay=GM_getValue(pn+'_'+TPLASTDISPLAY);
        if (lastdisplay) {
            var ld = lastdisplay.split(',');
            numf=ld[0];
            numh=ld[1];
        }
    }
    w.innerHTML=numf;

    ts = document.createElement('span');
    ts.appendChild(document.createTextNode('  Hippy: '));
    ts.addEventListener('click',update,true);
    ts.setAttribute('id',TPIDEH);
    ts.setAttribute('title',TPMOSTR);
    s.appendChild(ts);

    w = document.createElement('span');
    w.setAttribute('id',TPIDNUMH);
    s.appendChild(w);
    var ch=colours[numh];
    if (ch && (!cf || ch.s<cf.s)) {
        v.setAttribute('color',ch.c);
        v.setAttribute('size',ch.s);
    } 
    w.innerHTML=numh;

    updateColourIfLast(pn,x,numf,numh,cf,ch);

    //parent.insertBefore(x,parent.firstChild);
    parent.appendChild(x);
}

// update notice instead if it's there already for some reason
function updateActualNotice(numf,numh) {
    var x = document.getElementById(TPIDFONT);
    var y = document.getElementById(TPIDNUMF);
    var z = document.getElementById(TPIDNUMH);
    if (!x || !y || !z)
        return false;

    var cf=colours[numf];
    if (cf) {
        x.setAttribute('color',cf.c);
        x.setAttribute('size',cf.s);
    } else {
        x.removeAttribute('color');
        x.setAttribute('size','1');
    }
    if (numf!=TPNOTCHECK)
        y.innerHTML=numf;

    var ch=colours[numh];
    if (ch && (!cf || ch.s<cf.s)) {
        x.setAttribute('color',ch.c);
        x.setAttribute('size',ch.s);
    }
    if (numf!=TPNOTCHECK)
        z.innerHTML=numh;

    updateColourIfLast(GM_getValue("playername"),x,numf,numh,cf,ch);

    var pn = GM_getValue("playername");

    return true;
}

// check if the current update is an incremental change from the previous
// and if so colour it if it hasn't been coloured already
function updateColourIfLast(pn,x,numf,numh,cf,ch) {
    if (pn) {
        var cur=numf+','+numh;
        if (cur.match(/[0-9]+,[0-9]+/)) {
            var last=GM_getValue(pn+'_'+TPLAST,cur);
            if (last.length>0 && !cf && !ch && last!=cur && cur!='0,0') {
                //console.log("will update.  last: "+last+", cur: "+cur);
                x.setAttribute('color',colours.last.c);
                x.setAttribute('size',colours.last.s);
            } 
            //else console.log("overwrite update.  last: "+last+", cur: "+cur);

            GM_setValue(pn+'_'+TPLAST,cur);
        }
        if (numf!=TPNOTCHECK)
            GM_setValue(pn+'_'+TPLASTDISPLAY,cur);
        //else console.log("will not update last: "+cur);
    } 
    //else console.log("unknown player name");
}

// handler for on/off checkbox
function offonhandler(e) {
    var pn = GM_getValue("playername");
    if (!pn)
        return;
    var c = this.getAttribute('checked');
    if (c) {
        this.removeAttribute('checked');
        GM_setValue(pn+'_'+TPONOFF,'false');
    } else {
        this.setAttribute('checked','true');
        GM_setValue(pn+'_'+TPONOFF,'true');
    }
    update();
}

// checks if player is likely in the middle of a fight
function infight() {
    var somef=window.parent.frames;
    var goo = null;
    for(var j=0;j<somef.length;j++) {
        if (somef[j].name=="mainpane")
            goo=somef[j];
    }
    if (goo) {
        var doc = goo.document;
        if (!doc || !doc.body)
            return false;
        var mainframe=doc.body.innerHTML;
        if (mainframe && mainframe.indexOf('Combat!')>=0) {
            var x = doc.getElementsByTagName('a');
            for (var i=0;i<x.length;i++) {
                if (x[i].innerHTML && (x[i].innerHTML.indexOf('Adventure Again')==0 || x[i].innerHTML.indexOf('Go back to Your Inventory')==0))
                    return false;
            }
            return true;
        }
    } 
    return false;
}

// utility function, stolen from other scripts
function GM_get(dest, callback)
{   GM_xmlhttpRequest({
      method: 'GET',
      url: dest,
        //onerror:function(error) { console.log("GM_get Error: " + error); },
        onload:function(details) {
            if( typeof(callback)=='function' ){
                callback(details.responseText);
}   }   }); }


// main function to update state
// creates a temporary notice, checks the bigisland, updates the notice
function update() {
    var pn = GM_getValue("playername");
    if (!pn)
        return;
    if (GM_getValue(pn+'_'+TPONOFF,'true')!='true') {
        createNotice(TPDISABLE,TPDISABLE); // notice is turned off
    } else {
        // remove and restore event handlers to avoid concurrency problems
        // nb leave on/off---may cause weirdness if toggled in the middle of checking
        // but at least it still allows someone to always turn it off
        createNotice(TPCHECK,''); // intermediate notice while we load the page
        var ef,eh;
        ef = document.getElementById(TPIDEF);
        eh = document.getElementById(TPIDEH);
        if (ef) {
            ef.removeEventListener('click',update,true);
            ef.setAttribute('title',TPMOBUSYSTR);
        }
        if (eh) {
            eh.removeEventListener('click',update,true);
            eh.setAttribute('title',TPMOBUSYSTR);
        }
        GM_get('http://'+window.location.host+TPURL,function(response){
                if(response!=''){
                    //console.log('response is '+response);
                    var f = response.indexOf("bigisland/bfleft");
                    var h = response.indexOf("bigisland/bfright");
                    if (f>=0 && h>=0) {
                        var ff = response.substr(f).indexOf('.');
                        var hh = response.substr(h).indexOf('.');
                        f = parseInt(response.substr(f+16,ff-16));
                        h = parseInt(response.substr(h+17,hh-17));
                        createNotice(f,h);
                    } else {
                        createNotice('?','?'); // can't find battlefield images
                    }
                } else {
                    createNotice('?','?'); // can't load bigisland
                }
            });
        if (ef) {
            ef.addEventListener('click',update,true);
            ef.setAttribute('title',TPMOSTR);
        }
        if (eh) {
            eh.addEventListener('click',update,true);
            eh.setAttribute('title',TPMOSTR);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// stolen and adapted from Anti-Marty's fortune cookie script
////////////////////////////////////////////////////////////////////////////////
// parse the char pane for the player name
// revised version! now taken directly from kolpreviousnadventures to handle compact mode
function getPlayerNameFromCharpane() {
    var username = document.getElementsByTagName("b");
    if (!username || username.length < 1) return false;
    username = username[0];
    if (!username) return false;
    username = username.firstChild;
    if (!username) return false;
    // in full mode the link is <a><b>Name</b></a>
    // in compact mode it's <b><a>Name</a></b>
    // so have to handle this, and also can use it to tell
    // whether it's in compact mode or not.
    while (username && username.nodeType == 1) {
        username = username.firstChild;
    }
    if (!username) return false;
    username = username.nodeValue;
    if (!username) return false;
    username = username.toLowerCase();
    GM_setValue("playername",username);
    return username;
}


// what we do each time the charpane is loaded:
var firstpn=getPlayerNameFromCharpane();
if(firstpn) {
    if (!infight()) {
        update();
    } else {
        if (GM_getValue(firstpn+'_'+TPONOFF,'true')=='true') {
            createNotice(TPNOTCHECK,'');
        } else {
            createNotice(TPDISABLE,TPDISABLE);
        }
    }
}