Luaスクリプティング:NPCスクリプト
EliteMobsのNPC Luaスクリプトは、NPC設定にアタッチする独立した .lua ファイルです。ボスのLuaパワーとは別物で、ボスパワーは plugins/EliteMobs/powers/ に、NPCスクリプトは plugins/EliteMobs/npc_scripts/ に置かれます。
NPCスクリプトは現在、ボスパワー、FreeMinecraftModelsのプロップ、FMMアイテムと同じ統合MagmaCoreスクリプティングランタイム上で動作します。つまりNPCスクリプトは、共有スクリプティング面のすべて — context.world(strike_lightning を含む)、context.zones、context.scheduler、context.cooldowns、context.log、context.event、context.player — に加えて、NPC固有の context.npc テーブルを利用できます。MagmaCoreがスクリプトに公開しているものは、すべてここでも使えます。
NPC Luaスクリプトはまだ実験的です。NPC固有のフックと context.npc のヘルパーは変更される可能性があります。共有テーブル(context.world、context.zones、context.scheduler、context.cooldowns、context.log、context.event、context.player)は、スクリプティングエンジンとLua APIリファレンスで文書化されているものと同一です。
ファイルの配置場所
NPCスクリプトファイルは次の場所に作成します:
plugins/
EliteMobs/
npc_scripts/
wave.lua
サブフォルダも再帰的にスキャンされます。ただしスクリプトはファイル名のみで登録されるため、npc_scripts/wave.lua と npc_scripts/town/wave.lua は衝突します。ツリー全体でベース名が重複しないようにしてください。
NPC設定内での .lua 拡張子は省略可能です。- wave と - wave.lua はどちらも wave.lua に解決されます。NPC設定が存在しないスクリプトを参照している場合、EliteMobsは警告をログに出力し、NPC自体は通常どおりスポーンします。
NPCへのスクリプトのアタッチ
NPC設定に scripts: リストを追加します:
scripts:
- wave.lua
1体のNPCに複数のスクリプトをアタッチできます:
scripts:
- wave.lua
- greeting_particles.lua
スクリプトは優先度順に実行されます。priority の値が小さいものが先に実行されます。priorityを省略した場合の既定値は 0 です。
スクリプトの構造
すべてのNPCスクリプトは、1つのテーブルを返す必要があります:
return {
api_version = 1,
priority = 0,
on_spawn = function(context)
context.state.spawned = true
context.npc:play_model_animation("idle")
end
}
受け付けられるトップレベルフィールドは次のものだけです:
| フィールド | 型 | 備考 |
|---|---|---|
api_version | number | 必須。1 である必要があります。 |
priority | number | 任意。値が小さいものが先に実行されます。 |
on_spawn | function | NPCのスポーン後に実行されます。 |
on_remove | function | NPCが削除されたときに実行されます。 |
on_game_tick | function | NPCが有効な間、サーバーティックごとに実行されます。処理は極力軽くしてください。 |
on_npc_interact | function | プレイヤーがNPCとインタラクトしたときに実行されます。 |
on_npc_proximity_enter | function | プレイヤーがこのNPCのアクティベーション半径に入ったときに1回実行されます。 |
on_npc_proximity_leave | function | プレイヤーがこのNPCのアクティベーション半径から出たときに1回実行されます。 |
on_zone_enter | function | このスクリプトが監視しているゾーンにプレイヤーが入ったときに実行されます(context.zones を参照)。 |
on_zone_leave | function | 監視中のゾーンからプレイヤーが出たときに実行されます。 |
ゾーン監視が追跡するのはプレイヤーだけです。モブやその他のエンティティが on_zone_enter / on_zone_leave を発火させることはありません。
不明なトップレベルキーはスクリプト読み込み時に拒否されます。ヘルパー関数は、返却するテーブルより上で local 関数として宣言してください。
近接フック
NPCの近接フックは、NPCの activationRadius 設定値を使用します。
| フック | 発火するタイミング |
|---|---|
on_npc_proximity_enter | プレイヤーがNPCのアクティベーション半径の外から内へ移動したとき。 |
on_npc_proximity_leave | プレイヤーがNPCのアクティベーション半径の内から外へ移動したとき。 |
これらのフックは、サーバー側の近接スキャナーによってNPCごと・プレイヤーごとに追跡されます。あるNPCの近くに立っていても別のNPCが自身のenterイベントを発火するのを妨げることはなく、半径内に留まり続けてもenterイベントが連発されることはありません。
通常の挨拶、ダイアログ、クエストインジケーターの挙動はそのまま動作します。Luaフックはその上に振る舞いを追加するものです。
共有スクリプティング面
NPCスクリプトは統合ランタイム上で動作するため、すべてのNPCフックはFreeMinecraftModelsスクリプトが使うのと同じ共有MagmaCoreコンテキストテーブルも受け取ります。EliteMobsのボスパワーも同じランタイム上で動作しますが、いくつかのテーブルについてはボス固有のバリアントを使います。完全なメソッド一覧はLua APIリファレンスとスクリプティングエンジンにあります:
| テーブル | 役割 |
|---|---|
context.world | ワールドへの効果とクエリ:strike_lightning、spawn_particle、play_sound、set_block_at、place_temporary_block、spawn_entity、spawn_firework、get_nearby_entities、get_nearby_players、raycast など。座標形式(strike_lightning(x, y, z))とロケーションテーブル形式(strike_lightning_at_location(loc))の両方が使えます。 |
context.zones | 空間ゾーンの作成(create_sphere(x, y, z, radius)、create_cylinder(x, y, z, radius, height)、create_cuboid(x, y, z, xSize, ySize, zSize)) — それぞれ数値ハンドルを返します。watch(handle, on_enter, on_leave) で追跡を開始し(コールバックは渡した関数ではなく、スクリプトの on_zone_enter / on_zone_leave フックを発火させます)、unwatch(handle) で停止します。 |
context.scheduler | run_later(ticks, fn)、run_repeating(delay, interval, fn)、cancel(task_id)。 |
context.cooldowns | 共有MagmaCoreクールダウン:local_ready、local_remaining、check_local、set_local、global_ready、set_global。 |
context.log | info(msg)、warn(msg)、error(msg) — サーバーコンソールに出力します。 |
context.event | 該当するBukkitイベントが存在する場合の現在のイベント。下記を参照。 |
context.player | インタラクト/トリガーしたプレイヤー(存在する場合)。下記を参照。 |
context.state | このNPCスクリプトインスタンスに対して、NPCが削除されるまで保持される素のLuaテーブル。 |
例:インタラクト時に雷を落とす
return {
api_version = 1,
on_npc_interact = function(context)
-- NPC scripts can now reach the full world API.
context.world:strike_lightning_at_location(context.npc:get_location())
end
}
context.npc
context.npc はすべてのNPCフックで利用できます。
フィールド
| フィールド | 型 | 備考 |
|---|---|---|
name | string | 設定で指定したNPCの表示名。 |
filename | string | NPC設定のファイル名。 |
uuid | string | 実行時のNPCのUUID。 |
activation_radius | number | 設定されたアクティベーション半径。 |
current_location | location table | 実体となるエンティティが存在する場合の位置スナップショット。 |
entity_type | string | 実体となるエンティティが存在する場合のBukkitエンティティタイプ。 |
メソッド
| メソッド | 引数 | 戻り値 | 備考 |
|---|---|---|---|
is_valid() | - | boolean | NPCがまだ有効な実体エンティティを保持しているかどうか。 |
get_location() | - | location table | 現在のNPCの位置。エンティティが利用できない場合はスポーン位置。 |
get_eye_location() | - | location table | 現在の目線位置。利用できない場合はスポーン位置にフォールバック。 |
get_activation_radius() | - | number | 現在設定されているアクティベーション半径。 |
get_nearby_players(radius) | number | table | NPCの半径内にいるプレイヤーラッパー。 |
face_direction_or_location(target) | vector or location | nil | 方向ベクトルの向きを向くか、ロケーション/プレイヤー位置の方を向きます。 |
say_greeting(player?) | player, UUID, name, or nil | nil | 設定された挨拶を送信します。利用可能な場合はトリガーしたプレイヤーが既定値になります。 |
say_dialog(player?) | player, UUID, name, or nil | nil | 設定されたダイアログを送信します。利用可能な場合はトリガーしたプレイヤーが既定値になります。 |
say_farewell(player?) | player, UUID, name, or nil | nil | 設定された別れの言葉を送信します。利用可能な場合はトリガーしたプレイヤーが既定値になります。 |
play_model_animation(name) | string | nil | カスタムモデルのアニメーションが存在する場合に再生します。存在しない場合は安全に何もしません。 |
patrol_pause() | - | boolean | 設定された巡回を一時停止します。 |
patrol_resume() | - | boolean | スクリプトの待機・一時移動を解除して巡回を再開します。 |
walk_to(x, y, z) | 3 個の数値 | boolean | 原点からのオフセットまで歩き、巡回を再開します。長距離は自動解決されます。 |
hold(x, y, z) | 3 個の数値 | boolean | オフセットまで歩いて待機します。 |
teleport(x, y, z) | 3 個の数値 | boolean | 目的地でエンティティが tick している場合にテレポートします。 |
NPC に巡回が設定されていない場合や要求を受理できない場合、移動メソッドは false を返します。NPC・ボスの巡回 を参照してください。
context.player
context.player は on_npc_interact、on_npc_proximity_enter、on_npc_proximity_leave で利用できます。プレイヤーが関与しないライフサイクルフックでは nil です。
これは共有MagmaCoreのプレイヤーラッパーであり、ボスパワーやFMMスクリプトが使うのと同じ完全なリビングエンティティ/プレイヤーテーブルです。そのため、基本的なもの以外にも多くを公開しています(体力、ポーション効果、send_message、show_title、show_action_bar、get_held_item、レイキャストなど)。完全な一覧はLua APIリファレンスを参照してください。ここでよく使われるものは次のとおりです:
| フィールド/メソッド | 備考 |
|---|---|
name | プレイヤー名。 |
uuid | プレイヤーのUUID。 |
current_location | プレイヤーの現在位置のテーブル。メソッドではなくフィールドです。 |
get_eye_location() | 現在のプレイヤーの目線位置。 |
send_message(text) | チャットメッセージを送信します。カラーコードに対応。 |
共有ヘルパー関数の中で使う前に、必ず context.player のnilチェックを行ってください。
entity_type は小文字です共有MagmaCoreのエンティティテーブルでは、entity_type は Bukkit 名を小文字にしたものです("player"、"zombie")。大文字形式を使うのは context.npc.entity_type と EliteMobs のボスパワーのエンティティテーブルだけです。両方を扱う必要があるスクリプトでは、大文字小文字を区別せずに比較してください。
すべての共有エンティティテーブルに EliteMobs が追加するフィールド
EliteMobs が動作している間、すべての MagmaCore エンティティテーブル — context.player、context.npc:get_nearby_players(...) が返すラッパー、そして FreeMinecraftModels のプロップやアイテムのスクリプトが目にするもの — に追加のフィールドが提供されます。
| フィールド | 型 | 備考 |
|---|---|---|
is_elite | boolean | EliteMobs がそのエンティティをエリートとして追跡していれば true |
is_custom_boss | boolean | カスタムボスであれば true(is_elite が false のときは常に false) |
is_significant_boss | boolean | 体力倍率が 1 を超えるカスタムボスで true — 「これは増援ではなく本物のボスだ」を判定する実用的なチェック |
elite | table または nil | エリートにのみ存在します。下記を参照 |
elite サブテーブル:
| フィールド/メソッド | 型 | 備考 |
|---|---|---|
elite.level | number | エリートのレベル |
elite.name | string または nil | エリートの表示名 |
elite.health | number | 現在のエリートの体力(リアルタイムで読み取り) |
elite.max_health | number | エリートの最大体力(リアルタイムで読み取り) |
elite.is_custom_boss | boolean | トップレベルのフィールドと同じ値 |
elite.health_multiplier | number | 設定された体力倍率 |
elite.damage_multiplier | number | 設定されたダメージ倍率 |
elite:remove() | — | エリートをデスポーンさせます |
-- Warn the approaching player if a real boss is loose near this NPC
on_npc_proximity_enter = function(context)
if context.player == nil then return end
local here = context.npc:get_location()
local nearby = context.world:get_nearby_entities(here.x, here.y, here.z, 40)
for i = 1, #nearby do
if nearby[i].is_significant_boss then
context.player:send_message("&cA boss is nearby: " .. tostring(nearby[i].elite.name))
return
end
end
end
これらのフィールドは EliteMobs のボスパワーのエンティティラッパーには登場しません。そちらはボス側の別のテーブルビルダーによって構築されます。そのセットについてはボスとエンティティを参照してください。
context.event
フックに対応するBukkitイベントがない場合、context.event は nil です。存在する場合は共有MagmaCoreのイベントテーブルになります:
| フィールド/メソッド | 備考 |
|---|---|
is_cancelled | 元のイベントがキャンセルされているかどうか(キャンセル可能なイベントでのみ意味を持ちます)。 |
cancel() | キャンセル可能な場合にイベントをキャンセルします。 |
uncancel() | キャンセル可能な場合にイベントのキャンセルを解除します。 |
player | イベントの実行者(例:インタラクトしたプレイヤー)をプレイヤーラッパーとして。存在する場合のみ。 |
インタラクト/近接のプレイヤーについては context.player を使うことを推奨します(これらのフックでは必ず設定されます)。
状態、スケジューラー、クールダウン
context.state は、このNPCスクリプトインスタンスに対してNPCが削除されるまで保持される素のLuaテーブルです。
context.scheduler は共有MagmaCoreのスケジューラーです。MagmaCoreの名前とEliteMobsの run_after / run_every の名前はどちらも使えます — 同じ動作のエイリアスです:
| メソッド | 引数 | 備考 |
|---|---|---|
run_later(ticks, callback) / run_after(ticks, callback) | number, function | 遅延後に1回実行します。タスクIDを返します。 |
run_repeating(delay, interval, callback) | number, number, function | 初期遅延の後、繰り返し実行します。タスクIDを返します。 |
run_every(interval, callback) | number, function | interval ティックごとに実行します(初期遅延は0)。タスクIDを返します。 |
cancel(task_id) / cancel_task(task_id) | number | 所有しているタスクをキャンセルします。 |
スケジューラーのコールバックは新しいコンテキストを受け取ります。元の context.player や context.event は受け取りません。所有しているタスクは、NPCが削除された時点ですべて自動的にキャンセルされます。
context.cooldowns は共有MagmaCoreのクールダウンテーブルです:
| メソッド | 引数 | 戻り値 | 備考 |
|---|---|---|---|
local_ready(key?) | string | boolean | ローカルクールダウンが明けている場合にtrue。 |
local_remaining(key?) | string | number | 残りティック数。準備完了の場合は 0。 |
check_local(key?, duration) | string, number | boolean | 準備完了ならクールダウンを開始してtrueを返します。 |
set_local(duration, key?) | number, string | nil | クールダウンを設定またはリセットします。 |
global_ready() | - | boolean | 共有グローバルクールダウンが準備完了ならtrue。 |
set_global(duration) | number | nil | グローバルクールダウンを開始します。 |
NPCスクリプトは現在、ボスパワーやFreeMinecraftModelsスクリプトと同じ、共有MagmaCoreのクールダウン引数順(check_local(key?, duration))を使用します。以前の実験的なNPCビルドでは check_local(duration, key?) でした — 古いスクリプトは共有の引数順に更新してください。
例:接近時に手を振る
このスクリプトは、NPCが入ってきたプレイヤーの方を向き、wave のカスタムモデルアニメーションを再生します。近接enterは、プレイヤーが半径内に留まっている限りNPC/プレイヤーの組み合わせごとに1回だけ発火しますが、クールダウンによって、素早く出入りを繰り返した際にアニメーションが何度も再生されるのを防いでいます。
return {
api_version = 1,
priority = 0,
on_npc_proximity_enter = function(context)
if context.player == nil then return end
if context.cooldowns:check_local("wave:" .. context.player.uuid, 60) then
context.npc:face_direction_or_location(context.player.current_location)
context.npc:play_model_animation("wave")
end
end
}
play_model_animation(name) は、NPCにカスタムモデルがない場合や、モデルにそのアニメーションがない場合、安全に何も行いません。
パフォーマンスのガイドライン
on_game_tickフックは小さく保ってください。定義しているNPCスクリプトインスタンスごとに、毎秒20回実行されます。(on_game_tickを宣言していないスクリプトはティックされません。)- 近接に関する挙動には、毎ティック周囲のプレイヤーをポーリングするのではなく、
on_npc_proximity_enterとon_npc_proximity_leaveを使ってください。 - アニメーション、サウンド、パーティクルの発生を制御するには
context.cooldowns:check_local(...)を使ってください。 - 毎ティック実行する必要のない挙動には、適切な間隔を指定した
context.scheduler:run_repeating(...)を使ってください。 - Lua内での大規模な検索は避けてください。
context.npc:get_nearby_players(radius)は小規模なローカルチェックには問題ありませんが、広範囲のスキャンはプラグインランタイム側に留めるべきです。
関連ページ
- NPCの作成 --
activationRadiusを含むNPC設定フィールド - Luaはじめに -- ボスのLuaパワー
- スクリプティングエンジン -- 共通のLuaコンセプトと統合ランタイム
- Lua APIリファレンス --
context.world、context.player、context.zonesなどの完全なメソッド一覧
