ユーザー定義ステートメント 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.error(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, execute_default=None, reachable=None) link

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

name

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

block

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

parse

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

lint

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

execute

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

execute_init

これは初期化時に優先度0で呼び出される関数で、 parse から返されたオブジェクトを引数として渡されます。

predict

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

next

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

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

この関数はジャンプ先のラベルを指定する文字列、ブロックへ制御を移すなら第二引数、この後のステートメントに制御を移すなら None を返すべきです。 parse function. 関数内で呼び出されれば Lexer.renpy_statement() または Lexer.renpy_block() の結果も返せます。

label

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

warp

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

scry

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

init

このステートメントが初期化時にのみ実行されるべきなら True にします ( ステートメントが init ブロック内になければ自動的に init ブロックに配置されます)。

ステートメントを init ブロックに含めると、execute_initexecute 関数が同時に呼び出されるため、 execute_init 関数があるなら恐らくこれは望まないでしょう。

translatable

True に設定すると、このステートメントは translation ブロック、一般的には後続のsay ステートメントを含むブロックに含まれるようになります。これは1行のステートメントに対してのみ true に設定されるでしょう。これは nvl clearvoice のような、ダイアログで変更する必要のあるステートメントに使用されます。

init_priority

initexecute_init 関数によって作成される 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 だと呼び出されません。

execute_default

これは初期化後、ゲーム開始前に default ステートメントが実行されるのと同時に呼び出される関数です。セーブのロード時、ロールバック後、 lint 前、その他の場合もあります。

これは parse から返されたオブジェクトを引数に呼び出されます。

reachable

これは、呼び出すとこのステートメントが lint の到達可能性の解析にどのように参加するかをカスタマイズできる関数です。

デフォルトでは、ステートメントのカスタムブロック、 Lexer.renpy_block() で作成されたサブパースブロック、ステートメントの後のステートメントは、そのステートメント自体が到達可能であれば到達可能です。また、ステートメントがラベル関数を持つ場合も到達可能です。

これには reachable 関数を提供してカスタマイズできます。これは 5 つの引数を取る関数です ( 以下、 「ラベル」 は文字列でも不透明なオブジェクトでもかまいません) :

  • parse 関数から返されたオブジェクト

  • ステートメントが到達可能ならば True である真偽値

  • ステートメントのラベル

  • 次のステートメントのラベルまたは次のステートメントがないときは None

  • block が "script" に設定されていれば、そのブロックの最初のステートメントのラベル、そうではなくブロックがないときは None

次を含む set を返すと期待されます :

  • 到達可能なステートメントのラベルまたは subparse オブジェクト

  • True でこのステートメントが lint で報告されるべきでないと示しますが、本質的には到達可能ではありません(他のステートメントで到達可能と報告されると到達可能になります)。

  • None なら無視されます

この関数は、 is_reachable の両方の値で複数回呼び出せ、ステートメントが到達可能かどうかに基づいてその動作をカスタマイズできます ( 例えば、次のステートメントは、このステートメントが到達可能である場合にのみ到達可能である可能性があります)。

警告

空文字列を名前として使用した say ステートメントの再定義は、通常、悪い考え方です。なぜならば Ren'Pyのネイティブステートメントを置き換えると、その動作は ステートメントに相当するもの に依存することになるからです。say ステートメントの場合、これらの等価物は id と翻訳システムをサポートしていません。つまり、デフォルトのステートメントを再定義したゲームでは、これらの機能を(完全に再実装しない限り)使用できません。

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 スクリプトステートメントとして現在行をパースし、不可能ならエラーを生成します。このメソッドは renpy.register_statement() から渡された 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, check_unclosed=False) 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

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