メインコンテンツまでスキップ

Luaスクリプティング:はじめに

webapp_banner.jpg

学べること

このページでは、EliteMobs用の最初のLuaパワーを書く方法を、空のファイルから動作するボス能力まで段階的に解説します。最後にはフック、コンテキスト、クールダウン、およびすべてのLuaパワーファイルの一般的な構造を理解できるようになります。

基本に慣れたら、以下の関連ページに進んでください:

実験的機能

Luaパワーファイルは現在実験的です。フック名、ヘルパーメソッド、動作はEliteMobsの進化に伴い変更される可能性があるため、本番サーバーで使用する前に十分にテストしてください。

EliteScriptとの関係

Luaは既存のeliteScript: YAMLシステムを置き換えません

  • 既存のアクションターゲットゾーン条件クールダウン相対ベクトルページを使った宣言的なYAMLベースのスクリプティングが必要な場合はEliteScriptを使用してください。
  • 変数、ループ、ランダム選択、再利用可能なヘルパー関数、ボスごとの永続状態、より伝統的なプログラミングフローが必要な場合はLuaパワーファイルを使用してください。

Luaには、EliteScriptで既におなじみのenum名とコンセプトを使用する独自のターゲティングとゾーンシステムがあります。Script Utilities API(context.script)を使えば、EliteScriptページで文書化されている同じフィールド名でゾーン、ターゲット、相対ベクトルを作成できます。


Luaパワーとは

Luaパワーは、EliteMobsの通常のpowersディレクトリツリーに配置される独立した.luaファイルで、通常のパワーファイルと全く同じように参照されます。

Luaパワーが得意なこと

Luaパワーは以下のような場合に力を発揮します:

  • 攻撃ローテーションとランダムな技選択
  • context.stateを使ったフック間の永続状態
  • YAMLの待機で全てを構築せずに行う遅延・繰り返しアクション
  • 1ファイル内で共有されるカスタムヘルパー関数
  • 純粋なEliteScriptでは扱いにくい複雑な分岐
  • EliteScriptスタイルのターゲティングとゾーン定義を再利用したいボスロジック

パワーが主に「イベントをトリガーし、いくつかのスクリプトアクションを実行する」だけであれば、既存のEliteScriptドキュメントが最も速く明確な構築方法です。パワーに本格的なプログラムフローが必要な場合は、Luaがそのためのツールです。


このページの対象読者

このページは3種類の読者向けに書かれています:

  • EliteScriptを既に知っていて、「本格的なプログラミング」を一度に全て学ぶことなくLuaを学びたい人
  • EliteMobsスクリプティングが初めてで、正確な名前の完全なリファレンスが必要な人
  • AIを使ってパワーを作成し、AIが偽の情報を生成した時にそれを見分けるための十分な詳細が必要な人

有用なパワーを書く前に、完全なLua開発者になる必要はありません。ほとんどの実用的なEliteMobsパワーに必要なのは:

  • 返されるテーブルに有効なフックを配置する方法
  • contextから値を読み取る方法
  • if ... then return endで早期終了する方法
  • いくつかのヘルパーメソッドを正確に呼び出す方法
  • 既存のEliteScriptドキュメントから適切なターゲットとゾーンの仕様をコピーする方法

メンタルモデル:EliteScript vs. Lua

EliteScriptを知っている場合、この比較がLuaパワーを理解する最速の方法です:

EliteScriptの用語で考える場合Luaでは通常以下を意味する
Eventson_spawnon_boss_damaged_by_playerのようなフック名
Cooldownscontext.cooldowns(下記のcontext.cooldownsを参照)
Actionscontext.world:spawn_particle_at_location(...)context.script:damage(...)のような直接メソッド呼び出し
Targetscontext.script:target({...}) -- EliteScript Targetのフィールド名を使用
Zonescontext.script:zone({...})またはネイティブcontext.zonesヘルパー -- ゾーンとターゲティングを参照
Relative vectorscontext.script:relative_vector({...})またはcontext.vectors
Script flow自分自身のLuaのif文、ヘルパー関数、タイマー、状態

