以下のコマンドはゲーム中いつでも呼び出せます。
?reload_scripts -- オートセーブ後すべてのスクリプトとロケーションを再読み込みする
?kick <peer_id>
?ban <peer_id>
?add_admin <peer_id> -- そのプレイヤーがコマンドやカスタムメニューにアクセスできるように権限を与える
?remove_admin <peer_id>
?add_auth <peer_id> -- そのプレイヤーがワークベンチを使えるように権限を与える
?remove_auth <peer_id>
?save <save_name> -- 強制的にセーブさせる(セーブ名を省略するとオートセーブのデータに上書き)
Luaスクリプティングはあなたに高度なミッションやカスタムゲームモードを作るためのツールを提供します。
Stormworksでは、あなたのスクリプトがゲームを操作できるようないくつもの機能を提供しています。
このガイドは利用可能な関数について説明しますが、Luaスクリプティングを始めるためのチュートリアルではありません。
peer_id
に-1を渡すと全員を指定できるg_savedata
テーブルに入れた変数はセーブデータ毎に保存されるonCreate()
のis_world_create
がtrueのときに実行すれば良いonCreate()
内でserver.announce()
を使うとワールド生成/接続前に送信されるため、メッセージを受け取れない
以下は条件を満たした際に自動的に呼ばれる、スクリプトで使えるコールバック関数です。
最もシンプルなコールバック関数はonTick
で、ゲーム内tick毎に呼び出されます。
function onTick(delta_worldtime) -- delta_worldtimeは1フレームに経過したtick数です。通常は1ですが、寝ている間は400になります
function onCreate(is_world_create) -- ?reload_scriptsコマンド実行時にも呼び出されます
function onDestroy()
function onCustomCommand(full_message, user_peer_id, is_admin, is_auth, command, args ...)
function onChatMessage(peer_id, sender_name, message)
function onPlayerJoin(steam_id, name, peer_id, is_admin, is_auth)
function onCharacterSit(object_id, vehicle_id, seat_name)
function onPlayerRespawn(peer_id)
function onPlayerLeave(steam_id, name, peer_id, is_admin, is_auth)
function onToggleMap(peer_id, is_open)
function onPlayerDie(steam_id, name, peer_id, is_admin, is_auth)
function onVehicleSpawn(vehicle_id, peer_id, x, y, z, cost) -- スクリプトでスポーンしたビークルのpeer_idは-1です。costはプレイヤーがスポーンさせたときのみ計算されます
function onVehicleLoad(vehicle_id)
function onVechicleUnload(vechicle_id)
function onVehicleTeleport(vehicle_id, peer_id, x, y, z)
function onVehicleDespawn(vehicle_id, peer_id)
function onSpawnMissionComponent(object_id/vehicle_id, component_name, OBJECT_TYPE, playlist_index) -- スクリプトでスポーンしたオブジェクトやビークルすべてで呼び出されます
function onVehicleDamaged(vehicle_id, damage) -- 修理時はダメージ値が負の値で呼び出されます
function httpReply(port, request, reply)
function onFireExtinguished(x, y, z)
[編集メモ] moko256氏による検証によると、onSpawnMissionComponent
の第4引数はplaylist_nameではなくplaylist_indexで、ミッションプレイリストの中で何番目かを取得できるようです。しれっと修正してしまうとどちらが間違っていたのか分かりにくくなるため、一旦間違ったままリストに載せています。
→ v1.0.20時点では修正されていましたのでリストに反映しました。delta_worldtime
についてはこちらのほうが分かりやすいため、このままにします。
onCustomCommand
は「?」から始まるチャットのメッセージを検出できる柔軟な関数です。
function onCustomCommand(full_message, user_peer_id, is_admin, is_auth, command, arg1, arg2, arg3, arg4)
name = server.getPlayerName(user_peer_id)
-- Adminのみがこのコマンドを利用できます
if command == "?tp" and is_admin == true then
server.setPlayerPos(user_peer_id, matrix.translation(arg1,arg2,arg3))
end
if command == "?hello" then
server.announce("[Server]", "Hello " .. name)
end
end
以下のいくつかの関数はすべて大文字の引数を含みます。それらの変数の説明は後述します。
-- UI
server.announce(name, message[, peer_id]) -- peer_id省略時の値は-1(全員に送信)です
server.notify(peer_id, title, message, NOTIFICATION_TYPE)
ui_id = ui.getMapID()
server.removeMapID(peer_id, ui_id)
server.addMapObject(peer_id, ui_id, POSITION_TYPE, MARKER_TYPE, x, y, z, parent_local_x, parent_local_y, parent_local_z, vehicle_id, object_id, label, vehicle_parent_id, radius, hover_lavel)
server.removeMapObject(peer_id, ui_id)
server.addMapLabel(peer_id, ui_id, LABEL_TYPE, name, x, y, z)
server.removeMapLabel(peer_id, ui_id)
server.addMapLine(peer_id, ui_id, start_matrix, end_matrix, width)
server.removeMapLine(peer_id, ui_id)
server.setPopup(peer_id, ui_id, name, is_show, text, x, y, z, is_worldspace, render_distance) -- is_worldspaceがfalseの場合、ポップアップは画面上の指定位置で表示されます(yの値は無視されます)
server.setPopupScreen(peer_id, ui_id, name, is_show, text, horizontal_offset, vertical_offset)
server.removePopup(peer_id, ui_id)
server.createPopup(peer_id, ui_id)
-- Player
name, is_success = server.getPlayerName(peer_id)
PLAYER_LIST = server.getPlayers()
matrix is_success = server.getPlayerPos(peer_id)
is_success = server.setPlayerPos(peer_id, matrix)
x,y,z, is_success = server.getPlayerLookDirection(peer_id)
-- Vehicle
vehicle_id, is_success = server.spawnVehicle(matrix, playlist_index, component_id)
vehicle_id, is_success = server.spawnVehicleSavefile(matrix, save_name)
is_success = server.despawnVehicle(vehicle_id, is_instant)
matrix, is_success = server.getVehiclePos(vehicle_id, voxel_x = 0, voxel_y = 0, voxel_z = 0)
name, is_success = server.getVehicleName(vehicle_id)
is_success = server.setVehiclePos(vehicle_id, matrix)
server.cleanVehicles()
is_on = server.getVehicleButton(vehicle_id, button_name)
server.setVehicleSeat(vehicle_id, seat_name, axis_w, axis_d, axis_up, axis_right, button1, button2, button3, button4, button5, button6)
server.pressVehicleButton(vehicle_id, button_name)
server.getVehicleFireCount(vehicle_id)
server.setVehicleTooltip(vehicle_id, text)
is_simulating = server.getVehicleSimulating(vehicle_id)
server.setVehicleTransponder(vehicle_id, is_active)
server.setVehicleEditable(vehicle_id, is_editable)
-- Mission
playlist_index, is_success = server.getPlaylistIndexByName(name)
playlist_index = server.getPlaylistIndexCurrent()
location_index, is_success = server.getLocationIndexByName(playlist_index, name)
is_success = server.spawnThisPlaylistMissionLocation(name)
matrix, is_success = server.spawnMissionLocation(matrix, playlist_index, location_index)
-- 0,0,0のmatrixを指定すると(タイルの種類が同じ)ランダムな場所にスポーンします。それ以外を指定するとそのワールド座標にスポーンします
is_success = server.setObjectPos(object_id)
path, is_success = server.getPlaylistPath(playlist_name, is_rom)
object_id, is_success = server.spawnObject(matrix, OBJECT_TYPE)
matrix, is_success = server.getObjectPos(object_id)
server.killCharacter(object_id)
object_id, is_success = server.spawnFire(matrix, size, magnitude, is_lit, is_initialzied, is_explosive, parent_vehicle_id, explosion_point, explosion_magnitude)
object_id, is_success = server.spawnCharacter(matrix, (OUTFIT_TYPE))
object_id, is_success = server.spawnAnimal(matrix, ANIMAL_TYPE, size_multiplier)
is_success = server.despawnObject(object_id, is_instant)
-- server.despawnCharacter(object_id, is_instant) (server.despawnObjectに統合)
hp, matrix, is_incapacitated, is_dead, is_interactable = server.getCharacterData(object_id)
object_id, is_success = server.getPlayerCharacterID(peer_id)
EQUIPMENT_ID, is_success= server.getCharacterItem(object_id, SLOT_NUMBER)
server.setCharacterSeated(object_id, vehicle_id, seat_name)
server.reviveCharacter(object_id)
server.setCharacterData(object_id, hp, is_interactable)
server.setCharacterItem(object_id, slot, EQUIPMENT_ID, is_active)
ZONE_LIST = server.getZones() -- すべての環境MODのゾーンのリストを返す
ZONE_LIST = server.getZones(tag(s)) -- すべてのゾーンの中でタグにマッチするもののリストを返す
is_in_zone = server.isInZone(matrix, zone_name)
COMPONENT, is_success = server.spawnMissionComponent(matrix, playlist_index, location_index, object_index)
-- server.despawnMissionObject(object_id, is_instant) (server.despawnObjectに統合)
count = server.getPlaylistCount()
PLAYLIST_DATA = server.getPlaylistData(playlist_index)
LOCATION_DATA = server.getLocationData(playlist_index, location_index)
COMPONENT_DATA, is_success = server.getLocationComponentData(playlist_index, location_index, *component_index*)
server.setFireData(object_id, is_lit, is_explosive)
is_lit = server.getFireData(object_id)
matrix, is_success = server.getOceanTransform(matrix, min_search_range, max_search_range) -- 指定範囲内のランダムな海のタイルのワールド座標を返す
is_in_area = server.isInTransformArea(matrix_object, matrix_zone, zone_x, zone_y, zone_z)
-- [v1.0.21] 公式のリファレンスではObjectsというセクションになっていてそれに合わせるべきなのですが、力尽きました……
-- Game
server.setGameSetting(GAME_SETTING, value)
{ [GAME_SETTING] = value } = server.getGameSettings()
-- ゲーム設定にはインラインでアクセスできます: server.getGameSettings().third_person
server.setCurrency(money, research)
amount = server.getCurrency() -- 所持金の金額を取得
amount = server.getResearchPoints()
days_survived = server.getDateValue()
system_time = getTimeMillisec()
is_purchased = server.getTilePurchased(matrix)
-- Http
server.httpGet(port, request)
-- Admin
server.banPlayer(peer_id)
server.kickPlayer(peer_id)
server.addAdmin(peer_id)
server.removeAdmin(peer_id)
server.addAuth(peer_id)
server.removeAuth(peer_id)
-- Misc
tutorial_completed = server.getTutorial() -- チュートリアル中かを取得できます
server.setTutorial() -- チュートリアルをアクティブにするかを設定できます(独自のチュートリアルを作るのに便利です)
以下のスクリプトはどのようにしてプレイリスト(Lua)からミッションロケーションをスポーンさせればよいのかのサンプルです。
spawned_mission_objects = {}
function onCustomCommand(full_message, user_peer_id, is_admin, is_auth, command, arg1, arg2, arg3, arg4)
--example use: ?spawnLocation my_boat 50 15 1000
--example use: ?spawnLocation my_mission 0 0 0
if command == "?spawnLocation" and is_admin == true then
local playlist_index = server.getPlaylistIndexCurrent()
server.spawnMissionLocation(matrix.translation(arg2,arg3,arg4), playlist_index, server.getLocationIndexByName(playlist_index,arg1))
-- 例えばロケーションで港を選択し、そこに配置した物をスポーンさせようとすると以下のように書ける
-- server.spawnMissionLocation(matrix.translation(0,0,0), playlist_index, server.getLocationIndexByName(playlist_index,"TERMINAL SPYCAKES"))
end
if command == "?despawn" and is_admin == true then
for id, object in pairs(spawned_mission_objects) do
if object["type"] == "vehicle" then
server.despawnVehicle(id, true)
else
server.despawnMissionObject(id, true)
end
-- [メモ] ここのtrueをfalseに変えると、即座にデスポーンではなくプレイヤーが離れたときにデスポーンするようになる
end
end
end
POSITION_TYPE |
0 = fixed,
1 = vehicle,
2 = object
MARKER_TYPE |
0 = delivery_target,
1 = survivor,
2 = object,
3 = waypoint,
4 = tutorial,
5 = fire,
6 = shark,
7 = ice,
8 = search_radius
LABEL_TYPE |
0 = none,
1 = cross,
2 = wreckage,
3 = terminal,
4 = military,
5 = heritage,
6 = rig,
7 = industrial,
8 = hospital,
9 = science,
10 = airport,
11 = coastguard,
12 = lighthouse,
13 = fuel,
14 = fuel_sell
NOTIFICATION_TYPE |
0 = new_mission,
1 = new_mission_critical,
2 = failed_mission,
3 = failed_mission_critical,
4 = complete_mission,
5 = network_connect,
6 = network_disconnect,
7 = network_info,
8 = chat_message,
9 = network_info_critical
PLAYER_LIST | {
[peer_index] = {
["id"] = peer_id,
["name"] = name,
["admin"] = is_admin,
["auth"] = is_auth,
["steam_id"] = steam_id
}
}
ZONE_LIST | {
[zone_index] = {
["name"] = name,
["transform"] = matrix,
["size"] = {x, y, z},
["radius"] = radius,
["type"] = ZONE_TYPE,
["tags"] = {[i] = tag}
}
}
ZONE_TYPE |
0 = box,
1 = sphere,
2 = radius
EQUIPMENT_ID |
0 = none,
1 = diving,
2 = firefighter,
3 = scuba,
4 = parachute,
5 = arctic,
6 = binoculars,
7 = cable,
8 = compass,
9 = defibrillator,
10 = fire_extinguisher,
11 = first_aid,
12 = flare,
13 = flaregun,
14 = flaregun_ammo,
15 = flashlight,
16 = hose,
17 = night_vision_binoculars,
18 = oxygen_mask,
19 = radio,
20 = radio_signal_locator,
21 = remote_control,
22 = rope,
23 = strobe_light,
24 = strobe_light_infrared,
25 = transponder,
26 = underwater_welding_torch,
27 = welding_torch
PLAYLIST_DATA | {
["name"] = name,
["path_id"] = folder_path,
["file_store"] = is_app_data,
["location_count"] = location_count
}
LOCATION_DATA | {
["name"] = name,
["tile"] = tile_filename,
["env_spawn_count"] = spawn_count,
["env_mod"] = is_env_mod,
["object_count"] = object_count,
["component_count"] = component_count
}
COMPONENT | {
["name"] = name,
["display_name"] = display_name,
["type"] = type,
["x"] = x,
["y"] = y,
["z"] = z,
["transform"] = matrix,
["id"] = object_id / vehicle_id
}
COMPONENT_DATA | {
["name"] = name,
["display_name"] = display_name,
["type"] = TYPE_STRING,
["id"] = component_id,
["dynamic_object_type"] = OBJECT_TYPE,
["tags"] = {[i] = tag},
["transform"] = matrix,
["character_outfit_type"] = OUTFIT_TYPE
}
TYPE_STRING |
"zone",
"object",
"character",
"vehicle",
"flare",
"fire",
"loot",
"button",
"animal",
"ice",
"cargo_zone"
OBJECT_TYPE |
0 = none,
1 = character,
2 = crate_small,
3 = collectable,
4 = basketball,
5 = television,
6 = barrel,
7 = schematic,
8 = debris,
9 = chair,
10 = trolley_food,
11 = trolley_med,
12 = clothing,
13 = office_chair,
14 = book,
15 = bottle,
16 = fryingpan,
17 = mug,
18 = saucepan,
19 = stool,
20 = telescope,
21 = log,
22 = bin,
23 = book_2,
24 = loot,
25 = blue_barrel,
26 = buoyancy_ring,
27 = container,
28 = gas_canister,
29 = pallet,
30 = storage_bin,
31 = fire_extinguisher,
32 = trolley_tool,
33 = cafetiere,
34 = drawers_tools,
35 = glass,
36 = microwave,
37 = plate,
38 = box_closed,
39 = box_open,
40 = desk_lamp,
41 = eraser_board,
42 = folder,
43 = funnel,
44 = lamp,
45 = microscope,
46 = notebook,
47 = pen_marker,
48 = pencil,
49 = scales,
50 = science_beaker,
51 = science_cylinder,
52 = science_flask,
53 = tub_1,
54 = tub_2,
55 = filestack,
56 = barrel_toxic,
57 = flare,
58 = fire,
59 = animal,
60 = map_label,
61 = iceberg,
62 = small_flare,
63 = big_flare
OUTFIT_TYPE |
0 = none,
1 = worker,
2 = fishing,
3 = waiter,
4 = swimsuit,
5 = military,
6 = office,
7 = police,
8 = science,
9 = medical,
10 = wetsuit,
11 = civilian
GAME_SETTINGS |
"third_person",
"third_person_vehicle",
"vehicle_damage",
"player_damage",
"npc_damage",
"sharks",
"fast_travel",
"teleport_vehicle",
"starting_currency",
"rogue_mode",
"auto_refuel",
"megalodon",
"map_show_players",
"map_show_vehicles",
"show_3d_waypoints",
"show_name_plates",
"day_night_length", -- 現在書き込み不可
"sunrise", -- 現在書き込み不可
"sunset", -- 現在書き込み不可
"infinite_money",
"settings_menu",
"unlock_all_islands",
"infinite_batteries",
"infinite_fuel",
"engine_overheating",
"no_clip",
"map_teleport",
"cleanup_vehicle",
"clear_fow", -- clear fog of war
"vehicle_spawning",
"photo_mode",
"respawning",
"settings_menu_lock",
"despawn_on_leave",
"unlock_all_components"
Stormworksは、スクリプトでオブジェクトの位置を変えるのに便利ないくつかの行列関数を提供します。
matrix = matrix.multiply(matrix1, matrix2)
matrix = matrix.invert(matrix)
matrix = matrix.transpose(matrix)
matrix = matrix.identity()
matrix = matrix.rotationX(radians)
matrix = matrix.rotationY(radians)
matrix = matrix.rotationZ(radians)
matrix = matrix.translation(x,y,z)
x,y,z = matrix.position(matrix)
dist = matrix.distance(matrix1, matrix2)
上のほとんどの関数は引数として行列を受け取ります。
行列を直接使用したくない場合は、例えば以下のようにして使用します。
-- [v1.0.21] is_successを一緒に返す関数を引数に使う場合、余分な括弧が必要
local x, y, z = matrix.position((server.getPlayerPos(1)))
server.setPlayerPos(1, matrix.translation(x, y, z))
function onTick(game_ticks)
end
このスクリプトのAPIは非常に強力ですが、以下の点に注意する必要があります。
peer_id
に-1を指定すると接続している全員に送信することができます。g_savedata
という変数に入れた内容はセーブデータ毎にlua_table.xml
に保存されます。これにより、セーブ&ロードを挟んでもLua内の状態を維持できます。最後になりますが、これらのスクリプティング機能が提供するほぼ無限の可能性をお楽しみください。 Stormworksではスクリプト作成の仕組みをわかりやすく説明していますが、ここに取り上げていない質問がある場合は、公式Discord(一時停止メニューからアクセス可能、英語)に参加してください。
このページはゲーム内の公式Luaガイドを元に和訳し、ついでに「こんな情報もあったらいいな」を追記していく感じのページです。
GitHubでホストしてるので、gitできる方は手伝ってくださるとありがたいです。
公式Luaガイドを目視かOCRで見比べながら更新しているため、何かミスがあるかもしれません。あとアップデートが激しいときは落ち着くまで待ってから更新することにしています。
ページ先頭にバージョンを記載しています。最新の情報が必要な場合はゲーム内リファレンス(英語)を参照してください。
v1.0.21で行われたLuaへの大きな変更で燃え尽きました。カテゴリごと変わってたりするので、追従するには修正というレベルを超えた改稿が必要になるかもしれません。
使用したもの...
Materialize - CSS Framework
Prism - Syntax Highlight
作業者: イスターリャ(@is_ptcm)