直接贴代码影响帖子的阅读效果,还是把它包起来吧
multi-message.ks 点我察看
@if exp="typeof(global.multimessage_object) == 'undefined'"
@iscript
// 多重字幕输出指令用的Tag缓冲器
class TagBuffer
{
var owner;
var window;
var id; // 字幕序号
var fore, back; // 表/里字幕图层的引用
var forebreakglyph, backbreakglyph; // 换行/换页等待记号
var tagarray = []; // 存放本次输出的所有Tag的数组
var nexttagnum; // 将要执行的Tag的序号
var handlers; // 存放Tag处理函数的字典数组
function TagBuffer(owner, id)
{
// 构造函数
this.owner = owner;
this.id = id;
window = owner.window;
fore = window.fore.messages[id];
back = window.back.messages[id];
forebreakglyph = new ClickGlyphLayer(window, window.fore.base);
forebreakglyph.name = "换行/换页等待记号";
backbreakglyph = new ClickGlyphLayer(window, window.fore.base);
backbreakglyph.name = "换行/换页等待记号";
handlers = getHandlers();
}
function finalize()
{
// 资源释放
clear();
owner = void;
window = void;
fore = void;
back = void;
invalidate forebreakglyph;
invalidate backbreakglyph;
invalidate tagarray;
invalidate handlers;
}
function clear()
{
// 清除缓冲器中的内容
for(var i = 0; i < tagarray.count; i++)
invalidate tagarray;
tagarray.clear();
nexttagnum = 0;
}
function loadTags(strtags)
{
//载入输出指令
clear(); // 清除缓冲器中原有的内容
var p = 0;
var length = strtags.length;
var tagtemp;
var tagstart;
var taglength;
while(p < length)
{
tagtemp = void;
if(strtags[p] == '[')
{
tagstart = p;
taglength = 0;
while(p < length && strtags[p] != ']')
{
p++;
taglength++;
}
p++;
taglength++;
tagtemp = string2TagDictionary(strtags.substr(tagstart, taglength));
}
else
{
tagtemp = new Dictionary();
tagtemp["tagname"] = "ch";
tagtemp["text"] = strtags[p];
p++;
}
if(tagtemp !== void)
tagarray.add(tagtemp);
}
}
function string2TagDictionary(strtag)
{
// 将字符串转换为符合Tag格式的字典数组形式的对象
if(strtag[0] == '[')
strtag = strtag.substr(1);
if(strtag[strtag.length-1] == ']')
strtag = strtag.substr(0, strtag.length-1);
// 去掉两端的 '[' 和 ']'
var tagarr = [];
tagarr.split(' ', strtag, void, true);
var tag = %[];
var tagnodearr = [];
for(var i = 0; i < tagarr.count; i++)
{
tagnodearr.split('=', tagarr, void, true);
if(tagnodearr.count == 1)
tag["tagname"] = tagnodearr[0];
else if(tagnodearr.count == 2)
tag[tagnodearr[0]] = tagnodearr[1];
}
return tag;
}
function handleTag()
{
// 处理1个Tag,如果所有Tag都处理完了则返回false,否则返回true
var tag, handler;
if(tagarray.count > nexttagnum)
{
tag = tagarray[nexttagnum];
handler = handlers[tag.tagname];
if(handler !== void)
handler(tag);
else
Debug.message("一个名为“" + tag.tagname + "”的Tag因不在本插件的处理范围内而被忽略。");
nexttagnum++;
return true;
}
return false;
}
function getHandlers()
{
return %[ // 字典数组对象
/*
tagHandlers群是指名字和对应的函数组成的“名称-函数组”的序列。其格式为:
函数名 : function(elm)
{
// 函数体
} incontextof this,
但是,如果函数名同时也是关键字的话,就必须把「函数名 : 」换成
「"函数名" => 」。
incontextof this 的作用是把函数关联到其所在的类上。
*/
//--------------------------------------- タグハンドラ群(メッセージ操作) --
ch : function(elm)
{
// 文字表示
forebreakglyph.visible = false;
backbreakglyph.visible = false;
fore.processCh(elm.text);
back.processCh(elm.text);
} incontextof this,
graph : function(elm)
{
// グラフィックを文字として表示
forebreakglyph.visible = false;
backbreakglyph.visible = false;
fore.processGraph(elm);
back.processGraph(elm);
} incontextof this,
hch : function(elm)
{
// 縦中横
forebreakglyph.visible = false;
backbreakglyph.visible = false;
var expand = elm.expand !== void && +elm.expand;
fore.putHorizonCh(elm.text, expand);
back.putHorizonCh(elm.text, expand);
} incontextof this,
r : function(elm)
{
// 改行
//forebreakglyph.visible = false;
//backbreakglyph.visible = false;
fore.processReturn();
back.processReturn();
} incontextof this,
ruby : function(elm)
{
// 设置下一个文字对应的标注读音用的“振假名”
fore.setRuby(elm.text);
back.setRuby(elm.text);
} incontextof this,
font : function(elm)
{
// 设置字体风格风格
fore.setFont(elm);
back.setFont(elm);
} incontextof this,
deffont : function(elm)
{
// 设置默认字体风格
fore.setDefaultFont(elm);
back.setDefaultFont(elm);
} incontextof this,
resetfont : function(elm)
{
// 重置字体风格
fore.resetFont();
back.resetFont();
} incontextof this,
style : function(elm)
{
// 设置字幕显示风格
fore.setStyle(elm);
back.setStyle(elm);
} incontextof this,
defstyle : function(elm)
{
// 设置字幕显示默认风格
fore.setDefaultStyle(elm);
back.setDefaultStyle(elm);
} incontextof this,
resetstyle : function(elm)
{
// 重置字幕显示风格
fore.resetStyle();
back.resetStyle();
} incontextof this,
link : function(elm)
{
// 跳转选项开始
fore.beginHyperLink(elm);
back.beginHyperLink(elm);
} incontextof this,
endlink : function(elm)
{
// 跳转选项结束
fore.endHyperLink(elm);
back.endHyperLink(elm);
} incontextof this,
button : function(elm)
{
// 生成一个按钮
forebreakglyph.visible = false;
backbreakglyph.visible = false;
fore.addButton(elm);
back.addButton(elm);
} incontextof this,
edit : function(elm)
{
// 生成一个单行编辑框
forebreakglyph.visible = false;
backbreakglyph.visible = false;
fore.addEdit(elm);
back.addEdit(elm);
} incontextof this,
checkbox : function(elm)
{
// 生成一个选择框
forebreakglyph.visible = false;
backbreakglyph.visible = false;
fore.addCheckBox(elm);
back.addCheckBox(elm);
} incontextof this,
l : function(elm)
{
// 显示换行等待记号
fore.showLineBreakGlyph(forebreakglyph);
back.showLineBreakGlyph(backbreakglyph);
} incontextof this,
p : function(elm)
{
// 显示换页等待记号
fore.showPageBreakGlyph(forebreakglyph);
back.showPageBreakGlyph(backbreakglyph);
} incontextof this,
position : function(elm)
{
// 设置当前字幕图层的位置和属性
fore.setPosition(elm);
back.setPosition(elm);
} incontextof this,
er : function(elm)
{
// 擦除当前字幕图层的内容
forebreakglyph.visible = false;
backbreakglyph.visible = false;
fore.clear();
back.clear();
} incontextof this,
indent : function(elm)
{
// 设置缩进
fore.setIndent();
back.setIndent();
} incontextof this,
endindent : function(elm)
{
// 解除缩进
fore.resetIndent();
back.resetIndent();
} incontextof this,
locate : function(elm)
{
// 设定文字表示位置
fore.locate(elm.x, elm.y);
back.locate(elm.x, elm.y);
} incontextof this,
glyph : function(elm)
{
// 设定单击等待提示记号
fore.setGlyph(elm);
back.setGlyph(elm);
} incontextof this,
locklink : function(elm)
{
// link禁用
fore.setSelProcessLock(true);
back.setSelProcessLock(true);
} incontextof this,
unlocklink : function(elm)
{
// link禁用解除
fore.setSelProcessLock(false);
back.setSelProcessLock(false);
} incontextof this ];
}
}
// 即时切换的多重字幕插件
class MultiMessageControllerPlugin extends KAGPlugin
{
var numMessageLayers; // 字幕图层数量(2-9)
var numMessageLayersOld; // 原有的字幕图层数量
var window; // 父窗口,通常是kag主窗口
var fore = []; // 各个表图层的引用
var back = []; // 各个裏图层的引用
var tagbuffer = []; //各个字幕的tag缓冲器
var lock; // 字幕切换功能是否处于禁用状态
var stable; // 系统是否处于等待状态
var hidden; // 字幕图层是否处于隐藏状态
function MultiMessageControllerPlugin(window)
{
// 构造函数
super.KAGPlugin(); // 调用父类的构造函数
this.window = window; // 获取父窗口对象
this.numMessageLayers = 0;
Debug.message("多重字幕控制器构造完成");
}
function finalize()
{
release();
invalidate fore;
invalidate back;
invalidate tagbuffer;
window = void;
super.finalize(...);
}
function initialization(num)
{
// 初始化
if(typeof(num) == "Integer" && num >= 2 && num <= 9)
{
lock = false;
stable = false;
hidden = false;
numMessageLayersOld = window.numMessageLayers;
numMessageLayers = num;
window.allocateMessageLayers(numMessageLayers); // 设置字幕图层数量
for(var i = 0; i < numMessageLayers; i++)
{
fore = window.fore.messages;
back = window.back.messages;
tagbuffer = new TagBuffer(this, i);
}
setCurrentMessageLayer(0);// 将当前字幕图层置为Message0
window.keyDownHook.add(onKeyDown); // 向主窗口添加键盘消息响应函数
Debug.message("多重字幕控制器初始化完成, 字幕图层数为" + numMessageLayers);
}
else
{
throw new Exception("多重字幕控制器初始化失败 ( 字幕图层数量不合法,正确范围为 2 - 9 )");
}
}
function release()
{
// 释放该插件
if(numMessageLayers > 0)
{
setCurrentMessageLayer(0);
for(var i = 0; i < numMessageLayers; i++)
{
fore = back = void;
invalidate tagbuffer;
}
window.allocateMessageLayers(numMessageLayersOld);
window.keyDownHook.remove(onKeyDown);
numMessageLayers = 0;
}
}
function setCurrentMessageLayer(currentNum)
{
// 设置当前字幕图层
if(typeof(currentNum) == "Integer" && currentNum >= 0 && currentNum < numMessageLayers)
{
fore[window.currentNum].visible = false;
back[window.currentNum].visible = false;
// 将原有的字幕图层隐藏
window.currentNum = currentNum;
if(window.currentPage)
window.current = back[currentNum];
else
window.current = fore[currentNum];
//修改当前字幕图层
window.updateBeforeCh = 1;
fore[window.currentNum].visible = true;
back[window.currentNum].visible = true;
//显示设置后的当前字幕图层
Debug.message("当前字幕图层设置成功,当前字幕图层序号为" + currentNum);
}
else
{
throw new Exception("当前字幕图层设置失败 ( 指定的字幕图层序号不存在 )");
}
}
function onKeyDown(key, shift)
{
// 按键消息响应函数
Debug.message(key + "键按下");
if(key == 48 || key == 96)
{
// 主键盘数字0键或小键盘数字0键
System.inform("字幕数量为" + numMessageLayers + "\n"
+ "当前字幕为message" + window.currentNum + "\n\n"
+ "1-9的数字键:切换到message0-message8的各个字幕(如果存在的话)\n"
+ "PageUp键: 切换到上一个字幕\n"
+ "PageDown键: 切换到下一个字幕\n"
+ "Home键: 切换到第一个字幕(message0)\n"
+ "数字0键: 显示此帮助对话框",
"多重字幕插件的当前状态");
}
if(numMessageLayers == 0) // 该插件处于不可用状态
return;
if(lock)
return;
if(!stable || hidden)
return;
var index;
if(key >= 49 && key <= 57)
{
// 主键盘1-9的数字键
index = key - 49;
if(index >= 0 && index < numMessageLayers)
setCurrentMessageLayer(index);
}
else if(key >= 97 && key <= 105)
{
// 小键盘1-9的数字键
index = key - 97;
if(index >= 0 && index < numMessageLayers)
setCurrentMessageLayer(index);
}
else if(key == 33)
{
// PageUp键
if(window.currentNum > 0)
setCurrentMessageLayer(window.currentNum - 1);
}
else if(key == 34)
{
// PageDown键
if(window.currentNum < numMessageLayers - 1)
setCurrentMessageLayer(window.currentNum + 1);
}
else if(key == 36)
{
// Home键
setCurrentMessageLayer(0);
}
}
function messageOutput(elm)
{
// 输出多重字幕
var strtag, tag;
for(var i = 1; i < numMessageLayers; i++)
{
strtag = elm["message" + i];
if(strtag !== void)
{
tagbuffer.loadTags(strtag);
while(tagbuffer.handleTag())
{
}
}
else
tagbuffer.clear();
}
}
function onStore(f, elm)
{
// 栞を保存するとき
var dic = f.multimsg = %[];
dic.numMessageLayers = numMessageLayers;
dic.numMessageLayersOld = numMessageLayersOld;
}
function onRestore(f, clear, elm)
{
// 栞を読み出すとき
var dic = f.multimsg;
release();
initialization(dic.numMessageLayers);
numMessageLayersOld = dic.numMessageLayersOld;
}
function onStableStateChanged(stable)
{
// 「安定」( s l p の各タグで停止中 ) か、
// 「走行中」 ( それ以外 ) かの状態が変わったときに呼ばれる
this.stable = stable;
if(stable)
Debug.message("「安定」");
else
Debug.message("「走行中」");
if(!stable)
setCurrentMessageLayer(0);
}
function onMessageHiddenStateChanged(hidden)
{
// メッセージレイヤがユーザの操作によって隠されるとき、現れるときに
// 呼ばれる。メッセージレイヤとともに表示/非表示を切り替えたいときは
// ここで設定する。
this.hidden = hidden;
if(hidden)
Debug.message("MessageLayer「非表示」");
else
Debug.message("MessageLayer「表示」");
}
function onCopyLayer(toback)
{
// レイヤの表←→裏の情報のコピー
// backlay タグやトランジションの終了時に呼ばれる
// ここでレイヤに関してコピーすべきなのは
// 表示/非表示の情報だけ
}
function onExchangeForeBack()
{
// 裏と表の管理情報を交換
// children = true のトランジションでは、トランジション終了時に
// 表画面と裏画面のレイヤ構造がそっくり入れ替わるので、
// それまで 表画面だと思っていたものが裏画面に、裏画面だと思って
// いたものが表画面になってしまう。ここのタイミングでその情報を
// 入れ替えれば、矛盾は生じないで済む。
// ここで表画面、裏画面のレイヤに関して管理すべきなのは
// fore と back の変数だけ
var tmp;
tmp = back[0];
back[0] = fore[0];
fore[0] = tmp;
}
}
kag.addPlugin(global.multimessage_object = new MultiMessageControllerPlugin(kag));
// 插件对象的创建和注册
@endscript
@endif
;
; マクロの登録
@macro name="multimsginit"
@eval exp="multimessage_object.initialization(+mp.num)"
@endmacro
@macro name="multimsgsetcurrent"
@eval exp="multimessage_object.setCurrentMessageLayer(+mp.current)"
@endmacro
@macro name="multimsgrelease"
@eval exp="multimessage_object.release()"
@endmacro
@macro name="multimsglock"
@eval exp="multimessage_object.lock=true"
@endmacro
@macro name="multimsgunlock"
@eval exp="multimessage_object.lock=false"
@endmacro
@macro name="multimsg"
@eval exp="multimessage_object.messageOutput(mp)"
@endmacro
@return
0