route assistant

金书红颜录路线武功规划脚本,需配合相关维基页面使用

Per 09-11-2015. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name        route assistant
// @namespace   myhead
// @description 金书红颜录路线武功规划脚本,需配合相关维基页面使用
// @version     1
// @grant       none
// @include     http://tpr.inkit.org/doku.php?id=tpr5:route
// ==/UserScript==
/*
    oResult={
        '拳':[aRegExpSearchResult1{[0](matched content),[1](captured content in parenthesis),[2],index,input},aRegExpSearchResult2...],
        '剑':[],
        '兵':[],
        '特':[],
        '暗':[],
        '内':[],
        '轻':[],
        '余':[]
    }
    Pos= aC1[i] ={
            __Con: {
                sName: route position name,
                sCon: RegExp context of the position,
                nStart: start index in sCon,
                nEnd: end index in sCon
            },
            next: []
            //next is deeper list of route option object
        }
    下列说明中[]表示被包围的是一个词,括号内容为这个词的描述,实际使用中不要输入[]。
    路线格式:
<pre>
    #[天书或其他基本剧情部分1]
        1 [第一级路线分歧1] bind [第一级路线分歧3]
            2 [第二级路线分歧1]
                @ 武功1+8-特 武功二+8阳-剑 武功5+8-特?2
            2 [第二级路线分歧2]
                @ 武功d+8-特
        1 [第一级路线分歧2]
                @ 武功233+8-特
    #[天书或其他基本剧情部分1]
        1 [第一级路线分歧3]
                @ 武功233+8-特 武功2+3-特 武功1+8-特  武功8+8-特
……
</pre>
    每个以数字或符号开头的有效语句占一行。#表示选项路径的根;以1、2、3等一位数字开头的语句分别代表第一、二、三级的子选项,子选项最高9级;以@开头的为该选项路径的武功列表。
    使用路线绑定时,在需要添加绑定的路线后添加 bind [要绑定的路线分歧],[]中的内容是需要绑定的目标路线选项名,不需要包含完整路径,也不要不含语句前的数字符号,bind的内容和要绑定的目标在忽视前后空格的情况下需要精确一致。根据路线情况可添加单向绑定(如水笙线的收狄云分支单向绑定白绣线)或双向绑定(如倚天与鹿鼎的绑定)。要同时为一个语句添加多个绑定,则需在句尾添加多个bind参数,bind参数之间以空格隔开。
    缩进不是必要的,但为增强源数据可读性考虑请逐行缩进。
    武功条目格式:
<pre>
    [武功名,请勿使用数字、空格,必须]+[指数,一位数字,可选][阴阳,一位字符,阴或阳,可选]-[系别,一位字符,可选]?[周目要求,一位数字,可选,缺省为1]
</pre>
    条目起始部分为不限长度的汉字序列,决定该武功条目的名称;加号 + 引导武功的系数、轻内的等级或其他有意义的一位数字,将被用在输出结果的排序中;紧跟着+号与一位数字的是武功对应内力性质,一位汉字,阴或者阳,只有在指定了+号与数字内容的情况下才能添加阴阳参数,在进行输出结果的过滤时,如果指定了阴阳选项,将筛选出阴阳属性相符的以及未限制阴阳的武功;减号 - 引导一位汉字的武功类型指代,将被用在输出结果的分类中;问号 ? 引导可获得该武功的最低周数,将被用于判断是否将该武功条目输出,默认为1。除了开头的武功名必须指定之外,其他所有参数都是可选的,但所有被使用的参数必须按照+-?的顺序出现。所有符号都应是半角英文符号。武功条目之间以空格分隔。
    在编辑源数据时进行预排序(令武功条目从左至右从小到大基本有序)可降低脚本进行排序时的开销。
    
下面是一个示例,名字都是乱起的,信息也不全,供参照:
<pre>
#飞狐
    1 灵素线
        2 小灵素
            @ 辽东野狐拳+2-拳
        2 大大大大灵素 bind 救丁典 bind 灵素救青青 bind 救俞岱岩 bind 救赵敏
            @ 辽东野狐拳+2-拳
    1 冰霜线
        @ 飞天神行+4阳-轻
#碧血剑
    1 青青线
        2 灵素救青青 bind 灵素线
            @ 金蛇剑法+6阳-剑
        2 传说中的憋过桥再救
            @ 金蛇剑法+6阳-剑
        2 不救青青
            @ 金蛇剑法+6阳-剑 混元掌+6阳-拳?2
    1 阿九线
        @ 混元掌+6阳-拳
#鸳鸯刀
    @ 夫妻刀法+4-兵
#其他剧情
    1 低道德论剑
        @ 参合剑气
    1 高道德论剑
        2 拜师剑系
            @ 天子剑法+9-剑
……
</pre>
*/
var aC1= [],
    //aC1 correspond to the list of route root object
    aRoute=[],
    oResult={},
    sOrigin='',
    sResult='',
    t;
