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

Luaスクリプティング:NPCスクリプト

webapp_banner.jpg

EliteMobsのNPC Luaスクリプトは、NPC設定にアタッチする独立した .lua ファイルです。ボスのLuaパワーとは別物で、ボスパワーは plugins/EliteMobs/powers/ に、NPCスクリプトは plugins/EliteMobs/npc_scripts/ に置かれます。

NPCスクリプトは現在、ボスパワー、FreeMinecraftModelsのプロップ、FMMアイテムと同じ統合MagmaCoreスクリプティングランタイム上で動作します。つまりNPCスクリプトは、共有スクリプティング面のすべて — context.worldstrike_lightning を含む)、context.zonescontext.schedulercontext.cooldownscontext.logcontext.eventcontext.player — に加えて、NPC固有の context.npc テーブルを利用できます。MagmaCoreがスクリプトに公開しているものは、すべてここでも使えます。

実験的機能

NPC Luaスクリプトはまだ実験的です。NPC固有のフックと context.npc のヘルパーは変更される可能性があります。共有テーブル(context.worldcontext.zonescontext.schedulercontext.cooldownscontext.logcontext.eventcontext.player)は、スクリプティングエンジンLua APIリファレンスで文書化されているものと同一です。


ファイルの配置場所

NPCスクリプトファイルは次の場所に作成します:

plugins/
EliteMobs/
npc_scripts/
wave.lua

サブフォルダも再帰的にスキャンされます。ただしスクリプトはファイル名のみで登録されるため、npc_scripts/wave.luanpc_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_versionnumber必須。1 である必要があります。
prioritynumber任意。値が小さいものが先に実行されます。
on_spawnfunctionNPCのスポーン後に実行されます。
on_removefunctionNPCが削除されたときに実行されます。
on_game_tickfunctionNPCが有効な間、サーバーティックごとに実行されます。処理は極力軽くしてください。
on_npc_interactfunctionプレイヤーがNPCとインタラクトしたときに実行されます。
on_npc_proximity_enterfunctionプレイヤーがこのNPCのアクティベーション半径に入ったときに1回実行されます。
on_npc_proximity_leavefunctionプレイヤーがこのNPCのアクティベーション半径から出たときに1回実行されます。
on_zone_enterfunctionこのスクリプトが監視しているゾーンにプレイヤーが入ったときに実行されます(context.zones を参照)。
on_zone_leavefunction監視中のゾーンからプレイヤーが出たときに実行されます。

