Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

scripts/ の記述パターン

道具の名前を覚えたら、次は使いこなしですわ。ここでは、実際の scripts/ でよく出てくる 書き方の「型」を集めましたの。シーン関数の定型、イベントの受け止め方、単語の一括投入―― どれも一度覚えれば一生もの。型をなぞるだけで、あなたのゴーストはぐっと賢くなりますわよ。


この章では scripts/ 配下のカスタム Lua スクリプト、および Pasta DSL 内の ```lua ``` ブロックで 頻出する記述パターンを示す。対象方言は LuaJIT 2.1(Lua 5.1 系)である。

scripts/ の役割と優先順位

ゴーストディレクトリ直下の scripts/ にユーザーカスタム Lua スクリプトを置く。main.lua が エントリーポイントとしてランタイムに読み込まれる。

  • scripts/(ゴーストカスタム)は pasta_scripts/(エンジン標準ランタイム)より優先される。 同名ファイルを置けばエンジン動作を上書きできる。
  • pasta_scripts/ は pasta.dll が起動時に自己展開するエンジン同梱スクリプトで、通常は触らない。

パターン 1: シーン関数の定型

すべてのシーン関数は act:init_scene(SCENE) で始まる。これが必須の定型である。 戻り値の save(セッション間で永続するデータ)と var(このアクション内だけの一時変数)を受け取る。

function SCENE.挨拶(act)
    local save, var = act:init_scene(SCENE)  -- 必須: save / var を取得
    act:talk(act.ぱすた.actor, "こんにちは!")  -- アクター名でトーク
    act:yield()                                 -- 蓄積トークンを送出
end
要素役割
act:init_scene(SCENE)シーン初期化。save(永続)と var(一時)を返す。必須
act:talk(actor, text)アクターのセリフを出力(さくらスクリプトへ自動変換)
act:yield()蓄積したトークンを送出し、トークの区切りを作る

save@pasta_persistence 管理の永続変数で、セッションをまたいで保持される。var は そのアクション実行中のみ有効な一時変数である。

function SCENE.カウント(act)
    local save, var = act:init_scene(SCENE)
    save.count = (save.count or 0) + 1      -- セッション間で累積
    var.temp = "一時データ"                  -- このアクション内のみ
    act:talk(act.ぱすた.actor, save.count .. "回目ですね")
    act:yield()
end

複数トークと表示制御

act のメソッドはチェーンできる。複数のセリフ区切りは yield() を複数回呼んで作る。

function SCENE.物語(act)
    local save, var = act:init_scene(SCENE)
    act:talk(act.ぱすた.actor, "最初のセリフ")
    act:yield()  -- ここで一区切り
    act:surface(5):wait(500):talk(act.ぱすた.actor, "驚いた!"):newline()
    act:yield()  -- 2区切り目
end

主な表示制御メソッド: surface(id)(サーフェス変更)、wait(ms)(ウェイト)、newline(n?)(改行)、 clear()(表示クリア)。いずれも self を返すのでチェーン可能。

選択肢

function SCENE.分岐(act)
    local save, var = act:init_scene(SCENE)
    act:talk(act.ぱすた.actor, "どうする?")
    act:choice("挨拶", "挨拶する")   -- ジャンプ先シーン名, 表示テキスト
    act:choice("自己紹介")           -- 表示テキスト省略時はシーン名を表示
    act:choice_timeout(30)           -- 30秒でタイムアウト
    act:yield()
end

選択肢が選ばれると、ランタイムが OnChoiceSelectEx を発火し、選択 ID(choice の第1引数)を シーン名として前方一致検索して該当シーンを実行する。通常はこの自動ルーティングに任せればよい。

パターン 2: イベントハンドラの登録

カスタム SHIORI イベント処理は REG テーブルにハンドラを登録し、RES でレスポンスを返す。

local REG = require("pasta.shiori.event.register")
local RES = require("pasta.shiori.res")

REG.OnBoot = function(req)
    local shell_name = req.reference[0]  -- Reference0
    return RES.ok("\\h\\s[0]起動しました。\\e")
end

REG.OnClose = function(req)
    local reason = req.reference[0]
    if reason == "user" then
        return RES.ok("\\h\\s[0]またね。\\e")
    end
    return RES.no_content()  -- 表示なしで処理完了
end

ハンドラは req(SHIORI リクエスト情報)を受け取る。主なフィールド:

フィールド説明
req.idイベント名("OnBoot" 等)
req.reference[N]Reference ヘッダ(0 始まり)。未送信時は nil
req.date日時情報
req.statusステータス("talking" 等)

レスポンス生成 API:

関数説明
RES.ok(value)200 OK + さくらスクリプト
RES.ok_with(headers)200 OK + 複数ヘッダ
RES.no_content()204 No Content(表示なし)
RES.err(message)500 エラー

REG 未登録時のフォールバック

REG にハンドラが無いイベントは、同名のグローバルシーンが自動的に検索・実行される。 つまり DSL で *OnBoot シーンを定義しておけば、REG.OnBoot を書かなくても起動時に呼ばれる。 凝った分岐が要るときだけ REG でハンドラを書く、というのが基本方針である。

OnTalk(ランダムトーク)と OnHour(時報)は、ランタイムの仮想ディスパッチャが OnSecondChange を起点に自動発行する。これらも対応するシーンを定義しておけば呼ばれる。

パターン 3: 単語の一括投入

数十〜数百件の単語を投入したいときは、DSL の単語定義より Lua のループが向く。 pasta.word モジュールのビルダーパターンを使う。

local WORD = require("pasta.word")

-- グローバル単語: ループで一括投入
local foods = { "ラーメン", "カレー", "寿司", "焼肉", "パスタ" }
local builder = WORD.create_global("好きな食べ物")
for _, food in ipairs(foods) do
    builder:entry(food)
end

-- メソッドチェーンでまとめて
WORD.create_global("挨拶")
    :entry("こんにちは", "やあ")
    :entry("ごきげんよう")

-- ローカル単語(シーン名スコープ)
WORD.create_local("メイン_1", "返事")
    :entry("はい", "ええ")
    :entry("そうね")

-- アクター単語(アクター名スコープ)
WORD.create_actor("ぱすた", "一人称")
    :entry("わたし")
    :entry("あたし")
ファクトリ関数スコープ
WORD.create_global(key)グローバル
WORD.create_local(scene_name, key)ローカル(指定シーン内)
WORD.create_actor(actor_name, key)アクター

entry(...) は可変長引数で値を追加し、self を返すのでチェーンできる。外部 JSON / YAML を @json / @yaml で読み込んでループ投入すれば、データ駆動の大規模辞書も構築できる。

パターン 4: グローバル関数の定義

DSL から呼び出せるユーザー定義関数は pasta.globalGLOBAL)テーブルに登録する。

local GLOBAL = require("pasta.global")

GLOBAL.時報 = function(act)
    return os.date("%H") .. "時です"
end
-- DSL から呼び出し: @*時報()  (グローバル関数は * 付きで呼ぶ)

DSL の @関数名() はローカル(SCENE.)呼び出し、@*関数名() がグローバル(GLOBAL.)呼び出しである点に注意する。


型は出そろいましたわ。あとはこれらを組み合わせて、あなただけのゴーストを織り上げるだけ。 でも――Lua で書くべきか、DSL で済ませるべきか、迷う場面もございましょう? 次の章で、その見極め方をきっちりお教えいたしますわ。さあ、最後まで参りましょう!