美坂

前言

原文:http://lovelove.rabi-en-rose.net/misaka/specification.html

撰文與翻譯:rexboy

美坂官方:http://lovelove.rabi-en-rose.net/index_misaka.php

這篇文章,是參照美坂本家規格書的架構跟原文,再加入一些補充的東西寫成的。

不過,畢竟這兒只是介紹美坂的基本語法,沒有整體提到Ghost的具體架構。所以各位可以搭配美坂所附的Ghost範本,會更容易上手些。

基本定義檔

misaka.ini是美坂的重要設定檔。內容大概長得像這個樣子:

dictionaries
{
misaka.txt
hoge.txt
datamisaka.txt
}

debug,0
debugsaori,0
error,0
propertyhandler,0

dictionarie之下的括弧內,必須舉出Ghost會使用到的辭書檔。 辭書檔裡面,就是我們要費心撰寫的Ghost資料了。包含人物的對話,選單,發生事件時該有的反應..等等,都是寫在辭書檔裡的。 只要把該有的對話,事件都做出來了,Ghost就可以正常運作。內容可以全部擠在同一個辭書檔內,也可以分成很多個。看個人的習慣而定。

debug決定是否要建立除錯紀錄檔。以bool值指定(0=否,1=是)。除錯檔名為misaka_debug.txt,位在 misaka.dll的同一個目錄下。美坂會把動作期間所有執行的步驟輸出到這個檔案裡面。所以當美坂突然結束或是當掉的時候,可以從這個檔案裡找出問題所在。檔案本身膨脹相當快,請小心使用。預設值為0。

debugsaori決定是否要建立saori的除錯紀錄檔。以bool值指定。關於Saori這裡先暫時略過。除錯檔名為 misaka_debugsaori.txt,位於misaka主要DLL的同一個目錄下。紀錄對saori提出的request和接收到的 response內容。預設值為0。

美坂在讀入的時候,有所謂的前置處理階段。error決定是否要在此時掃瞄一遍辭書檔的語法錯誤,並且將錯誤的部分整行輸出成misaka_error.txt。也是供除錯使用。以bool值指定,位於misaka主要DLL的同一個目錄下。預設值為0。

單純變數

美坂可以在任何時候使用新的變數,變數的型態由使用時決定。

關於變數有一些注意點:

  1. 某些情形下,文字可以直接被當成數值處理。不過當其以數值處理,卻又無法正確轉換時,會被當成0來看待。
  2. bool值可以用true跟false保留字來處理。
  3. 美坂只能處理正負整數,無法處理小數。
  4. 字串必須以雙引號括起來。 

變數值的代入可以使用下面的語法。代入時如果變數不存在,則產生新的變數。

單純代入

單純代入使用=運算子連結。例如:

{$z=128}
{$nyo="喵~"}

記得字串的前後一定要用雙引號括起來。

演算代入

我們也可以把計算結果代入變數。例如:

{$z=(1+1)*4}

在這裡就可以看出雙引號的重要性了。像下面的寫法,美坂就不會計算右邊式子的結果,而只把它當成普通的文字:

{$z="(1+1)*4"}

單純演算代入

這是簡化的寫法,對使用過某些程式語言的人或許很熟悉:

{$z++}
{$z--}
{$z+=n}
{$z-=n}
{$z*=n}
{$z/=n}

從上往下,依序是$z的值加一,$z的值減一,把$z的值加上n,把$z的值減掉n,把$z的值乘上n,以及把$z的值除以n。 評價(取值)

要取變數的值,可以使用下面的語法。

{$z}

可以在任何時候使用,也可以「巢狀」取值。例如:

{$z=({$a}+1)*{$b}}

有一些實用的系統函數可以用來操作變數。請參考〔系統函數〕的部分。 單純變數的值在結束時會被自動保存,並且在下次啟動時回復。

陣列變數

陣列變數也是可以自由定義的。除了內含有多個元素以外,基本上跟單純變數沒有太大差別。 在宣告階段,單純變數跟陣列變數的表示法沒有什麼不同。例如:

{$list=""}

是指定空的$list陣列變數。 要增加陣列變數的元素,可以使用append這個函數。

{$append($list,"あいうえお")}
{$append($list,"かきくけこ")}