ゾーン監視が追跡するのはプレイヤーだけです。モブやその他のエンティティが 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_lightningspawn_particleplay_soundset_block_atplace_temporary_blockspawn_entityspawn_fireworkget_nearby_entitiesget_nearby_playersraycast など。座標形式(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.schedulerrun_later(ticks, fn)run_repeating(delay, interval, fn)cancel(task_id)
context.cooldowns共有MagmaCoreクールダウン:local_readylocal_remainingcheck_localset_localglobal_readyset_global
context.loginfo(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フックで利用できます。

フィールド

フィールド備考
namestring設定で指定したNPCの表示名。
filenamestringNPC設定のファイル名。
uuidstring実行時のNPCのUUID。
activation_radiusnumber設定されたアクティベーション半径。
current_locationlocation table実体となるエンティティが存在する場合の位置スナップショット。
entity_typestring実体となるエンティティが存在する場合のBukkitエンティティタイプ。

メソッド

メソッド引数戻り値備考
is_valid()-booleanNPCがまだ有効な実体エンティティを保持しているかどうか。
get_location()-location table現在のNPCの位置。エンティティが利用できない場合はスポーン位置。
get_eye_location()-location table現在の目線位置。利用できない場合はスポーン位置にフォールバック。
get_activation_radius()-number現在設定されているアクティベーション半径。
get_nearby_players(radius)numbertableNPCの半径内にいるプレイヤーラッパー。
face_direction_or_location(target)vector or locationnil方向ベクトルの向きを向くか、ロケーション/プレイヤー位置の方を向きます。
say_greeting(player?)player, UUID, name, or nilnil設定された挨拶を送信します。利用可能な場合はトリガーしたプレイヤーが既定値になります。
say_dialog(player?)player, UUID, name, or nilnil設定されたダイアログを送信します。利用可能な場合はトリガーしたプレイヤーが既定値になります。
say_farewell(player?)player, UUID, name, or nilnil設定された別れの言葉を送信します。利用可能な場合はトリガーしたプレイヤーが既定値になります。
play_model_animation(name)stringnilカスタムモデルのアニメーションが存在する場合に再生します。存在しない場合は安全に何もしません。
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.playeron_npc_interacton_npc_proximity_enteron_npc_proximity_leave で利用できます。プレイヤーが関与しないライフサイクルフックでは nil です。

これは共有MagmaCoreのプレイヤーラッパーであり、ボスパワーやFMMスクリプトが使うのと同じ完全なリビングエンティティ/プレイヤーテーブルです。そのため、基本的なもの以外にも多くを公開しています(体力、ポーション効果、send_messageshow_titleshow_action_barget_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.playercontext.npc:get_nearby_players(...) が返すラッパー、そして FreeMinecraftModels のプロップやアイテムのスクリプトが目にするもの — に追加のフィールドが提供されます。

フィールド備考
is_elitebooleanEliteMobs がそのエンティティをエリートとして追跡していれば true
is_custom_bossbooleanカスタムボスであれば trueis_elitefalse のときは常に false
is_significant_bossboolean体力倍率が 1 を超えるカスタムボスで true — 「これは増援ではなく本物のボスだ」を判定する実用的なチェック
elitetable または nilエリートにのみ存在します。下記を参照

elite サブテーブル:

フィールド/メソッド備考
elite.levelnumberエリートのレベル
elite.namestring または nilエリートの表示名
elite.healthnumber現在のエリートの体力(リアルタイムで読み取り)
elite.max_healthnumberエリートの最大体力(リアルタイムで読み取り)
elite.is_custom_bossbooleanトップレベルのフィールドと同じ値
elite.health_multipliernumber設定された体力倍率
elite.damage_multipliernumber設定されたダメージ倍率
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.eventnil です。存在する場合は共有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, functioninterval ティックごとに実行します(初期遅延は0)。タスクIDを返します。
cancel(task_id) / cancel_task(task_id)number所有しているタスクをキャンセルします。

スケジューラーのコールバックは新しいコンテキストを受け取ります。元の context.playercontext.event は受け取りません。所有しているタスクは、NPCが削除された時点ですべて自動的にキャンセルされます。

context.cooldowns は共有MagmaCoreのクールダウンテーブルです:

メソッド引数戻り値備考
local_ready(key?)stringbooleanローカルクールダウンが明けている場合にtrue。
local_remaining(key?)stringnumber残りティック数。準備完了の場合は 0
check_local(key?, duration)string, numberboolean準備完了ならクールダウンを開始してtrueを返します。
set_local(duration, key?)number, stringnilクールダウンを設定またはリセットします。
global_ready()-boolean共有グローバルクールダウンが準備完了ならtrue。
set_global(duration)numbernilグローバルクールダウンを開始します。
Unified cooldown API

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_enteron_npc_proximity_leave を使ってください。
  • アニメーション、サウンド、パーティクルの発生を制御するには context.cooldowns:check_local(...) を使ってください。
  • 毎ティック実行する必要のない挙動には、適切な間隔を指定した context.scheduler:run_repeating(...) を使ってください。
  • Lua内での大規模な検索は避けてください。context.npc:get_nearby_players(radius) は小規模なローカルチェックには問題ありませんが、広範囲のスキャンはプラグインランタイム側に留めるべきです。

関連ページ