最大の違いは以下の通りです:

  • EliteScriptはYAMLで何が起こるべきかを記述します。
  • Luaはコードを使っていつなぜどの分岐が実行されるべきかを決定できます。

EliteScriptが「設定」のように感じるなら、Luaは「設定+意思決定」のように感じます。


EliteMobs作者向けLuaミニ入門

これは、ほとんどのパワー作者が必要とするLua構文の最小限です。

変数

localを使って値を保存します:

local cooldown_key = "fire_burst"
local damage_multiplier = 1.5

localはこの変数がこのファイルまたはブロックにのみ属することを意味します。

関数

関数は再利用可能なロジックブロックです:

local function warn_player(player)
player:send_message("&cMove!")
end

後で呼び出せます:

warn_player(context.player)

ifチェック

何かが時々だけ発生すべき場合にifを使います:

if context.player == nil then
return
end

これは「このフックにプレイヤーがいない場合、ここで停止する」という意味です。

nil

nilは「値がない」という意味です。Luaにおける「ここには何もない」です。

よくnilをチェックします:

if context.event ~= nil then
-- イベントで何かをする
end

~=は「等しくない」という意味です。

テーブル

Luaはテーブルを複数の用途に使います:

  • リスト
  • 名前付きキーを持つオブジェクト
  • 最終的に返されるパワー定義

名前付きキーを持つテーブルの例:

local particle = {
particle = "FLAME",
amount = 1,
speed = 0.05
}

パワー定義を返す

ファイルの最後で、1つのテーブルを返します:

return {
api_version = 1,

on_spawn = function(context)
end
}

この返されたテーブルパワーファイルです。

コメント

--を使って人間向けのメモを書きます:

-- このクールダウンは毎回のヒットで攻撃が発動するのを防ぎます
context.cooldowns:set_local(60, "fire_burst")

最初の動作するパワー、ステップバイステップ

完全に初めての場合、これが「最初の成功」への最適な進行順です。

ステップ1の前:保存してボスに割り当てる

Luaファイルを通常のEliteMobsパワーフォルダーに保存します。例:

plugins/
EliteMobs/
powers/
first_test.lua

そのファイル名を通常のpowers:リストを使ってボス設定に追加します:

powers:
- first_test.lua

初心者の完全なループは:

  1. plugins/EliteMobs/powers/にファイルを保存
  2. .luaファイル名をボスのpowers:リストに追加
  3. そのボスをスポーンまたはリロード
  4. 現在構築中のフックをテスト

ボスファイル、パワーリスト、カスタムボスの構造についてより詳しい背景が必要な場合は、カスタムボスの作成を参照してください。

ステップ1:ファイルをロードさせる

return {
api_version = 1,

on_spawn = function(context)
end
}

これがエラーなくロードされれば、既に以下を証明しています:

  • ファイルは有効なLuaである
  • EliteMobsがファイルを見つけた
  • 返されるテーブルの形状が正しい
  • on_spawnは有効なフック名である

ステップ2:ボスに目に見えることをさせる

return {
api_version = 1,

on_spawn = function(context)
context.boss:play_sound_at_self("entity.blaze.ambient", 1.0, 1.0)
end
}

これで最も重要な初心者の証明が得られます:フックが発動しています。

ステップ3:プレイヤーのヒットに反応する

return {
api_version = 1,

on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end

context.player:send_message("&eYou hit the boss.")
end
}

これは3つの核心的な概念を教えます:

  • on_boss_damaged_by_playerがフック名
  • context.playerがそのフックに関わるプレイヤー
  • returnは必要なデータがない場合に早期終了する

ステップ4:クールダウンでスパムを防ぐ

return {
api_version = 1,

on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end

if not context.cooldowns:local_ready("hello_message") then
return
end

context.player:send_message("&eYou woke up the boss.")
context.cooldowns:set_local(60, "hello_message")
end
}

これはほとんどの作者が最初に必要とする本当に実用的なパターンです。このパターンを理解すれば、多くの実用的なパワーを構築できます。

