Forts
Not enough ratings
Mod Scripts Guide
By AlexD
This is a guide where you learn how to use scripts in mods. These scripts have the power to make UI elements, create and destroy devices and weapons, make people win, and more! Please go through the official modding guide first: https://steamcommunity.com/sharedfiles/filedetails/?id=1296612027

By Alexc96161
   
Award
Favorite
Favorited
Unfavorite
Introduction
Warning: This guide is expected to be improved and is not perfect. If you find any problems in the guide please let me know!

This tutorial will teach you how to use mod script. It will show you how to make UI and interact with weapons!

Before you start with this tutorial please complete this official modding tutorial:
Official modding guide

Here is the final product if you want to look at:
Link to demo mod
Once you have subscribed to it navigate to your Steam library where Forts is installed and then steamapps\workshop\content\410900\1914710945. Keep in mind there will be a file named published field. That is only there on workshop items, so your version of the mod should NOT have it if you have not published the mod. Also keep in mind that there may be some minor differences and a lot more comments.

If you get stuck in any bit of the tutorial you can PM me on discord on the official Forts discord
( https://discord.gg/BMpnRWm ), on steam or you can ask me questions in the comments of this guide. You can also look at the final product to see what you may have done wrong.

With all of that out of the way let’s start with the setup.
Setup
Lets start with the setup.

It is best if you use notepad++ to edit lua scripts as it has many useful features and tools, but other text editors will work as well.

To start right clicking on Forts in Steam, then the properties tab, then press the local files button. You should now see the data folder. Navigate to the data folder and then the mods folder. Now you will need to make a folder called Mod Script Demo. In this you will need to create a few files and folders.

First you will need to create a file called displayname.lua in this file you will need to add a little code. As the name implies this file is where the display name of the mod goes. You need to put this code in the file:
DisplayName = { ['English'] = L"Mod Script Demo", }
This code sets the English display name of the mod (You will see this when selecting the mod in game.) You can also have different names for different languages, but I will not go over that here.

We now need to create a file called itemversion.lua we will need to put a tiny bit of code in this file.
ItemVersion = 1
The item version automatically increases by one every time you update you mod if it is on the workshop. While you will not be publishing a mod in this tutorial you will hopefully have everything setup so you can upload it if you feel.

Next we need to make a file called mod.lua there is a tiny bit of code that you need to put here:
Selectable = true Category = "Misc"
This code makes the mod be able to be selected and puts it in the Misc category in the mod selection screen.

Now we shall create a file called script.lua we will not put any code in it yet but keep in mind this is where most of the mod scripting is done.

We shall now create a folder called db and in this folder we should make a file called sprites.lua, we will not put any code in this file yet but keep in mind this is where we will get sprites to use for our mod scripting.

Finally we need to make a folder called UI and in it we need to put four sprites button sprites:
https://ibb.co/qgFfyg7
https://ibb.co/CwfXtQq
https://ibb.co/8bztTty
https://ibb.co/9r2srFs
These sprites should be named ButtonSprite-A, ButtonSprite-D, ButtonSprite-R and ButtonSprite-S. These will be used by sprites.lua to make a button sprite.

Congrats you are done with the setup! We will now go on to start coding the map script and setting up the sprites.

Sprites
Now that we are all setup we can start working on sprites.

As you may remember from the setup we created a file called sprites.lua in the db folder of our mod. This file has the ability to access and edit the sprites table, which is where you put your sprites. We cannot change or add to the sprites table from the script.lua file (Currently at least) so we need to add our sprites to the table in this file.

We need to put this code in sprites.lua:
table.insert( Sprites, { Name = "ButtonSprite", States = { Normal = { Frames = {{ texture = path .. "/UI/ButtonSprite-A.png", bottom = 0.664 },},}, Rollover = { Frames = {{ texture = path .. "/UI/ButtonSprite-R.png", bottom = 0.664 },},}, Pressed = { Frames = {{ texture = path .. "/UI/ButtonSprite-S.png", bottom = 0.664 },},}, Disabled = { Frames = {{ texture = path .. "/UI/ButtonSprite-D.png", bottom = 0.664 },},}, }, })
Here in this code we insert a sprite named button sprite It has four sprites: one for when normal/idle, one when you are hovering over it, one when you press it and one when it is disabled.

We take all of these sprites from our UI folder where we put the sprites to use.

Now we have a sprite in the sprite table which can be used in script.lua for a button later in this tutorial!

Into mod scripts
Now we shall begin mod scripting

Lets start by opening script.lua preferably using notepad++. The first thing we need to do is add a little code at the top of our file.
dofile("scripts/forts.lua")
this little bit of code is extremely important, and what it does is it loads the fort.lua file and allows us to use some important functions.

Now lets start by putting this code:
function Update(frame) end
this function gets called every frame and also gives you the frame number. This is very useful for things that need to be updated very frequently.

We now have a function that is called every frame and we are ready to start making things happen.




Exploding weapons for team 1
Now that we have the basics done we can start with a small and fun task. We will make weapons explode when placed.
Start by putting a team variable, this will be used later to make weapons explode for both teams
local team = 1
We first need to see how many weapons are on a side. To do this we need to use the function GetWeaponCountSide(side). So we will start with making all weapon on team one (side 1) explode. To start we get the number of weapons on that side by using the get weapon count function:
local weaponCount = GetWeaponCountSide(team)
From there we need to make a for loop for each weapon on team 1:
for i=0,weaponCount - 1 do end
We now need to get the ID of the weapon we want to destroy. To do this we use GetWeaponIdSide(side, number) function. Now add this code inside the for loop:
local weaponToDestroy = GetWeaponIdSide(team, i)
Finally we will destroy the weapon using this function ApplyDamageToDevice(deviceID, damage). You may be wondering, weapons are not devices why is it applying damage to a device. Well weapons are actually devices in the games view. We will also be using the ScheduleCall(time, function, parameter1, parameter2) to delay the destruction of the device, because it gives it a cooler effect and it teaches you how to use the ScheduleCall function. (Keep in mind that if the delay is longer the function would be called many times and a lot will be queued up but then need to be ignored as the device no longer exists. So to destroy the weapon put this code inside the loop.
ScheduleCall(0.01, ApplyDamageToDevice, weaponToDestroy, 10000)
Your final result should look like this
local team = 1 local weaponCount = GetWeaponCountSide(team) for i=0,weaponCount - 1 do local weaponToDestroy = GetWeaponIdSide(team, i) ScheduleCall(0.01, ApplyDamageToDevice, weaponToDestroy, 10000) end
Congrats. You now have made a mod which destroys all weapons for team one.
Exploding weapons for team 2
You now need to make exploding weapons for team two. I will not go over it in detail as it is just adding a loop. Here is what the code for to make weapons explode on team two:
local team = 1 while team < 3 do --make a loop to make this code effect both teams local weaponCount = GetWeaponCountSide(team) for i=0,weaponCount - 1 do local weaponToDestroy = GetWeaponIdSide(team, i) ScheduleCall(0.01, ApplyDamageToDevice, weaponToDestroy, 10000) end team = team + 1 end

You code in script.lua should now look like this:
dofile("scripts/forts.lua") function Update(frame) local team = 1 while team < 3 do local weaponCount = GetWeaponCountSide(team) for i=0,weaponCount - 1 do local weaponToDestroy = GetWeaponIdSide(team, i) ScheduleCall(0.01, ApplyDamageToDevice, weaponToDestroy, 10000) end team = team + 1 end end

If you test the mod in sandbox it should destroy any weapon you place.

You have now got a functioning mod to play with, but we still need to learn how to make UI and use the data table.
The data table
One thing we have yet to do is use the data table. Instead of using global variables we use the data table, as global variables cause often desyncs.

To start we need to declare something in the data table. We will start with a timer. At the top of your code just under the dofile put:
data.timer = 0

We can now use this in our update function. Every frame we want to add the length in second of that frame. To do this put this code somewhere in your update function:
data.timer = data.timer + data.updateDelta
data.updateDelta gets how long the last frame in seconds was. This is normally the best estimate of how long this current frame will be. The data table also has many useful variables which the game uses and we can also use, updateDelta is one of them.

We now have a timer variable which increases by one every second, we will use this variable in the UI in the next section.
Creating a text element
We now have code which destroys all weapons and has a timer which gets added to by one every second.

Now we shall start with UI. To begin we need to create the UI element, and we only do that when the game starts. We need to call this code when the game loads up. So we can use a function called Load. Before the update function but under the dofile and the data.timer creation we can put this code:
function Load(gamestart) end

This function is called when the game starts. Here we can create our text element:
AddTextControl("HUD", "TextTest", "Timer " .. data.timer, ANCHOR_BOTTOM_CENTER, Vec3(500, 500, 0), false, "Body")
We first have our parent of the text which is "HUD", it has the name of TextTest, we have content text which is our timer, it is anchored to the bottom center, it's local location of 500 in x, 500 in y and 0 in z, it is not world relative, and is has the "body" style for it's text.

This should create a text element just above the bottom HUD.

Sweet, you have now created a text element. Next we will update the text to match what the timer variable is every frame.

Changing a text element
We now need to update the text element every frame.

I would like mention that while I know quite a few things about mod scripts I am not and dev and do not know what everything does exactly. Now update the text we will use a function called SetControlText(Parent, TextName, Content).

To now use this we can put this at the bottom of the Update function:
SetControlText("HUD", "TextTest" , "Timer " .. math.floor(data.timer))
This will set the text to the timer value converted to an int.

Now we have created a mod which has a timer text element which gets added to every second and makes all weapons placed get destroyed. Congrats for making it this far.
Creating a button
We shall now make a button.

To start we need to use the function called AddButtonControl(Parent, Name, Sprite, Anchor, Size, Location, Style). This function creates a button. We will use the sprite we created and put in the sprites table for this button's sprite. You need to put this code in the load function:
AddButtonControl("HUD", "ButtonTest", "ButtonSprite", ANCHOR_TOP_CENTER, Vec3(32, 32, 0), Vec3(500, 450, 0), "Normal")
This will create a button just above the text.

We now have a button, next we will learn how to do things when the button is pressed.
On button click
We will now print "Test" to the log when you click the button.

To do this we will need to use a function called function OnControlActivated(name). It gets called when a button is clicked and gets the name of the button clicked. To use this we put this after the load function:
function OnControlActivated(name) end

In this function when our button is clicked we need to call the Log(text) function. You now need to use an if statement to check when the name is the same as the name of our button. We need to put this code in the OnControlActivated function:
if name == "ButtonTest" then end

Next we need to print "Test" in the log when the name is "ButtonTest". To do this we need to put this code in the if statement:
Log("Test")

Keep in mind that using this to change the game state, like for example destroying weapons when pressed would cause desyncs in multiplayer.

You now should have a functioning button which prints to the log when you press the button (You can look at the chat/log in a game by pressing the button bellow escape (~).)

You may want to test this in game. Just load up sandbox and choose this mod. Now you can test all the things in the mod.
Conclusion
Well done completing this tutorial. Here is the final code with a lot of comments, you can see if you have done anything wrong and what things do which I may not have explained here.
dofile("scripts/forts.lua") --Get the forts.lua script to use it's functions. VERY IMPORTANT ALWAYS DO THIS. data.timer = 0 --Make a timer variable on the data table. --This function is run when the game starts. function Load(gamestart) --[[ This line of code makes a text UI element. It has the name of TextTest, is a child of the HUD, has the content of "Timer" the time on the timer variable, is anchored to the bottom center, is at the local location of 500 in x, 500 in y and 0 in z, it is not world relative, an d is in the body style. --]] AddTextControl("HUD", "TextTest", "Timer " .. data.timer, ANCHOR_BOTTOM_CENTER, Vec3(500, 500, 0), false, "Body") --[[ This line of code makes a button UI element. It has the name of ButtonTest, is a child of the HUD, it gets the sprite from db/sprites.lua called ButtonSprite, is anchored to the top center, has the size of 32 in the x, 32 in the y and 0 in the z, is at the local location of 500 in x, 450 in y and 0 in z, and it is normal style. --]] AddButtonControl("HUD", "ButtonTest", "ButtonSprite", ANCHOR_TOP_CENTER, Vec3(32, 32, 0), Vec3(500, 450, 0), "Normal") end --This function runs every time a button is clicked and gets the button's name. This code will print test when the test button is clicked. function OnControlActivated(name) if name == "ButtonTest" then --If the button clicked is our button that we made (ButtonTest) then run the code in the if statement. Log("Test") --Print test to the log. end end --This function runs every frame and gets the current frame. This code will destroy all weapons and add to the timer text. function Update(frame) --This destroyed all weapons on both sides (Teams) local team = 1 while team < 3 do --make a loop to make this code effect both teams local weaponCount = GetWeaponCountSide(team) --This checks how many weapons team 1 has. for i=0,weaponCount - 1 do --This runs the code inside the loop for each weapon. local weaponToDestroy = GetWeaponIdSide(team, i) ScheduleCall(0.01, ApplyDamageToDevice, weaponToDestroy, 10000) end team = team + 1 end data.timer = data.timer + data.updateDelta --Add to the timer every frame by the amount the frame goes for in seconds SetControlText("HUD", "TextTest" , "Timer " .. math.floor(data.timer)) --Set the Timer text to what the timer is at (Change the float to a int to save space) end

I would like to thank you for sticking with this guide and I hope you make some great mods using mod script. Ask questions in the comments and I would love to see any mods you make, so send links to the in the comments if possible. :)

Made by: Alexc96161
4 Comments
Долги 125к Oct 15, 2023 @ 10:44am 
Didn't enjoyed the guide, 80% of times i wasn't know where to put code, i was completely blind so i had to just copy paste entire code at once, beginning of guide is good
AlexD  [author] Feb 6, 2020 @ 7:51pm 
That is correct, I forgot about that fact at the time. I will change when I have the chance.
[DEV] BeeMan  [developer] Feb 6, 2020 @ 4:04pm 
You don't need to create itemversion.lua as it's done automatically by the game the first time the mod is uploaded. You also never have to edit it as the game will modify it when necessary.
AlexD  [author] Nov 17, 2019 @ 10:03am 
Hello, for anyone who is stuck or has questions, please ask them here. :D