document

  • 辞書ファイルは分割できますか?


    できます。辞書ファイルは 1ファイルでもよいし、一般的な SHIORI サブシステムのように用途別に分けても構いません。ただ、いずれにしても各ファイルに書式の違いなどはなく、美坂は起動時に繋ぎ合せて読むだけです。


  • 対ゴーストコミュニケートは使用できますか?


    できます。対ゴーストコミュニケート構造はテンプレートゴーストに含まれており、僅かな書き換えで簡単に反応を増やしていくことが可能です。


  • 何らかのポイント蓄積システムを実現することはできますか?


    できます。ポイントカウント用の適当な変数を定義し、必要に応じて加減算し、if によって反応や分岐を変えれば完成します。なお、美坂において全てのユーザ変数は自動的に保存/復帰されるので、変数の長期的保持について気を使う必要はありません。


  • 最終更新日からの経過日数を計測するにはどうすればよいですか?


    美坂には %daysfromlastupdate というシステム変数があり、これを参照するだけで簡単に経過日数が取得できます。もちろん自力で日数を数えることも可能です。例は省略しますが‥‥


  • 初回起動日からの経過日数を計測するにはどうすればよいですか?


    美坂には %daysfromfirstboot というシステム変数があり、これを参照するだけで簡単に経過日数が取得できます。もちろん自力で日数を数えることも可能です。例は省略しますが‥‥


  • 同じネタを連続して喋ることを抑制できますか?


    できます。いわゆる「ネタ」は美坂においては辞書変数として宣言しますが、その宣言で nonoverlap パラメータを与えると、その辞書変数は一巡するまで絶対に同一の文字列を吐かなくなります。
    $_OnTalkCore; nonoverlap;
    \0\s0テンプレートA。\e
    \0\s0テンプレートB。\e
    \0\s0テンプレートC。\e
    \0\s0テンプレートD。\e
    \0\s0テンプレートE。\e
    \0\s0テンプレートF。\e
    
    上記のように宣言すると $_OnTalkCore は必ず重複なくループします。つまり $_OnTalkCore を6回連続で呼び出したとしても、例えば AFBECD、BFADCE 等のように、重複なく文字列が返されます。


  • 敬称つきの名前に正しく敬称を付与することはできますか?


    できます。美坂には $adjustprefix 関数があり、以下のコードで簡単に敬称問題を解決できます。

    {$adjustprefix({$Sender},"さん")}
    
    $Sender に敬称が付いてなかったときに限り、敬称「さん」が付与されます。これはインラインで書いてもよいですが、多用するなら以下のように関数化して用いるのがベストでしょう。

    $_Sender
    {$adjustprefix({$Sender},"さん")}
    
    また、同様の処理は低レベルな文字列処理関数を組み合わせても簡単に実現できます。わざわざ低レベルな関数を組み合わせてこの処理を行うメリットは何もありませんが、やればできるという事実は重要なことです。

    以下の関数 $_Sender は、$Sender の語尾の数バイトをチェックし、「ちゃん」でも「さん」でもないときに限って「さん」を付与し、戻り値を返します。
    $_Sender
    {
      {$temp={$Sender}}
      {$if (({$substringr({$temp},6)}!="ちゃん") 
        &&  ({$substringr({$temp},4)}!="さん")) {
          {$temp={$temp}さん}
        }
      }
      {$temp}
    }
    
    ちなみに 1行で書くと

    $_Sender
    {$temp={$Sender}}{$if (({$substringr({$temp},6)}!="ちゃん") && ({$substringr({$temp},4)}!="さん"))
     { {$temp={$temp}さん} }}{$temp}
    
    例えば $Sender が「がおちゃん」のとき、$_Sender は「がおちゃん」のままです。しかし、仮に $Sender が「がお」だった場合、$_Sender は「がおさん」になります。


  • マルチキャラクタや多重人格は実現できますか?


    できます。2状態(もしくはそれ以上の状態)を持つゴーストは基本の領域を越えるため、テンプレートゴーストにはこの構造が含まれていませんが、美坂にはマルチキャラクタを簡単に実現できる仕様があります。

    多モードの基本的な考え方は以下のようにして成り立っています。


    現在のモードを示す変数 $mode を $_Variable で定義。$mode==0 で通常、$mode==1 で裏とする。例示
    $_Variable
    {$mode=0}
    
    主要なイベントハンドラを全て2つずつ用意し、先に出るものに条件式 {$if ({$mode}==1)} を付与して分岐させる。例示
    $_OnTalk; {$if (({$mode}==1) && ({$random({$talkinterval})}==0))}
    \0\s0ランダムトークテンプレート0、裏。\e
    \0\s0ランダムトークテンプレート1、裏。\e
    
    $_OnTalk; {$if ({$random({$talkinterval})}==0)}
    \0\s0ランダムトークテンプレート0。\e
    \0\s0ランダムトークテンプレート1。\e
    
    $OnClose; {$if ({$mode}==1)}
    \0\s0終了、裏。\e
    
    $OnClose
    \0\s0終了。\e
    
    モードを切り替えるときは $mode に値を代入するだけでよい。その際、surface も適切に変更しないと見た目上の矛盾が出るので、実際には surface の初期化も同時に行う(と言ってもモード切替時には相応のセリフがあるはずなので、特段意識する必要はないと思われる)。例えば以下のようにする。
    $OnChoiceSelect; {$if ({$reference(0)}==change2mode0)}
    {$mode=0}\0\s0\1\s0通常モードに変化。\e
    
    $OnChoiceSelect; {$if ({$reference(0)}==change2mode1)}
    {$mode=1}\0\s0\1\s[50]裏モードに変化。\e
    
    ユーザ変数は自動保存/復帰されるので、次回起動時は前回終了時のモードで起動する。


    基本的な考え方は以上ですが、この基本的な考え方をそのままコード化すると、全イベントハンドラが2重化されて著しく可読性が低下します。さらに全イベントハンドラに if が付くことも深刻な問題で、全く同じ論理式が延々と繰り返され、非常に無駄が多くなることに加え、可読性も低下します。

    これを防ぐために以下の 2つの手法を使用します。


  • モード毎に別々のファイルに分ける。

    美坂のデータファイルは自由に分割できます。そこで、2モードあるなら、ファイルを例えば mode0.txt と mode1.txt に分け、それぞれのモードに依存したハンドラを各ファイルに配置します。ただしハンドラによってはどちらにも依存しない「全体で一つだけあればよい」ものもあり(例えば OnSecondChange 等)、徹底して分化しても逆に無駄が出る場合があるので、それらはモード共通のファイル global.txt に配置します。そして misaka.ini で以下のように宣言します。

    dictionaries
    {
    mode1.txt
    mode0.txt
    global.txt
    }
    
    二重化による可読性の低下の問題は以上で解決できます。


  • システムシンボル $_Common にモード判定論理式を記述する。

    多モードである以上イベント毎に現在のモードが何であるかを判定する処理が入ることは避けられず、そのため多モードキャラクタのイベントハンドラには全て {$if ({$mode}==0)} のような論理式が付くことになります。これは無意味で非効率的に感じられるでしょう。実際無駄です。これは以下のような記述で簡潔に記述できます。
    #_Common
    {$if ({$mode}==0)}
    
    ※mode1.txt では論理式は {$if ({$mode}==1)}

    このシンボルを mode0.txt および mode1.txt ファイル内に配置します。場所はどこでもよいですが、先頭付近に配置するのが分かりやすいでしょう。#_Common は特殊なシステムシンボルであり、ここで宣言された論理式は当該ファイル内の全てのシンボルに関係演算子 && で付与されます。つまり、#_Common でモード判定式を一度だけ記述すれば、以降そのファイル内の全てのシンボルにおいてモード判定式の記述は不要になります。

    機械的なモード判定によって不毛な論理式が膨大に増加する問題は以上で解決できます。

    このように、美坂におけるマルチキャラクタ/多重人格は、基本的に単にモード別にファイルを分けるだけで簡単に実現できます。


  • 複数属性を持つ単語を定義できませんか?


    できます。複数属性定義は単にシンボル名にその文字列を含めることで行い、参照には $search 関数を用います。例えば以下のように記述します。

    $_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 が返ります。

    search 関数は単純な文字列検索で判定を行うので、定義名は必ずしもセパレータの概念を持っている必要はありませんが(この例の場合はハイフン)、実用的に単語を管理するには結局ユーザ自身が何らかのセパレートルールを定める必要があるでしょう。


  • ユーザからの communicate に反応するにはどうすればよいですか?


    $_OnGhostCommunicateReceive がコールされることに変わりはありません。相手がユーザであるということは $Sender が User であるかどうかを判定することで分かります。

    $_OnGhostCommunicateReceive,{$if ({$sender}=="User")}
    \0\s0相手がユーザ。\e
    
    $_OnGhostCommunicateReceive,{$if ({$sender}=="花ちゃん")}
    \0\s0相手が花ちゃん。\e
    
    $_OnGhostCommunicateReceive
    \0\s0不明。\e
    

  • 同じ条件式を何度も書くことを避けられませんか?


    避けられます。例えば
    $OnBoot; {$if ({$mode}==1)}
    \0\s0起動、裏。
    
    $OnClose; {$if ({$mode}==1)}
    \0\s0終了、裏。
    
    $OnGhostChanged; {$if ({$mode}==1)}
    \0\s0ゴースト変化終了、裏。
    
    $OnGhostChanging; {$if ({$mode}==1)}
    \0\s0ゴースト変化開始、裏。
    
    このように、全く同じ式がデータファイル中に何度も出現するケースがあるとします。これは無意味で非効率的に感じられるでしょう。実際無駄です。これは以下のように効率的に書き換えることができます。
    $ismode1
    {$if ({$mode}==1)}
    
    $OnBoot; {$ismode1}
    \0\s0起動、裏。
    
    $OnClose; {$ismode1}
    \0\s0終了、裏。
    
    $OnGhostChanged; {$ismode1}
    \0\s0ゴースト変化終了、裏。
    
    $OnGhostChanging; {$ismode1}
    \0\s0ゴースト変化開始、裏。
    
    このように整理して記述することには様々なメリットがあります。


  • 1つのセンテンス中で同じランダム単語を2回以上使うにはどうすればよいですか?


    適当な一時変数を用いれば実現できます。例えば、以下の構文で $ms で引かれた名前を2度使用できます。
    {$temp={$ms}}\0\s0{$temp}‥‥\w8\w8あの{$temp}が、だよ?\e
    

  • 可読性を上げるためにレイアウト的な空行を入れたりコメントを入れたりすることはできますか?


    できます。
    $_OnTalk; {$if (({$mode}==0) && ({$random({$talkinterval})}==0))}
    
    // 貧乏ネタ
    
    \0\s0{$ms}の家計、2ヶ月前から火の車らしいよ。\w8\w8\1\s0むう。
    
    // 火事ネタ
    
    \0\s0{$ms}の家が燃えたらしいよ。\w8\w8\1\s0むう。
    
    上記の記述は、下記の記述と等価です。
    $_OnTalk; {$if (({$mode}==0) && ({$random({$talkinterval})}==0))}
    \0\s0{$ms}の家計、2ヶ月前から火の車らしいよ。\w8\w8\1\s0むう。
    \0\s0{$ms}の家が燃えたらしいよ。\w8\w8\1\s0むう。
    
  • 複数行にレイアウトしてスクリプトを書くことはできますか?


    できます。複数行で渡って一つのスクリプトを書く場合は、スクリプト全体をブレスでくくって記述します。
    $_OnTalk; {$if (({$mode}==0) && ({$random({$talkinterval})}==0))}
    {
    \0\s0{$ms}の家計、2ヶ月前から火の車らしいよ。\w8\w8
    \1\s0むう。
    }
    {
    \0\s0{$ms}の家が燃えたらしいよ。\w8\w8
    \1\s0むう。
    }
    
    上記の記述は下記の記述と等価です。
    $_OnTalk; {$if (({$mode}==0) && ({$random({$talkinterval})}==0))}
    \0\s0{$ms}の家計、2ヶ月前から火の車らしいよ。\w8\w8\1\s0むう。
    \0\s0{$ms}の家が燃えたらしいよ。\w8\w8\1\s0むう。
    
  • 複数行にレイアウトして構文を書くことはできますか?


    できます。複数行で渡って一つの構文を書く場合は、一つの構文をブレスでくくって記述し、独立したシンボルを与え、論理式部で参照を行います。

    $checkinterval
    {
      $if (({$mode}==0) 
      &&   ({$random({$talkinterval})}==0))
    }
    
    上記の記述は下記の記述と等価です。
    $_OnTalk; {$checkinterval}
    \0\s0{$ms}の家計、2ヶ月前から火の車らしいよ。\w8\w8\1\s0むう。
    \0\s0{$ms}の家が燃えたらしいよ。\w8\w8\1\s0むう。
    

  • 戻る