Move docs to luacs-docs/{lua,cs,landing-page}
luacs-docs/cs also has a proper http server for testing locally
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
# Common Questions
|
||||
|
||||
## What does attempt to index a nil value mean!?
|
||||
It means you tried to access a field of something that doesn't exist, for example:
|
||||
```
|
||||
local thisIsNil = nil
|
||||
print(thisIsNil.what)
|
||||
print(thisIsNil.something())
|
||||
```
|
||||
|
||||
## Why i'm getting "Cannot access field test of userdata<something>" errors?
|
||||
Because you are trying to access a field in a C# class that doesn't exist.
|
||||
```
|
||||
print(Item.thisDoesntExist)
|
||||
```
|
||||
|
||||
## What is "Attempt to access instance member "Drop" from a static userdata"
|
||||
It means you are trying to access a member that requires an instance to accessed, for example:
|
||||
```
|
||||
-- there's a static global class called Item, but because it's static, you can't call item specific
|
||||
-- things on it, this will error.
|
||||
Item.Drop()
|
||||
Item.ItemList[1].Drop() -- this won't error, it will drop the first item ever created.
|
||||
```
|
||||
|
||||
## I'm getting a super big error with things related to the C# side
|
||||
It usually happens when you call something on the C# side and you provide nil inputs, you can get a better idea of it by analyzing the error message and trying to link it your lua code, for example:
|
||||
```
|
||||
Game.SendMessage(nil)
|
||||
```
|
||||
This will result in
|
||||
```
|
||||
[LUA ERROR] System.NullReferenceException: Object reference not set to an instance of an object.
|
||||
at Barotrauma.Networking.ChatMessage.GetChatMessageCommand(String message, String& messageWithoutCommand)
|
||||
at Barotrauma.Networking.GameServer.SendChatMessage(String message, Nullable`1 type, Client senderClient,
|
||||
Character senderCharacter, PlayerConnectionChangeType changeType)
|
||||
```
|
||||
You can easily tell that the error has something to do with chat messages, and by looking back at your Lua code you can easily see whats causing it.
|
||||
|
||||
## How do i list all clients, characters and items?
|
||||
```
|
||||
for _, client in pairs(Client.ClientList) do
|
||||
|
||||
end
|
||||
|
||||
for _, character in pairs(Character.CharacterList) do
|
||||
|
||||
end
|
||||
|
||||
for _, item in pairs(Item.ItemList) do
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
## Running pairs() on an enumerator doesn't work!
|
||||
pairs() Returns an enumerator that iterates through the entire table keys, if you already have an enumerator, you can just pass it in directly.
|
||||
```
|
||||
-- get first item ever created and loop through all the items stored inside it.
|
||||
for item in Item.ItemList[1].OwnInventory.AllItems do
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
## How do i spawn an item?
|
||||
```
|
||||
local prefab = ItemPrefab.GetItemPrefab("screwdriver")
|
||||
local firstPlayerCharacter = Client.ClientList[1].Character
|
||||
|
||||
-- Spawn on the world
|
||||
Entity.Spawner.AddItemToSpawnQueue(prefab, firstPlayerCharacter.WorldPosition, nil, nil, function(item)
|
||||
print(item.Name .. " Has been spawned.")
|
||||
end)
|
||||
|
||||
-- Spawn inside an inventory
|
||||
Entity.Spawner.AddItemToSpawnQueue(prefab, firstPlayerCharacter.Inventory, nil, nil, function(item)
|
||||
print(item.Name .. " Has been spawned.")
|
||||
end)
|
||||
```
|
||||
|
||||
## How do i give a character a certain affliction
|
||||
|
||||
```
|
||||
local burnPrefab = AfflictionPrefab.Prefabs["burn"]
|
||||
|
||||
local char = Character.CharacterList[1]
|
||||
local limb = char.AnimController.MainLimb
|
||||
-- or char.AnimController.Limbs[1]
|
||||
|
||||
char.CharacterHealth.ApplyAffliction(limb, burnPrefab.Instantiate(100))
|
||||
|
||||
```
|
||||
|
||||
## How do i get the amount of a affliction that a character has?
|
||||
|
||||
```
|
||||
local char = Character.CharacterList[1]
|
||||
|
||||
print(char.CharacterHealth.GetAffliction("burn"))
|
||||
-- or
|
||||
print(char.CharacterHealth.GetAffliction("burn", char.AnimController.Limbs[1]))
|
||||
```
|
||||
|
||||
## How do i send a private chat message?
|
||||
|
||||
```
|
||||
local chatMessage = ChatMessage.Create("Sender name", "text here", ChatMessageType.MessageBox, nil, nil)
|
||||
chatMessage.Color = Color(255, 255, 0, 255)
|
||||
Game.SendDirectChatMessage(chatMessage, Client.ClientList[1])
|
||||
```
|
||||
|
||||
## How do i teleport a character or an item?
|
||||
|
||||
```
|
||||
|
||||
-- teleports an item to 0, 0
|
||||
local item = Item.ItemList[1]
|
||||
item.SetTransform(Vector2(0, 0), item.Rotation)
|
||||
|
||||
-- teleports a character to 0, 0
|
||||
local character = Client.ClientList[1].Character
|
||||
character.TeleportTo(Vector2(0, 0))
|
||||
```
|
||||
@@ -0,0 +1,103 @@
|
||||
# Getting started
|
||||
|
||||
If you want to learn how Lua works and the syntax, you can check these websites: [https://www.lua.org/manual/5.2/](https://www.lua.org/manual/5.2/) [https://www.tutorialspoint.com/lua/lua_overview.htm](https://www.tutorialspoint.com/lua/lua_overview.htm)
|
||||
|
||||
## How mods are executed
|
||||
When the server finishes loading everything, Lua For Barotrauma starts up and reads the file `Lua/LuaSetup.lua` and executes it, this Lua script registers classes to be available on the Lua side, creates static references and puts them in the global space, and then goes on each mod and executes their Autorun if the content package is enabled, and after that, goes on each mod, and executes ForcedAutorun which executes even if the content package is disabled.
|
||||
|
||||
## Creating your first mod
|
||||
When creating a new Lua mod, you will need to create a new folder on the **LocalMods** folder, then inside this folder you will need to create a folder called **Lua**, and then inside the Lua folder you create a folder called **Autorun**, inside this folder you can add your Lua scripts that will be executed automatically, it will look something like this `LocalMods/MyMod/Lua/Autorun/test.lua`, remember that your mod will only work if it's a valid xml mod that has a filelist.xml. If you use ForcedAurorun, the mod executes even if it's disabled.
|
||||
|
||||
Now you can open **test.lua** in your favorite text editor (<a href="https://code.visualstudio.com/" target="_blank">VSCode</a> with the <a href="https://marketplace.visualstudio.com/items?itemName=sumneko.lua" target="_blank">Lua Sumneko extension</a> recommended) and type in **print("Hello, world")**, now start the server by hosting a game or running the server executable manually, you will see in your console a text appear with the text that you entered in print, you can now type in the server console `reloadlua`, this will re-execute all the Lua scripts. You can also type in `lua yourscript` to run a short lua snippet, like this `lua print('Hello, world')`, remember to remove double quotes, because Barotrauma console automatically formats those.
|
||||
**Note: When you host a server via the in game menus, you won't be able to see the first server debug prints, because the server will print those before your client joins the server.**
|
||||
|
||||
## Including other files
|
||||
|
||||
If you wish to separate your Lua scripts into multiple files, you can do it by either having multiple scripts in the Autorun folder, or having a single script that is responsible for executing the rest of your Lua scripts, for that you will need to get relative paths to your mod, here's an example of how to do it:
|
||||
|
||||
```
|
||||
-- this variable will be accessible to any other script, so you can use it to get the mod's path.
|
||||
MyModGlobal = {}
|
||||
|
||||
MyModGlobal.Path = ...
|
||||
|
||||
dofile(MyModGlobal.Path .. "/Lua/yourscript.lua")
|
||||
```
|
||||
|
||||
Alternatively, you may use the `require` function to load Lua scripts by their module name (file name without `.lua` inside your mod's `Lua/` folder, subfolders are dot-separated). This is slightly different from `dofile`; `require` will return whatever the Lua script you are calling returns. Also, `require` will execute the Lua script only the first time, and will just return its original return value on subsequent calls.
|
||||
|
||||
```
|
||||
local MyMod = {
|
||||
Path = ... -- Get path to this mod/content package, exact same as MyModGlobal.Path = ... above.
|
||||
}
|
||||
|
||||
-- Equivalent to: dofile(MyMod.Path .. "/Lua/MyScript.lua")
|
||||
require "MyScript"
|
||||
|
||||
-- Equivalent to: dofile(MyMod.Path .. "/Lua/Subfolder1/Subfolder2/Subfoler3/MyScript.lua")
|
||||
require "Subfolder1.Subfolder2.Subfolder3.MyScript"
|
||||
```
|
||||
|
||||
Note that `require` does not necessitate the use of `MyMod.Path = ...`, instead it looks for the first match in the `Lua/` folder of every enabled (including forced ran) Barotrauma mod. This means that you can execute the scripts of other Barotrauma mods. To ensure that you do not inadvertently execute a script from a different Barotrauma mod, you should use a unique subfolder name for your scripts.
|
||||
|
||||
```
|
||||
-- Generic: Bad, likely to execute MyScript.lua from a different mod from ours.
|
||||
-- AnyModPath/Lua/MyScript.lua
|
||||
require "MyScript"
|
||||
|
||||
-- More unique: Good, less likely to execute MyScript from a different mod from ours.
|
||||
-- AnyModPath/Lua/MyName/MyMod/MyScript.lua
|
||||
require "MyName.MyMod.MyScript"
|
||||
```
|
||||
|
||||
`require` will return whatever the script it loads returns.
|
||||
|
||||
```
|
||||
-- Inside MyScript.lua:
|
||||
local MyString = "Hello World!"
|
||||
|
||||
return MyString
|
||||
|
||||
-------------------------------
|
||||
|
||||
-- Inside another script:
|
||||
local MyString = require "MyScript"
|
||||
|
||||
-- Will print "Hello World!"
|
||||
print(MyString)
|
||||
```
|
||||
|
||||
The main advantages of `require` over `dofile` are: 1. multiple scripts can try to load the same file without executing its contents multiple times but instead only once; and 2. scripts can load scripts from third-party mods without having to know their paths. It is an alternative over using global variables for sharing data between scripts; while a script needs to wait for a global variable to be initialised first, you can instead bundle the variable initalisation logic (which must only run once) with a script that also returns the variable (in a table) for other mods to `require` and use.
|
||||
|
||||
Find more information about `require` in the Programming in Lua book: http://www.lua.org/pil/8.1.html.
|
||||
|
||||
## Error handling
|
||||
|
||||
Sometimes you may expect an error to happen when you call specific functions in your script. Errors will stop the execution of your script unless they are handled correctly.
|
||||
|
||||
`pcall` (protected call) is a function that allows you to call another function in *protected mode*, which means that any errors that occur will be caught and a status code will be returned that your script can use to understand whether the function failed or succeeded, and the type of error that occured.
|
||||
|
||||
```
|
||||
-- Require a third-party script, risky as it raises an error if the user does not have it installed.
|
||||
local result = require "ThirdPartyAuthor.ThirdPartyMod.ThirdPartyScript"
|
||||
-- Only prints if the above does not raise an error.
|
||||
print(result)
|
||||
|
||||
-- Same as above, except any error is handled.
|
||||
local ok, result = pcall(require, "ThirdPartyAuthor.ThirdPartyMod.ThirdPartyScript")
|
||||
-- `ok` is true if no error, false if there is an error.
|
||||
if ok then
|
||||
-- No error, print our result!
|
||||
print(result)
|
||||
else
|
||||
-- There was an error, in this case `result` is a string containing the error code.
|
||||
print("Error when loading third-party script: ", result)
|
||||
end
|
||||
```
|
||||
|
||||
Read more about error handling in the Programming in Lua book: http://www.lua.org/pil/8.4.html. Confer also the Lua 5.2 reference manual: http://www.lua.org/manual/5.2/manual.html.
|
||||
|
||||
## Learning the libraries
|
||||
In the sidebar of the documentation, you can see a tab named Code, in there you can check out all the functions and fields that each class has, each one of them has a box with a color on it, where <span class="realm server"></span> means Server-Side, <span class="realm client"></span> means Client-Side and <span class="realm shared"></span> means both Server-Side and Client-Side, by clicking on them you can learn more about them. Not everything is documented here, theres stuff missing that still needs to be added, if you want to find more in-depth functions and fields in the Barotrauma classes, you should check the Barotrauma source code.
|
||||
|
||||
See <a href="../lua-examples" target="_blank">Lua Examples</a>, <a href="../common-questions" target="_blank">Common Questions</a>
|
||||
@@ -0,0 +1,51 @@
|
||||
# How to use hooks
|
||||
|
||||
Hooks are basically functions that get called when events happen in-game, like chat messages. They can be triggered either by Lua itself or the game code.
|
||||
|
||||
## Adding hooks
|
||||
|
||||
Hooks can be added like this:
|
||||
|
||||
```
|
||||
Hook.Add("chatMessage", "test", function(message, client)
|
||||
print(client.Name .. " has sent " .. message)
|
||||
end)
|
||||
```
|
||||
|
||||
The event name (first argument), is case-insensitive, so you can call it chatMessage, cHaTmEsSaGe, chatmessage, etc.
|
||||
|
||||
## Calling hooks
|
||||
|
||||
You can also call hooks with the following code:
|
||||
|
||||
```
|
||||
Hook.Call("myCustomEvent", {"some", "arguments", 123})
|
||||
```
|
||||
|
||||
## XML Status Effect Hooks
|
||||
|
||||
With Lua, a new XML tags is added, it can be used to call Lua hooks inside status effects:
|
||||
|
||||
```
|
||||
<StatusEffect type="OnUse">
|
||||
<LuaHook name="doSomething" />
|
||||
</StatusEffect>
|
||||
```
|
||||
|
||||
```
|
||||
Hook.Add("doSomething", "something", function (effect, deltaTime, item, targets, worldPosition)
|
||||
print(effect, ' ', item)
|
||||
end)
|
||||
```
|
||||
|
||||
## Patching
|
||||
|
||||
Patching allows you to hook into existing methods in the game code, notice that it can be a little unstable depending on the method that you are patching, so be aware.
|
||||
|
||||
```
|
||||
Hook.HookMethod("Barotrauma.CharacterInfo", "IncreaseSkillLevel", function (instance, ptable)
|
||||
print(string.format("%s gained % xp", instance.Character.Name, ptable.increase))
|
||||
end, Hook.HookMethodType.After)
|
||||
```
|
||||
|
||||
If you return anything other than nil, it will stop the execution of the method, if the method has a return type, it will also return what you returned in the Lua function. (Only in Hook.HookMethodType.After)
|
||||
@@ -0,0 +1,88 @@
|
||||
# Installing Lua For Barotrauma Manually
|
||||
|
||||
## Notice: Using the LuaForBarotrauma package is not required if it's installed manually, but you may use it anyway if you wish to support the mod, since players automatically download packages when joining the server.
|
||||
|
||||
## If you are getting TextManager errors, open config_player.xml in your server and change the language from None to English
|
||||
|
||||
## Adding Lua For Barotrauma to an existing server
|
||||
1 - Download [latest version of LuaForBarotrauma](https://github.com/evilfactory/Barotrauma-lua-attempt/releases/tag/latest), choose the correct platform in the assets drop down.<br>
|
||||
2 - Extract the zip file<br>
|
||||
3 - Copy the following files inside the extracted zip:<br>
|
||||
|
||||
- **DedicatedServer.deps.json**
|
||||
- **DedicatedServer.dll**
|
||||
- **DedicatedServer.pdb**
|
||||
- **0Harmony.dll**
|
||||
- **MoonSharp.Interpreter.dll**
|
||||
- **MonoMod.Common.dll**
|
||||
- **Mono.Cecil.dll**
|
||||
- **Mono.Cecil.Mdb.dll**
|
||||
- **Mono.Cecil.Pdb.dll**
|
||||
- **Mono.Cecil.Rocks.dll**
|
||||
- **Microsoft.CodeAnalysis.CSharp.Scripting.dll**
|
||||
- **Microsoft.CodeAnalysis.CSharp.dll**
|
||||
- **Microsoft.CodeAnalysis.dll**
|
||||
- **Microsoft.CodeAnalysis.Scripting.dll**
|
||||
- **System.Collections.Immutable.dll**
|
||||
- **System.Reflection.Metadata.dll**
|
||||
- **System.Runtime.CompilerServices.Unsafe.dll**
|
||||
- file that starts with **mscordaccore\_amd64\_amd64\_**
|
||||
- and the **Lua/** folder
|
||||
|
||||
4 - Paste them to your existing server, and let it replace the files<br>
|
||||
|
||||
## Adding Lua For Barotrauma to an existing client
|
||||
|
||||
Same as above, but instead you need to copy/replace the following files:
|
||||
|
||||
- **Barotrauma.deps.json**
|
||||
- **Barotrauma.dll**
|
||||
- **Barotrauma.pdb**
|
||||
- **0Harmony.dll**
|
||||
- **MoonSharp.Interpreter.dll**
|
||||
- **MonoMod.Common.dll**
|
||||
- **Mono.Cecil.dll**
|
||||
- **Mono.Cecil.Mdb.dll**
|
||||
- **Mono.Cecil.Pdb.dll**
|
||||
- **Mono.Cecil.Rocks.dll**
|
||||
- **Microsoft.CodeAnalysis.CSharp.Scripting.dll**
|
||||
- **Microsoft.CodeAnalysis.CSharp.dll**
|
||||
- **Microsoft.CodeAnalysis.dll**
|
||||
- **Microsoft.CodeAnalysis.Scripting.dll**
|
||||
- **System.Collections.Immutable.dll**
|
||||
- **System.Reflection.Metadata.dll**
|
||||
- **System.Runtime.CompilerServices.Unsafe.dll**
|
||||
- file that starts with **mscordaccore\_amd64\_amd64\_**
|
||||
- and the **Lua/** folder
|
||||
|
||||
|
||||
## Using Lua For Barotrauma from scratch
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/ov0MUOUVB7A" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
1 - Download [latest version of LuaForBarotrauma](https://github.com/evilfactory/Barotrauma-lua-attempt/releases/tag/latest), choose the correct platform in the assets drop down.<br>
|
||||
|
||||
2 - Extract the zip file<br>
|
||||
|
||||
3 - Find the Content folder in your original Barotrauma game: <br>
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
4 - Copy the Content folder to the extracted folder <br>
|
||||
|
||||

|
||||
|
||||
6 - Optional: Copy config_player.xml from your original game so it retains your configurations.
|
||||
|
||||
7 - Done! Now run DedicatedServer.exe to run the modded server or run Barotrauma.exe to run the modded client.<br>
|
||||
|
||||
### Linux notice
|
||||
Sometimes you will get steam initialization errors, most of the time it's because it's missing the linux64/steamclient.so binary, so you can just copy the binary from your steam instalation over to the folder and it should work.
|
||||
|
||||
|
||||
|
||||
## Checking if everything is working
|
||||
|
||||
If the commands `reloadlua` or `cl_reloadlua` work without errors, it means you successfully installed the mod.
|
||||
@@ -0,0 +1,87 @@
|
||||
# Lua Examples
|
||||
|
||||
```
|
||||
Hook.Add('chatMessage', 'suicide_mod', function(msg, client)
|
||||
if msg == '!suicide' and client.Character ~= nil then
|
||||
client.Character.Kill(CauseOfDeathType.Unknown)
|
||||
Game.SendMessage(client.name .. ' killed himself!', ChatMessageType.Server)
|
||||
return true -- hide message
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
```
|
||||
Hook.Add("itemApplyTreatment", "testItemApplyTreatment", function (item, user, character, targetlimb)
|
||||
if item.Prefab.Identifier == "antibleeding1" then
|
||||
local pos = character.WorldPosition
|
||||
Game.Explode(pos, 1, 500, 5000, 5000, 5000)
|
||||
|
||||
Game.RemoveItem(item)
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
```
|
||||
-- for example: create an item in xml named RandomComponent and add the wiring inputs/outputs trigger_random and random_out
|
||||
Hook.Add("signalReceived", "signalReceivedTest", function (signal, connection)
|
||||
if connection.Item.Prefab.Identifier == "RandomComponent" and connection.Name == "trigger_random" then
|
||||
connection.Item.SendSignal(tostring(Random.Range(0, 100)), "random_out")
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
```
|
||||
local discordWebHook = "your discord webhook here"
|
||||
|
||||
local function escapeQuotes(str)
|
||||
return str:gsub("\"", "\\\"")
|
||||
end
|
||||
|
||||
Hook.Add("chatMessage", "discordIntegration", function (msg, client)
|
||||
local escapedName = escapeQuotes(client.name)
|
||||
local escapedMessage = escapeQuotes(msg)
|
||||
|
||||
Networking.RequestPostHTTP(discordWebHook, function(result) end, '{\"content\": \"'..escapedMessage..'\", \"username\": \"'..escapedName..'\"}')
|
||||
end)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
```
|
||||
-- by jimmyl
|
||||
Hook.Add("chatMessage","controlhuskcommand",function(msg, client)
|
||||
if msg == "!controlhusk" then
|
||||
if client.Character ~= nil then
|
||||
if not client.Character.IsDead then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if not client.InGame then
|
||||
return true
|
||||
end
|
||||
|
||||
local chars = Character.CharacterList
|
||||
local suitablechars = {}
|
||||
for i = 1, #chars, 1 do
|
||||
local charat = chars[i]
|
||||
if not charat.IsDead and string.match(string.lower(charat.SpeciesName.Value), "husk") and not charat.IsRemotelyControlled then
|
||||
table.insert(suitablechars, charat)
|
||||
end
|
||||
end
|
||||
|
||||
if #suitablechars >= 1 then
|
||||
client.SetClientCharacter(suitablechars[Random.Range(1, #suitablechars)])
|
||||
end
|
||||
|
||||
return true -- hide message
|
||||
end
|
||||
end)
|
||||
```
|
||||
Reference in New Issue
Block a user