WMLを使っていると時々、Luaという言葉を目にすることがあると思います。
それほど複雑で無いアドオンを作っているうちはLuaについて気にする必要はありませんが、慣れてくるとWMLの機能に不満が出てくるかもしれません。そんな時に解決法になるかもしれないのがLuaコードです。WMLとは構文も違いますが、よりフレキシブルなイベントの作成が可能なようです。この記事がLuaとは何か理解するための助けになればいいのですが・・・
このトピックの元記事は本家wikiの「LuaWML」です。ここでは記事の冒頭の部分しか訳していませんが、本記事ではLuaで使用できるコマンドの詳しい情報が網羅されています。
原文で読みたい方はこちら
LuaWMLの概要
The [lua] tag
このタグはActionWMLの一部であり、[event]内やActionWMLを使用できる場所で使用できます。 これは、Lua 5.2言語でアクションを書くことを可能にします。
このタグを[scenario] ScenarioWMLの中に入れることも可能です。これらのタグは、シナリオプリロードイベントが発生する前であっても、luaエンジンのロード時に即座に実行されます。
(バージョン1.13.0 以降のみ)[lua]は[era]と[modification]でも使用できるようになりました。 [lua]タグは[era]や[modification]の中で[event]のように使われたときに[scenario]/[multiplayer]にコピーされます。
タグはコードキーのみをサポートします。これはLuaスクリプトを含む文字列です。 Luaは引用符と{と}の記号を使用するので、プリプロセッサがマクロ展開とトークン化を実行するのを防ぐため、スクリプトをより強力な引用符で囲むことをお勧めします。
[lua] code = << wesnoth.message "Hello World!" >> [/lua]
Luaカーネルはコマンドモードからもアクセスできます:
:lua local u = wesnoth.get_units({ id = "Konrad" })[1]; u.moves = 5
[args]サブタグを使用して、可変のローカル変数 "..."を介してWMLオブジェクトをスクリプトに渡すことができます。
[lua] code = << local t = ...; wesnoth.message(tostring(t.text)) >> [args] text = _ "Hello world!" [/args] [/lua]
Global environment (グローバル環境)
シナリオのすべてのLuaスクリプトは、同じ global environment(別名Lua状態)を共有します。 たとえば、イベントで定義された関数は、その後に発生するすべてのイベントで使用できます。
[event] name = preload first_time_only = no [lua] code = << function narrator(t) -- Behave like the [message] tag. wesnoth.fire("message", { speaker = "narrator", message = t.sentence }) end >> [/lua] [/event]
[event] name = turn 1 [lua] code = << narrator(...) >> [args] sentence = _ "Hello world!" [/args] [/lua] [lua] code = << narrator(...) >> [args] sentence = _ "How are you today?" [/args] [/lua] [/event]
上記の例では、冗長構造がマクロの背後に隠れる可能性があります。 むしろ、単純に新しいWMLタグを定義する方が良いかもしれません。
[event] name = preload first_time_only = no [lua] code = << -- The function is now placed in the wesnoth.wml_actions table -- The tag is [narrator], same as the function name function wesnoth.wml_actions.narrator(t) -- Behave like the [message] tag. wesnoth.fire("message", { speaker = "narrator", message = t.sentence }) end >> [/lua] [/event]
[event] name = turn 1 [narrator] sentence = _ "Hello world!" [/narrator] [narrator] sentence = _ "How are you today?" [/narrator] [/event]
グローバル環境はセーブ/ロードサイクルにわたって保存されません。 したがって、グローバル環境に値を格納することは、一般的には悪い考えです。
グローバル変数(関数定義を含む)を割り当てる唯一のタイミングは[シナリオ]直下の[lua]ブロックに直接、またはプリロードイベントの中です。 それはこのイベントは常に実行されるためです。したがって、その時点で定義されたヘルパー関数は、後のすべてのスクリプトで使用できるようになります。
グローバル環境には、basic(名前なし)、string、table、および math のモジュールが含まれています。wesnothモジュールも利用可能で、C ++エンジンへのアクセスを提供します。
さらに、osライブラリからのクロック、日付、時刻、およびdifftime関数(マルチプレイヤーおよびリプレイセーフではないことに留意してください)、バッグライブラリからのトレースバックも利用できます。
スクリプトの開始時にある可変のローカル変数 ...(3つの点)は、WMLデータを表す代理テーブルです。 このテーブルは[lua]タグの[args]サブタグの内容です(存在する場合)。
Examples
次のWMLイベントは、Wesnothのチュートリアルから取り上げたものです。 これは、LuaスクリプトがWesnothにどのように埋め込まれているかを示す例として役立ちます。 このイベントは、WML変数target_hexに設定されていないタイルに、サイド1のユニット(つまり、ユーザーによって制御されるヒーロー)が移動するたびに発生します。
# General catch for them moving to the wrong place. [event] name=moveto first_time_only=no [allow_undo][/allow_undo] [filter] side=1 [/filter]
[if] [variable] name=target_hex.is_set equals=yes [/variable] [then] [if] [variable] name=x1 equals=$target_hex.x [/variable] [variable] name=y1 equals=$target_hex.y [/variable] [then] [/then] [else] [redraw][/redraw] [message] speaker=narrator message=_ "*Oops! You moved to the wrong place! After this message, you can press 'u' to undo, then try again." + _ " *Left click or press spacebar to continue..." [/message] [/else] [/if] [/then] [/if] [/event]
同じアクションを実行するLuaスクリプトを以下に示します。
[event] name=moveto first_time_only=no [allow_undo][/allow_undo] [filter] side=1 [/filter]
[lua] code = << local event_data = wesnoth.current.event_context if V.target_hex.is_set and (event_data.x1 ~= V.target_hex.x or event_data.y1 ~= V.target_hex.y) then W.redraw() narrator_says(_ "*Oops!\nYou moved to the wrong place! After this message, you can press 'u' to undo, then try again.") end >> [/lua] [/event]
以下は、Luaコードのより詳細な説明です。 まずは最初の行
local event_data = wesnoth.current.event_context
イベントデータをevent_dataローカル変数に格納します。 これはmovetoイベントであるため、event_dataテーブルには、x1フィールドとy1フィールドにユニットの宛先が含まれています。
次の2行は、テスト
if V.target_hex.is_set and (event_data.x1 ~= V.target_hex.x or event_data.y1 ~= V.target_hex.y)
変数V.target_hexがイベントパラメータと一致するかどうかを判定します。 Vはローカル変数ではないため、グローバル環境から取得されます。 通常、グローバル環境の変数は永続的ではないので(リロード時に失われます)データの格納には使用しないでください。 通常の永続Wml変数に簡単にアクセスできるように、グローバル変数Vは、次のプリロードイベントによってWML変数の記憶域にマップされます。
[event] name=preload first_time_only=no [lua] code = << H = wesnoth.require "lua/helper.lua" -- skipping some other initializations -- ... V = H.set_wml_var_metatable {} >> [/lua] [/event]
Vをリダイレクトするプレリュードがなければ、条件文が書かれます。
if wesnoth.get_variable("target_hex.is_set") and (event_data.x1 ~= wesnoth.get_variable("target_hex.x") or event_data.y1 ~= wesnoth.get_variable("target_hex.y")
条件の本体は[redraw]アクションを実行します。
W.redraw()
ここでも、この短い構文は、WをWMLアクションを実行するためのプロキシーにするプレリュードの行によって可能になります。
W = H.set_wml_action_metatable {}
このショートカットがなければ、最初のステートメントが書かれます。
wesnoth.fire("redraw")
最後に、次のコードでスクリプトはメッセージを表示します。
narrator_says(_ "*Oops!\nYou moved to the wrong place! After this message, you can press 'u' to undo, then try again.")
narrator_says関数も、プレリュードで定義されています。その背後にある構造はチュートリアルで何度も出現するためです。 単純なWMLでは、代わりにマクロが使用されていました。 関数の定義は次のとおりです。
function narrator_says(m) W.message { speaker="narrator", message = m .. _ "\n*Left click or press spacebar to continue..." } end
この関数は[message]アクションを起動し、通常の2つのフィールドを含むWMLオブジェクトを渡します。 2番目のフィールドは、関数の引数を別の文字列と連結することによって初期化されます。 両方の文字列の先頭に_記号が付いています。 (_は単なる単なる関数であり、キーワードではないことに注意してください)。これもまた、プレリュードの特定の行によって可能になります。
_ = wesnoth.textdomain "wesnoth-tutorial"
Interface to the engine and helper functions(エンジンとヘルパー関数へのインタフェース)
ゲームエンジンの機能は、wesnothグローバルテーブルに含まれる機能を通じて利用可能です。 これらの関数の中には、プロキシテーブルを返すものがあります。 "read-only"とマークされたフィールドへの書き込みは無視されます。 __cfgフィールドはプレーンなテーブルを返します。 特に、書き込みは元のオブジェクトを変更せず、読み込みはダンプが実行された時点からの値を返します。
いくつかのヘルパー関数は、lua / helper.luaライブラリによって提供されています。 wesnoth.requireを使用してライブラリをロードするときに返されるテーブルの中に格納されます。
helper = wesnoth.require "lua/helper.lua"
さらに詳しい情報
このトピックの元記事は本家wikiの「LuaWML」です。ここでは記事の冒頭の部分しか訳していませんが、本記事ではLuaで使用できるコマンドの詳しい情報が網羅されています。
原文で読みたい方はこちら