window.mainDiv= document.createElement('div'),
window.navi= document.createElement('div'),
window.div1= document.createElement('div'),
window.div2= document.createElement('div'),
window.div2p= document.createElement('code'),
window.div2f= document.createElement('div'),
window.round= document.createElement('select'),
window.speci= document.createElement('span'),
window.toggle= document.createElement('button'),
window.oTarget= {};
toggle.innerHTML="打开路线武功规划器";
toggle.onclick=function(){
    if(sOrigin=== '') main();
    mainDiv.style.display='';
};
window.onload=function(){
    toggle.id='__toggle';
    mainDiv.id='__mainDiv';
    oTarget= document.getElementById('bodyContent');
    oTarget.insertBefore(toggle,oTarget.firstChild);
};
function main(){
    //search route and set UI;
    if(!oTarget){
        alert('找不到源数据');
        return;
    }
    sOrigin=oTarget.textContent;
    var rP1= /#(.+)/g;
    for(var i=0,aTemp;i<100;i++) {
        aTemp=rP1.exec(sOrigin);
        //aTemp contains [0] [1] .index .input
        if (aTemp=== null) break;
        aC1[i]={
            //aC1[i] is route option object
            __Con: {
                sPath: aTemp[0],
                sName: aTemp[1].match(/\s*\S+\s*/)[0],
                sCon: aTemp.input,
                nStart: aTemp.index,
                nEnd: undefined
            },
            next: []
            //next is deeper list of route option object
        };
        //initialise the route result array
        aRoute[i]= aC1[i];
        if(i>0)
            aC1[i-1].__Con.nEnd= aTemp.index-1;
    }
    //create UI and set up initial option list
    mainDiv.setAttribute('style','position: absolute; left: 200px; top: 100px; width: 800px; height: 600px; background: white; border: solid #E0E0E0; overflow: auto;resize: both;');
    //
    navi.innerHTML=
        "<button onclick='div1.style.display=\"\";div2.style.display=\"none\";'>路线选择</button>"+
        "<button onclick='div2.style.display=\"\";div1.style.display=\"none\";'>武功统计</button>"+
        "<button onclick='mainDiv.style.display=\"none\";' style='float: right;'>隐藏</button>";
    navi.setAttribute('style','border-bottom: solid #F0F0F0; background: #F0F0F0');navi.setAttribute('style','border-bottom: solid #F0F0F0;background: #F0F0F0');
    div2.style.display='none';
    div2.innerHTML+= '请选择周目:';
    for(var i=1;i<7;i++)
        round.innerHTML+='<option value='+i+'>'+i+'周</option>';
    div2.appendChild(round);
    button= document.createElement('button');
    button.innerHTML='开始统计';
    button.onclick= analyse;
    div2.appendChild(button);
    //create filter option bar div2f
    div2f.innerHTML+= '结果过滤器:';
    speci.innerHTML+=
        '名称显示<select><option value=1 selected>简名</option><option value=2>全名</option></select> '+
        '类别<select><option value=0 selected>&nbsp;&nbsp;&nbsp;&nbsp;</option><option value="拳">拳</option><option value="剑">剑</option><option value="兵">兵</option><option value="特">特</option><option value="暗">暗</option><option value="内">内</option><option value="轻">轻</option><option value="[^拳剑兵特暗内轻]">其他</option></select>'+
        '阴阳<select><option value=0 selected>&nbsp;&nbsp;&nbsp;&nbsp;</option><option value="阴">阴</option><option value="阳">阳</option></select>'+
        '最低数值<select><option value=0 selected>&nbsp;&nbsp;&nbsp;&nbsp;</option><option value=2 >2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option><option value="6">6</option><option value="7">7</option><option value="8">8</option><option value="9">9</option></select>';
    div2f.appendChild(speci);
    button= document.createElement('button');
    button.innerHTML='过滤结果';
    button.onclick=filter;
    div2f.appendChild(button);
    div2.appendChild(div2f);
    div2.appendChild(div2p);
    var tarList=document.createElement('ol');
    for(var i=0,li,doList;i<aC1.length;i++){
        //set up the root group
        li= document.createElement('li');
        li.innerHTML=aC1[i].__Con.sName;
        doList= dive(aC1[i],1);
        //set up the first layer option
        if(doList.length>0){
            var select=document.createElement('select');
            select.onchange=routeQuery;
            select.innerHTML+="<option value='"+i+"' selected ></option>";
            for(var j=0;j<doList.length;j++){
                //add drop-down list for each option
                select.innerHTML+=
                    "<option value='"+i+"."+j+"' >"+doList[j].__Con.sName+"</option>";
            }
            li.appendChild(select);
        }
        tarList.appendChild(li);
    }
    button= document.createElement('button');
    button.innerHTML='检查路线冲突';
    button.onclick=checkBinding;
    div1.appendChild(button);
    div1.appendChild(tarList);
    mainDiv.appendChild(navi);
    mainDiv.appendChild(div1);
    mainDiv.appendChild(div2);
    document.body.appendChild(mainDiv);
}
function dive(Pos,nSym){
    //dive into route chain,group represents the number of option root
    var rP= new RegExp('\\s'+nSym+'(.*)','g'),
        sContext= Pos.__Con.sCon.substring(Pos.__Con.nStart,Pos.__Con.nEnd);
    for(var i=0,aTemp;i<100;i++) {
        aTemp= rP.exec(sContext);
        if(aTemp=== null) break;
        Pos.next[i]={
            __Con: {
                sPath: Pos.__Con.sPath+'-'+aTemp[1],
                sName: aTemp[1].match(/\s*\S+\s*/)[0],
                sCon: aTemp.input,
                nStart: aTemp.index,
                nEnd: undefined
            },
            next: []
        };
        if(i>0) Pos.next[i-1].__Con.nEnd= aTemp.index-1;
    }
    return Pos.next;
    //the newly matched route to create drop-down of
}
function sort(aEntry,aTarget){
    var i,nIndi;
    if(!aTarget[0]){
        aTarget[0]=aEntry;
        return;
    }
    if(!aEntry[1]) nIndi=0;
    else nIndi=parseInt(aEntry[1].match(/\d/)[0]);
    for(i=0;i<aTarget.length;i++){
        if(nIndi< aTarget[i][1]){
            for(var j=aTarget.length-1;j>= i;j--){
                aTarget[j+1]= aTarget[j];
            }
            break;
        }
    }
    aTarget[i]= aEntry;
}
//following fuctions are event handle
function routeQuery(){
    //connecting with selcet.onchange, the event handle to search option of drop-down list,whose value corresponds to the index of content array.Then create relevant drop-down list.
    while(this.nextSibling)
        this.parentNode.removeChild(this.nextSibling);
    if(this.value== this.firstChild.value) return;
    var aValue=this.value.match(/\d+/g),
        //[this] is the node elements triggering the event.[this.value] has form like'1.2.3'
        Pos=aC1[aValue[0]],
        //Pos is route option object
        nSym= 0;
        //nSym is the current route layer of Pos
    for(var i=1;i<aValue.length;i++){
        //convert string path in select's value to virtual array path
        Pos=Pos.next[aValue[i]];
        nSym++;
    }
    var list= dive(Pos,nSym+1);
    if (list.length>0) {
        var select=document.createElement('select');
        select.onchange=routeQuery;
        select.innerHTML+="<option value='"+this.value+"' selected ></option>";
        for(var j=0;j<list.length;j++){
            select.innerHTML+=
                "<option value='"+this.value+"."+j+"' >"+list[j].__Con.sName+"</option>";
        }
        this.parentNode.appendChild(select);
    }
    //if there is no sub option in deeper layer,log the result
    aRoute[aValue[0]]= Pos;
}
//connect with the button '开始统计'
function analyse(){
    oResult={};
    for(var i=0,oT;i<aRoute.length;i++){
        var rP2=/\S+?(\+[1-9][阴阳]?)?(-\S)?(\?[1-6])?(?=\s|$)/gm;
        oT=aRoute[i].__Con;
        sResult=oT.sCon.substring(oT.nStart,oT.nEnd);
        t=sResult.match(/@.*/);
        if(!t) continue;
        else sResult=t[0].substring(1);
        for(var j=0,aT=[];j<200;j++){
            aT=rP2.exec(sResult);
            //at[0]-- content; at[1]-- +[rank][yinyang]; at[2]-- -[type]; at[3]-- ?[round]
            if(!aT) break;
            if(!aT[1]) aT[1]= '+0';
            if(aT[3] && parseInt(aT[3].substring(1))> round.value) continue;
            if(!aT[2]) aT[2]='未分类';
            else aT[2]=aT[2].substring(1);
            if(!oResult[aT[2]]) oResult[aT[2]]=[];
            sort(aT,oResult[aT[2]]);
        }
    }
    sResult='';
    //output
    for(var x in oResult){
        sResult+= '类别——'+x+':<br />&nbsp;&nbsp;';
        var rTp= /[^\-?\s]+/;
        for(var i=0;i<oResult[x].length;i++){
            sResult+= oResult[x][i][0]+'&nbsp;&nbsp;';
        }
        sResult+='<br /><br />';
    }
    div2p.innerHTML= sResult;
}
//connect with button in filter bar
function filter(){
    var aOut= speci.children,
        rTp= /[^-?\s]+/;
    sResult= '';
    //aOut[1]-- string representing type; aOut[2]-- string representing yin yang; aOut[3]-- number representing value rank
    for(var x in oResult){
        if(aOut[1].value!= '0'){
            var rP=new RegExp(aOut[1].value);
            if(!rP.test(x)) continue;
        }
        sResult+= '类别——'+x+':<br />&nbsp;&nbsp;';
        if(aOut[0].value== 1){
            for(var i= 0,aTemp;i<oResult[x].length;i++){
                aTemp=oResult[x][i];
                if(aOut[2].value!= '0')
                    if(aTemp[1])
                        if(t= aTemp[1].match(/[阴阳]/))
                            if(t[0]!= aOut[2].value)
                                continue;
                if(aOut[3].value== '0' || parseInt(aTemp[1].match(/\d/)[0])>= aOut[3].value)
                    sResult+= aTemp[0].match(rTp)[0]+'&nbsp;&nbsp;';
            }
        }
        else{
            for(var i= 0,aTemp;i<oResult[x].length;i++){
                aTemp=oResult[x][i];
                if(aOut[2].value!= '0')
                    if(aTemp[1])
                        if(t= aTemp[1].match(/[阴阳]/))
                            if(t[0]!= aOut[2].value)
                                continue;
                if(aOut[3].value== '0' || parseInt(aTemp[1].match(/\d/)[0])> aOut[3].value)
                    sResult+= aTemp[0]+'&nbsp;&nbsp;';
            }
        }
        sResult+='<br /><br />';
    }
    div2p.innerHTML= sResult;
}
//connect with button to confirm proper binding
function checkBinding(){
    var aBind=[],Compare={},sCollision='';
    for(var i=0;i<aRoute.length;i++){
        t= aRoute[i];
        t=t.__Con;
        var aT= t.sPath.split(/-/);
        for(var j=0;j<aT.length;j++){
            Compare[aT[j].match(/[^ ]+/)[0]]= 1;
        }
        var rP= /[^ \-\n]*bind\s*([^ \-\n]+)/g;
        for(var j=0,aT1;j<100;j++){
            aT1= rP.exec(t.sPath);
            if(aT1) aBind.push(aT1);
            else break;
        }
    }
    for(var i=0,aT;i<aBind.length;i++){
        if(!Compare[aBind[i][1]]) sCollision+= aBind[i].input+"\n";
    }
    if(sCollision!== '')
        alert ('路线冲突:\n'+sCollision.replace(/bind/g,'绑定'));
    else
        alert ('无冲突');
}