ステップ5:本物のエフェクトを追加する

return {
api_version = 1,

on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end

if not context.cooldowns:local_ready("shock") then
return
end

context.player:send_message("&cStatic jumps from the boss into your armor!")
context.world:strike_lightning_at_location(context.player.current_location)
context.cooldowns:set_local(100, "shock")
end
}

この時点で、既に本物のパワーを書いています。


最初の実践的ワークフロー

新しいLuaパワーを一から構築する際は、この順序を使ってください:

  1. ファイルを作成し、on_spawnを動作させる。
  2. 実際に使いたいフックに変更する。
  3. フックにcontext.playercontext.eventなど期待するデータがあることを確認する。
  4. ダメージやパーティクルの前に、まずメッセージやサウンドを追加する。
  5. クールダウンを追加する。
  6. ゲームプレイエフェクトを1つ追加する。
  7. その後に初めて、ヘルパー、状態、スケジューラーロジック、ゾーンを追加する。

この順序により、一度に1つのことだけが変わるため、デバッグが大幅に容易になります。


Luaファイルの配置場所

.luaファイルを通常のパワー.ymlファイルと同じフォルダーツリーに配置します:

plugins/
EliteMobs/
powers/
mycoolpower.lua
attack_push.yml
subfolder/
myotherpower.lua

LuaパワーはEliteMobsが既にロードするパワーディレクトリから自動検出されます。

ボスがLuaパワーを参照する方法

ボスファイルは引き続き通常のpowers:リストを使用します:

powers:
- attack_push.yml
- mycoolpower.lua

特別なフィールドは不要です。LuaパワーはeliteScript:を通してロードされません。

ファイル命名規則

  • ボスはLuaパワーをファイル名で参照し、フォルダーパスではありません。
  • EliteMobsは現在、検出されたLuaパワーをベース名のみで登録します。
  • powers/fire.luapowers/bosses/fire.luaのような重複名は避けてください。検出時に一方が他方を上書きする可能性があります。

既製のLuaパワー

EliteMobsはpowersフォルダーにattack_fire.luafrost_cone.luameteor_shower.luaなど、数十の既製Luaパワーを同梱しています。これらは学習に最適なリファレンスです。plugins/EliteMobs/powers/ディレクトリの任意の.luaファイルを開いてください。後方互換性のため、既製パワーはレガシーの.yml名でも登録されています。


最小ファイル契約

すべてのLuaパワーはreturnでテーブルを返す必要があります。このテーブルは意図的に厳格です。

必須およびオプションのトップレベルフィールド

フィールド必須備考
api_versionはいNumber現在は1でなければならない
priorityいいえNumber実行優先度。低い値が先に実行される。デフォルトは0
サポートされるフックキーいいえFunctionフックリファレンスに記載されている正確なフック名の1つを使用する必要がある

バリデーションルール

  • ファイルはテーブルを返す必要があります
  • api_versionは必須で、現在は1でなければなりません。
  • priorityは存在する場合、数値でなければなりません。
  • 追加のトップレベルキーはすべてサポートされるフック名でなければなりません。
  • すべてのフックキーは関数を指している必要があります。
  • 不明なトップレベルキーは拒否されます。

ヘルパー関数とローカル定数は最終的なreturnに配置し、実際のフックでない限り返されるテーブルの内部に置かないでください。


コピー&ペーストスターターテンプレート

最小の有効なLuaパワー

return {
api_version = 1,

on_spawn = function(context)
end
}

推奨スターターテンプレート

local ATTACK_COOLDOWN = "my_attack"

local function can_run_attack(context)
return context.cooldowns:local_ready(ATTACK_COOLDOWN)
and context.cooldowns:global_ready()
end

local function run_attack(context)
context.boss:play_sound_at_self("entity.blaze.shoot", 1.0, 1.0)
context.cooldowns:set_local(100, ATTACK_COOLDOWN)
context.cooldowns:set_global(20)
end

return {
api_version = 1,
priority = 0,

on_boss_damaged_by_player = function(context)
if context.player == nil then
return
end

if not can_run_attack(context) then
return
end

run_attack(context)
end
}