當我們呼叫陣列變數的時候,如果不作任何指定,則傳回的值會從陣列中隨機選取。例如,經過上面兩行處理後,如果我們用{$list}取值的話,傳回的可能會是「あいうえお」,也可能會是「かきくけこ」。

如果要取用陣列中特定的元素,則必須使用[]符號。

{$list[1]}

這樣就一定會傳回「かきくけこ」了。請注意,陣列元素是從0開始的。

陣列的最大元素個數不限。有一些實用的系統函數可以用來使用陣列變數。請參考〔系統函數〕的部分。

陣列變數同樣會在結束時自動保存,再啟動時重新讀入。

辭書變數

辭書變數會在啟動時自動被讀入。辭書變數的構造類似陣列變數,不過增加了一些特殊性質:

  1. 唯讀。
  2. 可以有「採用條件式」。 

其餘的部分大致跟陣列變數相同。 辭書變數也可以使用系統函數來控制。不過,此時辭書變數會被當成普通的陣列變數看待,暫時失去部分的特性。 辭書變數的採用條件式中,可以包含的設定引數

辭書變數可以包含多個設定引數。這是和一般變數不同的地方。

邏輯運算式

含有邏輯運算式的辭書變數,就像是「符合條件才能使用的變數」一樣。這可以讓頻繁發生事件,有更多樣化的反應。寫法像以下這樣:

$OnBoot; {$if ({$hour}==12)};
\s\0起動、12時。\e
$OnBoot
\s\0それ以外。\e

像這樣,當時間是12點的時候,就會採用上面的辭書變數﹔其他情況,則採用下面的。利用這種特性,就可以簡單地讓同一個事件,在不同情況下,產生各式各樣的反應。

先被定義的辭書變數,會先被判斷。(距離檔案開頭比較近的﹔如果在不同檔案中的話,則以misaka.ini所引入的順序判定。)當結果為真,就直接取值傳回,不再繼續往下判斷。利用這種特性,就可以輕易的寫出出if-elseif-else的架構了。(因此,沒有條件式的辭書變數,必須擺在同類變數的最後一個。否則在它之下的同類辭書變數將永遠不會被呼叫到。)

當判斷引數是邏輯運算式的時候,不一定要使用$if結構。像下面的寫法也是正確的:

$Dummy; {$stringexists({$array},{$s})};
\s\0既にある。\e
$Dummy
\s\0ない。\e

nonoverlap

被指定nonoverlap的辭書變數,就不會重複傳回已經使用過的值。直到其中所有的元素都已經使用過一輪之後,才會再重複出現。這個性質通常使用於RandomTalk之中,防止重複的話題一直出現。例如:

$_OnTalkCore; nonoverlap;
A
B
C
D
E
F

$_OnTalkCore在使用的時候和一般的辭書變數沒有什麼不同。不過當我們連續呼叫它的時候,得到的結果可能會像是「A」「D」「B」「F」「E」「C」,或是「C」「F」「A」「D」「E」「B」,總之,在全部都使用過之前,它絕對不會重複出現。

sequential

被指定sqeuential的辭書變數,值會從第零個元素開始依序傳回。等全部的元素都傳回過之後,又回到第0個元素。例如

$_alphabet; sequential;
A
B
C
D
E
F

則連續呼叫$_alphabet得到的值會是A,B,C,D,E,F,A,B,C,D,E,F......。

所有的引數都可以重複使用。邏輯運算式跟nonoverlap等等也可以同時指定。舉一個重複指定的例子:

$OnBoot; {$if ({$hour}==12)}; nonoverlap;

不過,有些引數、邏輯運算式的組合會造成無效的情形。像是nonoverlap跟sequential兩個引數,因為動作內容重複,無法同時使用。

前置處理符號

#_Common

#_Common裡面所宣告的邏輯運算式,會和該檔案內所有的Symbol,以邏輯運算子&&連接起來。也就是說,只有在#_Common中設定的條件成立時,該辭書檔內的Symbol才會生效。通常對於含有多種模式的Ghost很有用。所以,我們不需要在檔案中寫好幾次的模式判定,只要把檔案分開就可以做到了。例如:

#_Common
{#if ({$mode}==0)}

則該檔案內的所有Symbol都會被加上邏輯運算式{$if ({$mode}==0)}。

系統Symbol

一般以On開頭的事件處理Symbol會自動成為系統Symbol。像是$OnBoot$OnGhostChange等等。為了方便使用,省略事件名稱以外的部分。

$OnNotify_*

當NOTIFY request產生的時候,美坂首先會將 Reference 參數適當的轉成系統變數,經由核心作最低限度的處理之後,會變成持有 $OnNotify_id Symbol的原始碼來執行。Reference參數在接受NOTIFY的情況下是會持續存在的。另外就NOTIFY request的性質而言,它並不會傳回任何的 Script ,而會直接被廢棄掉。

$_Variable

$_OnVariable會在美坂起動之後、使用者變數自動還原之前被呼叫。主要是用於設定全部變數的初值。不過這只在初次啟動時才有效。我們可以把它看待成全域變數的宣告區。雖然在美坂中,所有的變數都是不需要經過宣告的Variant(變異數)型態,不過我們可以像這樣先把變數訂出來,以增加整個辭書檔的可讀性。當然,這是選擇性的。

$_Constant

$_OnConstant會在美坂起動之後、使用者變數自動還原之後被呼叫。換句話說,它會無視於自動還原,強制設定變數的初值。可以當成全域的常數宣告區來看待。

$_OnGhostCommunicateReceive

接收到其他Ghost送來的對話時,就會被呼叫。要知道送來的訊息來自誰,或是有沒有說了哪些話的話,可以參考系統變數{$sender}和系統函數{$insentence},{$inlastsentence}。

$_OnRandomTalk

每經過一段時間,它會被定時呼叫。間隔時間是系統變數$_talkinterval指定的0.5到1.5倍。一般被稱為"Random Talk"。

系統變數(Passive)

下面的系統變數是由系統預先定義好的,並且一直保持自動更新。使用者不能再定義同名的環境變數。

$year / $month / $day / $hour / $minute / $second / $dayofweek

現在的年/月/日/時/分/秒/星期,星期從0到6依序代表星期日到星期六。

$elapsedhour / $elapsedminute / $elapsedsecond

Ghost的連續啟動時間。依序是時/分/秒。

$elapsedhouros / $elapsedminuteos / $elapsedsecondos

作業系統的連續啟動時間。依序是時/分/秒。

$elapsedhourtotal / $elapsedminutetotal / $elapsedsecondtotal

Ghost的總共啟動時間。依序是時/分/秒。

$os.version

作業系統版本。「5.0.2195」等等。

$os.name

作業系統一般的名稱。「Windows 2000」等等。

$os.phisicalmemorysize

實體記憶體總容量。單位byte。

$os.freememorysize

空的實體記憶體容量。單位byte。

$os.totalmemorysize

含虛擬記憶體的總記憶體容量。單位byte。

$cpu.vendorname

目前使用中的CPU廠牌名稱。「Intel」等等。

$cpu.name

目前使用中CPU一般的名稱。「Athlon」「PentiumIII」等等。

$cpu.clockcycle

目前使用中CPU的時脈。單位MHz。

$daysfromlastupdate

距離上一次線上更新的日數。單位為天。例如:

$_OnBoot; {$if (7<={$daysfromlastupdate})};
\s\0そろそろネットワーク更新して欲しい。\e
$_OnBoot
\s\0通常。\e

像這樣,就可以用來催促使用者經過七天就更新。

$daysfromfirstboot

第一此啟動後經過的日數。單位為天。

$lastsentence

最近一次COMMUNICATE時,由對方傳來的Script。也是$inlastsentence函式要比較的對象。

$otherghostlist

陣列變數。同一個桌面上存在的其他Ghost的列表。在只有自己的情況下是空值(NULL)。

$hwnd.sakura / $hwnd.kero / $hwnd.sakuraballoon / $hwnd.keroballoon

各自的HWnd值。

系統變數(Active)

下面的系統變數是由系統預先定義好的,作為某些特殊用途。使用者不能再定義同名的環境變數。

$to

Ghost間COMMUNICATE時,要傳送到的Ghost名稱。設定這個變數後,發言就會傳送到所指定的Ghost身上。例如

{$to=花ちゃん}s0こんにちは。e

$to在每次說話之後都會重新設定。

$_talkinterval

系統Symbol $_OnRandomTalk 的呼叫間隔。單位秒。

系統函數

下面的系統函數名稱是由系統預先定義好的,它們永遠會被判斷為函數。使用者不能再定義同名的變數。

評價函數

{$if [ifelement] { [true] } else {false}}

if 結構。在判斷邏輯算式[ifelement]的bool值後,傳回{true}或{false}大括弧中的內容。例如:

\s\0{$if (({$month}==12) && (({$day}==24) || ({$day}==25))) { クリスマス } else { ただの日 }}\e

如果現在時間是12月24日或25日的話,就會得到「聖誕節」﹔否則得到「其他」。

關係運算子可以使用 == <= >= < > != 邏輯運算子可以使用 && ||

處理true值跟處理false值的兩個部分都是可以省略的。當我們只省略else {false}的部分的時候,如果[ifelement]的值為false,則什麼東西都不會傳回,就像完全消失了一樣。不過,當{true}的部分也跟著一起省略的時候,$if會傳回[ifelement]的bool值。下面的例子全部都是正確的:

{$if (({$hour}==9) || ({$minute}==59))}にょーん。e
{$if (({$hour}==9) || ({$minute}==59)) { 不細工{$hour} } else { 不細工じゃない }}にょーん。e
{$if (({$hour}==9) || ({$minute}==59)) { 不細工{$hour} }}にょーん。e
{$if (({$hour}==9) || ({$minute}==59)) { 不細工{$if ({$hour}==9)} }}にょーん。e

下面有幾個注意事項:

每個式子必須以完全獨立的括弧括起來。

{$if ({$month}==12 && ({$day}==24 || {$day}==25))}

雖然在C語言中,這樣寫是可以被編譯器接受的,不過美坂不接受這種寫法。應該改成:

{$if (({$month}==12) && (({$day}==24) || ({$day}==25)))}

不過,當使用相同的關係運算子的時候,還是可以把多個邏輯算式分配在同一個括號裡面。像下面這樣:

{$if ({$month}==12 && {$day}==24 && {$hour}==0)}

空格只能增加不能省略。當空格不夠的時候,可能會造成美坂失控暴走。下面是錯誤示範:

{$if(({$month}==12) && (({$day}==24) || ({$day}==25))) {聖誕節} else {其他}}

基本函數

{$reference(n)}

傳回目前編號n的reference內容。

{$random(n)}

傳回0~n-1之間的亂數(值為整數)。

{$calc(expression)}

傳回expression表示的四則運算式的結果。算式可以有很多項,也可以使用括弧。例如:

{$calc(1+1)}
{$calc(1+1*2)}
{$calc((1+1)*2)}
{$a=1}{$b=2}{$c={$calc({$a}*5+{$b}*10)}}{$c}点。

可以使用的運算子

+ 加法
- 減法
* 乘法
/ 除法
% 取餘數
^ 次方

同樣不能計算小數點。除法的結果小數點以下無條件捨去。

字串函數

{$extractfilename(s)}

字串s會被當成檔案名稱,傳回去除路徑後的檔名。例如:

C:hogehoge.txt → hoge.txt

{$index(n,s)}

在字串s裡面尋找跟字串n完全符合的部分,並且傳回找到的開頭位置。單位byte。例如:

{$index("hum","human")}
{$index("hux","human")}
{$index("n","human")}

{$insentence(s,n,n,n,n....)}

比對字串s跟後面所有的n是否都有相符合的部分,傳回其bool值。引數個數不限。

因為引數的順序跟index函數相反,使用時請注意。

{$insentence("human","hum")}
{$insentence("human","hux")}
{$insentence("human","hum","uma","u")}
{$insentence("human","hum","hua")}

{$inlastsentence(n,n,n,n....)}

類似insentence的函數。比對lastsentence跟所有的n是否都有相符合的部分,傳回其bool值。

{$length(s)}

傳回字串s的長度(byte數)。

{$substring(s,offset,count)}

{$substringl(s,count)}

{$substringr(s,count)}

傳回字串s的一部分:
substring從字串s的第offset個字元,取count個字元。substringl從s的左端,取count個字元。
substringr從s的右端,取count個字元。單位皆為byte。

{$substringw(s,m,n)}

{$substringwl(s,n)}

{$substringwr(s,n)}

寬字元版的substring。字元數是由實際的文字數計算,而非byte。其餘皆和substring相同。

{$temp="あaいbうcえdお"}{$substringl({$temp},3)}
{$temp="あaいbうcえdお"}{$substringlw({$temp},3)}

上式的結果是「あa」、下式的結果是「あaい」。

{$substringfirst(s)}

{$substringlast(s)}

類似substringw的函數。substringfirst是取s的前面一個字,而substringlast是取s的最後一個字。函數考慮MBCS(由實際的文字數計算,而非byte)。

{$hiraganacase(s)}

日語專用的函式。將字串s中所有的平假名轉成片假名。漢字等不能轉換的部分會被保留。當然這無法適用於big5日文..(汗)

{$isequallastandfirst(s0,s1)}

從\s\0的末端和s1的開頭,各取一個字出來比較,傳回兩者是否相同的bool值。函數考慮MBCS(由實際的文字數計算,而非byte)。

{$getvalue(n,m)}

依照字串n中的逗號,將n分割成數個元素,並傳回其中的第n個。例如:

{$_ref="a,b,c,d,e,f"}{$getvalue("{$_ref}",3)}

這個式子的值是d。

{$getvalueex(n,m)}

依照字串n中單位元的字,將n分割成數個元素,並傳回其中的第n個。

陣列函數

{$append(a,s)}

新增一個元素s到陣列變數a裡面。當a只是單純變數的時候,會被自動轉換成陣列變數。
此時a原先的值被保留在a[0]中,s則新增到a[1]裡面。

{$copy(a0,a1)}

把陣列變數a0的值複製到a1中。a0也可以是辭書變數,不過複製時,採用條件式會被忽略。且先找到的辭書變數先採用。

{$count(a)}

傳回陣列 a的元素個數。當a是單純變數時,傳回值恆為1。當a為陣列中的元素,或是其值為NULL時,則傳回-1。

{$pop(a)}

隨機傳回陣列變數a中的一個元素,並且將該元素從陣列中刪除。陣列為空時則傳回空字串。

{$popmatchl(a,s)}

隨機傳回陣列變數a中,其左方和s相同的元素。並且將該元素從陣列中刪除。沒有相同元素時則傳回空字串。

{$stringexists(a,s)}

尋找陣列變數a中,是否有跟s完全相同的字串元素。傳回其bool值。

SAORI函數

$loadsaori(filename)

讀入filename所指定的SAORI DLL並鎖定。

$unloadsaori(filename)

關閉filename所指定的SAORI DLL,並且解除鎖定。

$saori(filename,arg0,arg1,arg2....)

執行filename所指定的SAORI,並且以arg為argument header傳入。直到SAORI傳回response之前,都不會繼續往下動作。讀取不到指定的DLL時,或者SAORI內部發生錯誤時中止。例如:

{$loadsaori("substr.dll")}
{$temp={$saori("substr.dll","あいうえお",2,3)}}{$temp}
{$unloadsaori("substr.dll")}

假設這是里々內附的substr.dll。$temp的值會被設為「いうえ」。

上面的例子是在讀取完後立刻執行,執行完就立刻關閉。不過,並沒有特別限制一定要這樣做。 在美坂起動的同時讀取(例如$_Constant等),一直存放在記憶體中,隨時呼叫也是可以的。 存活期由使用者自由決定。

一般來說,在美坂啟動的同時,讀入全部要用到的SAORI,也不會有什麼問題。這樣做有時可以省去一些麻煩。 另外,在美坂結束的時候,也會自動關閉所有的SAORI。所以不需要太擔心。 其他的函數

$choice(n,n,n,n,n,n....)

隨機傳回一個引數。可以使用的引數個數不限。例如:

{$choice("あいうえお",{$b},256)}

$isghostexists(n)

判斷字串n指定的Ghost是否在同一個桌面上。傳回bool值。

$search(n,n,n,n....)

尋找n所指定的元件名稱,並隨機選取一個,取其值傳回。例如:

$_OnTest
{$search("human","japan")}
$_OnTest2
{$search("male")}
$human-male-japan
A

$human-female-japan
B

$human-male-korea
C

$human-female-korea
D

$_OnTest會傳回A或B,$_OnTest2會傳回A或C。

$backup()

沒有引數。用來備分美坂在記憶體裡全部的資料。動作內容相同於美坂結束時的自動變數保存。

$getmousemovecount(n,m)

傳回角色n(0=Sakura,1=Unyu)的m部位的OnMouseMove次數。通常用來控制「撫摸反應」的敏感度。例如:

$OnMouseMove,{$if (64<={$getmousemovecount(0,"Bust")})}
{$resetmovecount(0,"Bust")}\s\0被摸胸部的反應。\e
$OnMouseMove,{$if (32<={$getmousemovecount(0,"Bust")})}
\s\0懷疑被摸胸部的反應。\e

附註:部位的名稱是由Shell指定的。請參考製作Shell的相關文件。

resetmousemovecount(n,m)

重設角色n(0=Sakura,1=Unyu)的m部位的OnMouseMove次數。

註解

在一行的開頭加上//,則整行的內容會被視為註解,內容完全忽略。
不過,不能在一行的中間或末尾使用。
執行時,註解不會佔去多餘的記憶體或是拖慢處理速度。

暗號化(編碼)

執行misakac.exe可以把同一個目錄下的 *.txt 檔案全部暗號化,輸出成 *.__1。 .__1不再像一般的文字檔案,能輕易觀看到其原始內容,不過它還是可以當成普通的辭書檔,在misaka.ini中引入。

這個功能,通常是用來保密辭書檔的原始內容。當然,也無法完全保證不會被別人看到。

因為misakac.exe會對同目錄下所有 .txt的檔案動作,所以可能會一併把readme.txt等等無關的檔案暗號化。雖然原始檔案會被保留,不過操作時請稍微留意一下。 SAORI

請參考系統函數的SAORI函數部分。

關於SAORI的詳細資料,這裡暫時省略。 全體的傾向,注意事項,小常識等 要區分大小寫

例如,$z和$Z會被視為不同的變數來處理。 資料必須全部為Shift_JIS

所有的雙位元文字必須使用Shift_JIS碼。 不過目前使用Big5碼來撰寫還沒有發現嚴重的問題。 從0開始計數

大部分的序數資料由0開始計數。 字串必須以雙括號括住

例如:

{$_ref="a,b,c,d,e,f"}

不可以這樣寫:

{$_ref=a,b,c,d,e,f}

寫法錯誤的時候會關閉

錯誤發生時通常會出現錯誤訊息,然後直接關閉。期間無法做任何其他動作。 大括弧{}有「評價(取值)」的意思

例如我們寫$hour,它就只會單單印出$hour這一串字而已。只有寫成{$hour}的時候,它才會取 $hour 的值並且傳回。

{$lasthour="$hour"}
{$lasthour={$hour}}

在上面那一行,$lasthour只會變成字串﹔而在下面那一行,$lasthour會變成$hour的值。

也有些函數要傳遞變數本身才能運作,此時就不需要用大括號取值。例如使用$append增加陣列元素的時候:

{$append($z,"あいうえお")}

就不能寫成下面這樣:

{$append({$z},"あいうえお")}

因為這個時候需要用變數本身來處理,而不是它的值。 用來指示區塊開始/結束的大括弧必須自成一行。

例如,這是錯誤的寫法:

$_OnTest {
\0\s0\1\s0
\0ねえ。\w8\w8
\1\s0ん?\w8\w8
\0\n\n‥‥\w8\w8いや、いい。\w8\w8
\1\n\n‥‥\w8\w8何だよ。
\e
}

應該改成這樣:

$_OnTest
{
\0\s0\1\s0
\0ねえ。\w8\w8
\1\s0ん?\w8\w8
\0\n\n‥‥\w8\w8いや、いい。\w8\w8
\1\n\n‥‥\w8\w8何だよ。
\e
}

首頁   編輯 封鎖 差異 備份 上傳檔案 複製 變更名稱 重新載入   新建條目 一覽 搜索 最近的變更   幫助   最近更新的RSS
Last-modified: 2011-11-02 (三) 13:54:51 (4237d)