Stormworks(v1.0.21) Lua Reference

Default Game Commands

以下のコマンドはゲーム中いつでも呼び出せます。

						
							?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 Scripting Overview

Luaスクリプティングはあなたに高度なミッションやカスタムゲームモードを作るためのツールを提供します。
Stormworksでは、あなたのスクリプトがゲームを操作できるようないくつもの機能を提供しています。

このガイドは利用可能な関数について説明しますが、Luaスクリプティングを始めるためのチュートリアルではありません。

API General Info
  • Peer IDはプレイヤーリストの左側で確認できる
  • Y座標は高さを表します(そのためマップ上の座標はxとz)
  • peer_idに-1を渡すと全員を指定できる
  • g_savedataテーブルに入れた変数はセーブデータ毎に保存される
  • ワールド生成時に1度だけ実行したい場合はonCreate()is_world_createがtrueのときに実行すれば良い
  • onCreate()内でserver.announce()を使うとワールド生成/接続前に送信されるため、メッセージを受け取れない
  • Luaの配列は添字が1からなので注意
Callback Functions

以下は条件を満たした際に自動的に呼ばれる、スクリプトで使えるコールバック関数です。
最もシンプルなコールバック関数は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
						
					
Server Functions

以下のいくつかの関数はすべて大文字の引数を含みます。それらの変数の説明は後述します。

						
							-- 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
						
					
Types and Info
						
							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"
						
					
Matrix Manipulation

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
						
					
Lua Functions

以下のグローバルLua関数が使用可能です。

および、以下のライブラリを使うことができます。

これらのライブラリによって提供される関数の詳しい説明はドキュメント(英語)を参照してください。
※本ページではリストの内容に対し、非公式に翻訳された日本語版リファレンスにリンクしています。

Meta from the Devs

このスクリプトのAPIは非常に強力ですが、以下の点に注意する必要があります。

  • スクリプトの最大実行時間は1000ミリ秒ですが、ゲームを著しく重くするスクリプトを作成することは可能です。スクリプトが効率的に実行されるようにするのはユーザーの責任です。
  • peer_idに-1を指定すると接続している全員に送信することができます。
  • mathライブラリによる乱数はクライアントサイドで実行されます。マルチプレイで使用すると、同期されないことによる不都合が発生する場合があります。
  • Luaの状態は基本的に保存されませんが、g_savedataという変数に入れた内容はセーブデータ毎にlua_table.xmlに保存されます。これにより、セーブ&ロードを挟んでもLua内の状態を維持できます。
  • スクリプトをサンドボックス化するための多くの安全策が用意されていますが、ゲームをクラッシュさせる可能性のあるスクリプトを作成することは可能です。スクリプトでクラッシュさせた場合、何か(非常に)間違ったことをしている可能性があります。それはあなたの責任です。バグに遭遇したと思う場合は、Stormworks Issue Tracker(一時停止メニューからアクセス可能)で報告してください。
  • Steam Workshopでは、悪意のあるスクリプトや有害なスクリプトは許容されません。

最後になりますが、これらのスクリプティング機能が提供するほぼ無限の可能性をお楽しみください。 Stormworksではスクリプト作成の仕組みをわかりやすく説明していますが、ここに取り上げていない質問がある場合は、公式Discord(一時停止メニューからアクセス可能、英語)に参加してください。

About This Page

このページはゲーム内の公式Luaガイドを元に和訳し、ついでに「こんな情報もあったらいいな」を追記していく感じのページです。 GitHubでホストしてるので、gitできる方は手伝ってくださるとありがたいです。
公式Luaガイドを目視かOCRで見比べながら更新しているため、何かミスがあるかもしれません。あとアップデートが激しいときは落ち着くまで待ってから更新することにしています。

ページ先頭にバージョンを記載しています。最新の情報が必要な場合はゲーム内リファレンス(英語)を参照してください。
v1.0.21で行われたLuaへの大きな変更で燃え尽きました。カテゴリごと変わってたりするので、追従するには修正というレベルを超えた改稿が必要になるかもしれません。

使用したもの...
Materialize - CSS Framework
Prism - Syntax Highlight

作業者: イスターリャ(@is_ptcm)