大きなファイルのレイアウト

local CONSTANT_NAME = "value"

local function helper_function(context)
end

local function another_helper(context, value)
end

return {
api_version = 1,
priority = 0,

on_spawn = function(context)
end,

on_enter_combat = function(context)
end,

on_boss_damaged_by_player = function(context)
end,

on_exit_combat = function(context)
end
}

フック

フックは、返すテーブル内の特別な名前の関数です。EliteMobsは何かが起こったとき(ボスがスポーンする、ダメージを受ける、戦闘に入るなど)にそれらを呼び出します。上記のチュートリアルでon_spawnon_boss_damaged_by_playerを既に見ました。

最も一般的な開始フックはon_spawnon_boss_damaged_by_playeron_enter_combaton_exit_combatです。すべてのフックの完全なリスト、各フックで利用可能なコンテキストキー、実行順序の仕組みについては、フックとライフサイクルを参照してください。


contextとは?

すべてのフック関数はcontextと呼ばれる1つの引数を受け取ります。これは何かが起こるたびにEliteMobsがあなたに渡すツールボックスと考えてください。ボス、プレイヤー、ワールド、クールダウンなどとやり取りするために必要なすべてが含まれています。

on_boss_damaged_by_player = function(context)
-- context.boss = ヒットされたボス
-- context.player = ヒットしたプレイヤー
-- context.world = パーティクル、サウンドなどをスポーンするツール
-- context.cooldowns = クールダウン管理
-- context.state = 自分の永続ストレージ
end

contextは自分で作成しません。EliteMobsがそれを作成してフックに渡します。そこから読み取り、メソッドを呼び出すだけです。

備考

contextはフック呼び出しごとに新しく作成されます。ただしcontext.stateはボスの全ライフタイムにわたって永続します。これにより、context.stateにデータを保存し、別のフックで後から読み取ることができます。


主要なcontext API

contextから使用する最も重要なAPIは以下の通りです:

  • context.state -- ボスのライフタイム中永続する単純なLuaテーブル。フェーズ番号、タスクID、フラグ、フック間で記憶する必要があるものを保存するために使用します。context.stateのみが永続します。他のすべてのコンテキストテーブルは呼び出しごとに新しく作成されます。

  • context.log -- log:info(msg)log:warn(msg)log:debug(msg)によるコンソールログ。開発中に非常に有用です。

  • context.cooldowns -- パワーごとのローカルクールダウンとボスごとのグローバルクールダウン。キーメソッドはcooldowns:check_local(key, ticks)で、クールダウンをアトミックにチェックしてセットします。完全なクールダウンAPIについてはフックとライフサイクルページを参照してください。

  • context.scheduler -- scheduler:run_after(ticks, callback)scheduler:run_every(ticks, callback)による遅延・繰り返しタスク。コールバックは新しいcontextを受け取ります。外側のcontextではなく、常にコールバックパラメータを使用してください。繰り返しタスクはon_exit_combatでキャンセルしてください。詳細はフックとライフサイクルを参照してください。

  • context.boss / context.player -- 現在のイベントに関わるボスとプレイヤー。すべてのフィールドとメソッドについてはボスとエンティティを参照してください。

  • context.world -- パーティクル、エンティティ、サウンド、雷、ブロックのスポーン。ワールドと環境を参照してください。

  • context.zones / context.script -- ゾーンジオメトリ、ターゲティング、ダメージ、パーティクル。ゾーンとターゲティングを参照してください。


メソッド構文::.

Luaでは、object:method(arg)object.method(object, arg)の省略形です。EliteMobs APIは両方の形式を受け付けるため、どちらでも動作します:

context.cooldowns:set_local(60, "test")
context.cooldowns.set_local(60, "test") -- 同じ

すべてのドキュメントでは一貫して:を使用しています。


次のステップ

基本を理解したら、Luaスクリプティングドキュメントの残りを探索してください:

YAMLベースのスクリプティングについては、EliteScriptページが引き続き正式なリファレンスです: