ユーザー定義ステートメント link

ユーザー定義ステートメント (Creator-Defined Statements CDS) は独自のステートメントを Ren'Py に追加出来るようにします。これにより現在の文法ではサポートされない機能が追加出来ます。

CDS は直接的な同等の Python コードより柔軟です。

例えば、ランダムに1行の台詞を所得します :

label introduction:
    python:
        greetings = ['Hello.', 'Welcome.', 'Can I help you?']
        greeting = renpy.random.choice(greetings)

    "[greeting]"

Ren'Py のパーサーでは Python ブロック内で何が起こり、どのように実行されるはずか予め分かりません。実行までこのコードにできることはなく、例外が発生するとエラーとなります。

CDS を使用すれば次のことができます。 :

  • パースされた文法の妥当性確認 (例えば、 renpy.random.choice に送られるリストの要素が妥当なテキストか確認します)

  • 実行時の不正なデータの無視 (クリティカルでない関数に対しては、例外を投げるよりも実行をスキップする方がしばしばよいです)

  • Displayable の予測 (関数が使用する場合)

  • Lint 実行中の追加情報の掲示 (実行時エラーが無視されるなら、ここでレポートとして受け取られます)

例えば、上記のような動作は CDS では次のように書けます。 :

python early:
    def parse_random(lexer):
        subblock_lexer = lexer.subblock_lexer()
        choices = []

        while subblock_lexer.advance():
            with subblock_lexer.catch_error():
                statement = subblock_lexer.renpy_statement()
                choices.append(statement)

        return choices


    def next_random(choices):
        return renpy.random.choice(choices)


    def lint_random(parsed_object):
        for i in parsed_object:
            renpy.check_text_tags(i.what)


    renpy.register_statement(
        name="random",
        block=True,
        parse=parse_random,
        next=next_random,
        lint=lint_random,
    )

random がステートメントとして利用できるようになります。 :

label introduction:
    random:
        "Hello."
        "Welcome."
        "Can I help you?"

CDS の使用は実行の安全を保証しませんが、あなたのステートメントのコードが良ければ、その分 Ren'Py はあなたがそれに何を期待しているか "理解" できます。

使用方 link

ユーザー定義ステートメント (CDS) は次のルールに準拠しなければなりません :

  • python early ブロックで定義されなければならない

  • ユーザー定義ステートメントはそれが定義されたファイル内では使用不能です。

  • CDS を含むファイルはそれを使用するどのファイルよりも先にロードされる必要があります( Ren'Py のファイルロードはそのパスのユニコーン順なので、 CDS を含むファイル名に 01 やその他小さな数値の接頭辞を付けると良いです)

ユーザー定義ステートメントは renpy.register_statement() 関数を使用して登録されます。この関数は CDS 内の処理を実行する他の関数を受け取ります。

ここでは新しいステートメント line を作成し、クォーテーションを付けずにテキストを指定できるようにしています。

line e "These quotes will show up," Eileen said, "and don't need to be backslashed."

parse 関数には、パースのための字句解析されたコンテンツが送られます。 execute 関数はパースされたコンテンツに対して操作を実行するべきです。 lint 関数は、パースされたコンテンツにエラーがあればそれを報告します。

python early:
    def parse_smartline(lexer):
        who = lexer.simple_expression()
        what = lexer.rest()
        return (who, what)

    def execute_smartline(parsed_object):
        who, what = parsed_object
        renpy.say(eval(who), what)

    def lint_smartline(parsed_object):
        who, what = parsed_object
        try:
            eval(who)
        except Exception:
            renpy.error("Character not defined: {}".format(who))

        tte = renpy.check_text_tags(what)
        if tte:
            renpy.error(tte)

    renpy.register_statement(
        "line",
        parse=parse_smartline,
        execute=execute_smartline,
        lint=lint_smartline,
    )

API リファレンス link

renpy.register_statement(name, parse=None, lint=None, execute=None, predict=None, next=None, scry=None, block=False, init=False, translatable=False, execute_init=None, init_priority=0, label=None, warp=None, translation_strings=None, force_begin_rollback=False, post_execute=None, post_label=None, predict_all=True, predict_next=None) link

これはユーザー定義ステートメントを登録します。

name

これはステートメントを開始する名前のスペース区切りのリストか、または空の文字列にして新しいデフォルトステートメントを定義します ( デフォルトステートメントは say ステートメントを置き換えるでしょう )。

block

False なら、ステートメントはブロックを使用しません。 True ならブロックを使用し、 そのブロックの解釈は lexer に任せます。文字列 "script" なら、ブロックは一つ以上の Ren'Py スクリプト言語を含むものとして解釈されれます。文字列 "possible" なら、パース関数でブロックを使用するか決定します。

parse

これは Lexer オブジェクトを引数に受け取る関数です。この関数はステートメントを解析し、任意のオブジェクトを返すべきです。このオブジェクトは他のすべての関数に引数として渡されます。

lint

これはステートメントをチェックするために呼び出され、 parse から返されたオブジェクトを引数として渡されます。 renpy.error() を呼び出してエラーを報告するとよいでしょう。

execute

これはステートメントが実行されると呼び出される関数で、 parse から返されたオブジェクトを引数として渡されます。

execute_init

これは初期化時の優先度 0 に呼び出される関数です。

predict

これはステートメントに使用される画像を予測するために呼び出される関数で、 parse から返されたオブジェクトを引数として渡されます。 ステートメントに使用される displayable のリストを返すべきです。

next

これは次のステートメントを決定するために呼び出される関数です。

block が "script" でないなら、これには parse 関数から返されたオブジェクトが引数に渡されます。 block が "script" ならそのブロックの最初のステートメントの名前のオブジェクトが追加の引数に渡されます。

この関数はジャンプ先のラベルを指定する文字列、ブロックへ制御を移すなら第二引数、この後のステートメントに制御を移すなら None を返すべきです。

label

ステートメントのラベルを決定する関数です。返した文字列がステートメントのラベルとして使用され、その他のラベル同様に呼び出しやジャンプが出来ます。

warp

ステートメントがワープ中に実行されるかを決定する関数です。関数が存在し、 True を返せばワープ中に実行され、そうでなければ実行されません。

scry

Ren'Py 内部で使用されます。

init

このステートメントが初期化時にのみ実行されるべきなら True です ( ステートメントが init ブロック内になければ自動的に init 0 ブロックに配置されます)。これは execute_init 関数に加えて execute 関数を呼び出します。

init_priority

init ブロックの初期化優先度の整数値です。

translation_strings

パースされたブロックを引数に呼び出される関数で、翻訳可能として報告される文字列のリストを返します。

force_begin_rollback

これは menucall screen のようなファストスキップを停止するステートメントでは True にするべきです。

post_execute

次のステートメントの一部として実行される関数です( post_execute 関数の追加により RPYC ファイルの内容が変化するため、強制コンパイルが必要になります )。

post_label

post_execute ステートメントのラベルを決定する関数です。返した文字列がステートメントのラベルとして使用され、その他のラベル同様に呼び出しやジャンプが出来ます。これを使用して他と被らないリターンポイントを作成できます。

predict_all

True なら、このステートメントと次のステートメントのすべての sub-parse を予測します。

predict_next

これはこのステートメントの次のステートメントのラベルを引数とする関数です。

これは次に実行するステートメントを予測するために呼び出されるべきです。ラベルか SubParse オブジェクトのリストを返します。 predict_all が True だと呼び出されません。

Lexer オブジェクト link

Lexer オブジェクトのインスタンスを受け取ってカスタムステートメントをパースする関数です。

class Lexer link
error(msg) link
パラメータ:msg (str) -- 検出したパースエラーのリストに追加されるメッセージです。

検出されたパースエラーのリストに(現在位置とともに) msg を追加します。これは現在のステートメントのパースを停止しますが、以降のパースは妨げません。

require(thing, name=None) link

thing のパースを試み、出来なければエラーを返します。

thing が文字列なら、 match() を使用してそれをパースしようとします。

そうでなければ、 thing はこの lexer オブジェクトの引数無しで呼び出される他のメソッドでなければいけません。

name が指定されなければ、メソッド名が (文字列であるなら thing が) メッセージに使用され、そうでないければ name が使用されます。

eol() link
戻り値:lexerが行の末端に達していれば True を返し、そうでなければ False を返します。
戻り値の型:bool
expect_eol() link

行の末端に達していなければエラーを投げます。

expect_noblock(stmt) link

このステートメントがブロックを使用しないと示すために呼び出されます。ブロックが発見されると、例外が投げられます。 stmt はエラーとともにメッセージに追加される文字列です。

expect_block(stmt) link

このステートメントが空でないブロックを必要とする示すために呼び出されます。 stmt はエラーとともにメッセージに追加される文字列です。

has_block() link
戻り値:現在行に空でないブロックがあれば True 、そうでなければ False です。
戻り値の型:bool
match(re) link

任意の正規表現文字列にマッチします。

何かをマッチさせる lexer のすべてのステートメントは、この関数と同様な方法で実装されています。最初に空白をスキップし、その行に対してマッチするかを試みます。マッチが成功すればマッチしたテキストが返され、そうでなければ None が返され、 Lexer は変更されません。

keyword(s) link

キーワードとして s にマッチします。

name() link

名前にマッチしますが組み込みのキーワードにはマッチしません。

word() link
戻り値:マッチされた単語のテキストです。
戻り値の型:str

キーワードを含むどのような単語にもマッチします。

image_name_component() link

画像名にマッチします。単語と違い画像名は数字で始められます。

string() link

文字列にマッチします。

integer() link
戻り値:整数を含む文字列
戻り値の型:str

整数にマッチします。

float() link
戻り値:浮動小数点を含む文字列
戻り値の型:str

浮動小数にマッチします。

label_name(declare=False) link

ラベル名または、absolute, relative にマッチします。 declare が True なら、グローバルラベルが設定されます ( ステートメントは label 関数からラベルを返す必要あるためにそうしているだけで、これはは実際にはラベルを宣言しないことに注意してください)。

simple_expression() link

単純式にマッチし、それを文字列として返します。これはしばしば変数名を期待する時に使用されます。結果の変更は推奨されません。正しいアクションは future(訳注: python3互換モジュールのこと?) で結果を評価することです。

delimited_python(delim) link

':' のような delim で終わる Python 式にマッチします。これはしばしばデリミタまでを条件として期待する場合に使用されます。結果の変更は推奨されません。正しいアクションは future(訳注: python3互換モジュールのこと?) で結果を評価することです。デリミタの前に行端に達すればエラーを投げます。

arguments() link

これは引数リストの丸括弧の前に呼び出されなければなりません。引数リストが確認されなければ None を返し、そうでなければ引数を表すオブジェクトを関数呼び出しに渡します。このオブジェクトには scope 辞書を省略可能な引数にとる evalute メソッドがあり、1番目が位置引数のタプル、二番目がキーワード引数の辞書であるタプルを返します。

rest() link

空白をスキップし、行の残りを返します。

checkpoint() link

現在の lexer の状態を表現する opaque オブジェクトを返します。

revert(o) link

o が checkpoint() から返されたオブジェクトなら、 lexer の状態を checkpoint() が呼び出されたときにまで戻します ( これはバックトラッキングのために使用されます )。

subblock_lexer() link
戻り値:現在行と対応するブロックに対する Lexer
advance() link

サブブロック lexer 内で次の行に解析を進めます。一行目を解析できるようにするために、一行目よりも前で呼び出される必要があります。そのブロック内の行に進めれば、 True を返し、末端を越えていれば False を返します。

renpy_statement() link

Ren'Py スクリプトステートメントとして現在行をパースし、不可能ならエラーを生成します。このメソッドは get_next() から返されたり renpy.jump()renpy.call() に渡される opaque オブジェクトを返します。このオブジェクトはステートメントのパース結果としてを除いて保存されるべきではありません。

ステートメントがこの処理を完了すると、制御はユーザー定義ステートメントの次のステートメント( post_execute を使用して作成されたステートメントもありえます)に渡されます。

renpy_block(empty=False) link

これは現在ブロックの残りの行を Ren'Py スクリプトとしてパースし、ブロックの最初のステートメントに対応する SubParse を返します。そのブロック内で実行されるすべてのステートメントとともにブロックが処理されると、制御はこのユーザー定義ステートメントの次のステートメントに渡されます。

これは現在のブロックをパースすることに注意してください。。現在のステートメントのサブブロックをパースしたい場合は、以下のようにしてください。

def mystatement_parse(l):

    l.require(':')
    l.expect_eol()
    l.expect_block("mystatement")

    child = l.subblock_lexer().renpy_block()

    return { "child" : child }
empty

True なら、空のブロックもパースされます( 空のブロックは pass ステートメントのみのブロックに相当します)。

False なら、空のブロックでエラーとなります。

catch_error() link

これは with ステートメントと共に使用され、そのコンテキストブロック内で Lexer エラーをキャッチしてレポートし、次のブロックに続ける context decorator です。

こちらは1つのサブブロック内での複数のエラーをどのようにレポートするかの例です。

def mystatement_parse(l):

    l.require(':')
    l.expect_eol()
    l.expect_block("mystatement")

    strings = [ ]
    ll = l.subblock_lexer()

    while ll.advance():
        with ll.catch_error():
            strings.append(ll.require(ll.string))
            ll.expect_noblock("string inside mystatement")
            ll.expect_eol()

    return { "strings" : strings }

Lint 用ユーティリティー関数 link

これらの関数は lint 関数を記述するのに便利です。

renpy.check_text_tags(s) link

s 内部のテキストタグが正しいかチェックします。エラーがあればエラー文字列を、なければ None を返します。

renpy.error(msg) link

ユーザーに対して文字列 msg をエラーとして表示します。

renpy.try_compile(where, expr, additional=None) link

式をコンパイルし、エラーがあればそれを lint.txt に書き出します。

where

式が見つかる場所を指定する文字列です。 "whereexpr を評価できません。 " のような形式でエラーメッセージを生成するために使用されます。

expr

コンパイルを試みる式です。

additional

指定すると、エラーメッセージへの追加情報となる行になります。

renpy.try_eval(where, expr, additional=None) link

式の評価を試み、 失敗すると lint.txt にエラーを書き込みます。

where

式が見つかる場所を指定する文字列です。 "whereexpr を評価できません。 " のような形式でエラーメッセージを生成するために使用されます。

expr

評価を試みる式です。

additional

指定すると、エラーメッセージへの追加情報となる行になります。