Lua/First Script: Difference between revisions

From Total War Modding
< Lua
(starting progress so I don't lose it all to a power outage)
 
mNo edit summary
Line 74: Line 74:
=== Start It ===
=== Start It ===
With our newly created file, our VSC layout will change ever so slightly. In "Open Editors", we'll see our fresh new baby script created; and in the central window, we'll see a new tab that's named the same as our new file, and an empty text file in the center with just the number "1" in it.
With our newly created file, our VSC layout will change ever so slightly. In "Open Editors", we'll see our fresh new baby script created; and in the central window, we'll see a new tab that's named the same as our new file, and an empty text file in the center with just the number "1" in it.
[[File:First script 6.png|none|thumb|1141x1141px|Notice the lil' moon symbol next to our file? That's because Lua is Portuguese for moon!]]
[[File:First script 6.png|none|thumb|894x894px|Notice the lil' moon symbol next to our file? That's because Lua is Portuguese for moon!]]
We'll kick off our script with the first piece of the puzzle, outputting some text to a file so we can read and interface with our scripts.
We'll kick off our script with the first piece of the puzzle, outputting some text to a file so we can read and interface with our scripts.
Let's get it going with a function called `out()`. This is a CA-defined function that they use all over their scripts, to print out to a text file various bits of info about the game, for debugging and logging purposes. It's a really handy system that'll let you know for certain what's actually going on in your scripts at runtime, since we can't truly test our script outside of the game.
To use the `out` function, we just have to write that word, put an open parenthesis, put in what we want to output, and then write a closing parenthesis. Let's try the old standard, and toss in the following:<blockquote>out( Hello World! )</blockquote>And we should be goo- oh no! Everything's red! Why are there so many problems?!
[[File:First script 7.png|thumb|563x563px|If you don't see the above problems or red text, you probably didn't do the setup correctly in the previous lesson. Do it!]]
=== Debugging ===
This will be something we'll see maybe a million times as we go forward, and it's important to not get too stressed out about it early on.
Because of our work in [[Lua/Setup]], we now have access to a right proper debugging system that will yell at us when we do something wrong.
There's a lot of confusing bits here, and honestly, none of the problems listed are going to be specific enough to tell us what to do to fix it - at least in this situation. Some of them are telling us that we're arguing too much, one is talking about undefined globals, and one is just absolute pig latin saying it expected <exp>. None of those mean anything to us, but let's check out something that does - some documentation!
If you hover over the word "out", you'll see a popup that gives us some information about the function `out`. The most important bit is the following:
[[File:First script 8.png|none|thumb]]
This is our '''function definition'''. It tells us what the function is called, and what it expects within the parentheses. It's asking for something it calls "output", and output is a string. '''String''' is the first of several Lua "types" we'll go over in this tutorial series. A '''string''' is text - and it's called such because it's a string of ''characters'' that make up a long piece of text. The issue we've had here is that, while `Hello World!` is text, Lua needs to be told that it's a string type. To do that, we just need to wrap quotation marks - either single or double, doesn't matter, but it can't be backticks or "pretty" quotations like what mobile phones default to. I learned that the hard way.
The easiest way to do that is to highlight the entirety of Hello World! and type in the quotation mark, just one. VSCode will plop a quotation mark on either part of your selection, and all SEVEN of our problems will vanish through air.
[[File:First script 9.png|thumb|The white dot on our tab means this is an unsaved file; use Ctrl+S to save it!]]
== Getting It In-Game ==
We have now an incredibly basic script that I'd like to test out.
Now, the `out` function is defined by Creative Assembly within their games, but it comes '''disabled by default''' for retail. That means that while your code runs, it won't actually write anything to a log file. That way they can have their debugging tools, and we can opt into it, but it doesn't automatically happen for every single user.
This disable-by-default can be overridden in different ways, depending on the game. As of the time of writing this guide, my favorite methods are:
* Using the [https://steamcommunity.com/sharedfiles/filedetails/?id=2927955021 Mod Configuration Tool]'s  setting for enabling game logging, if available for that game.
* Using the [https://steamcommunity.com/sharedfiles/filedetails/?id=2789857593&searchtext=script+debug+activator Script Debug Activator] mod, if available for that game.
* Creating a file in your mod at `script/enable_console_logging`, with no extension. You can use VSCode and use add text file at that destination, add any text inside the file and save it.
These may change or evolve as time goes on.
=== Loading ===
Once you've got your script ready, and your game is set up to have script logs printed out, we'll want to load up our .pack file and get it set in game. For the sake of my soul and time, I'm going to assume we know how packs work, creating them and loading them, and the like.
Step one, we've got to save our stuff in VSCode. Use File -> Save All.<blockquote>Worth a shout here - I altered the shortcut in Visual Studio Code for Save All, since it's maybe my most-used shortcut combination.
If you go to Manage -> Keyboard Shortcuts and type in "save all" in the search bar, you can click on that shortcut and type in on your keyboard what you'd prefer. I use Ctrl+Alt+S, but set it to whatever makes you happy.</blockquote>Then, in RPFM, use MyMod -> Import. Again, I added a keyboard shortcut here through RPFM - I set Import/Export to Ctrl+Alt+I/E. You can get there through PackFile -> Preferences -> Shortcuts.
Import '''pulls in all the stuff from your MyMod folder into your pack''', while '''export does the opposite'''. I use these all the time, since I externally edit Lua files but internally edit database tables. You'll want to keep in mind where you're working, because if you edit a Lua file externally, and then a table file internally, without adding the changes from one to the other, you may end up overwriting some of your work. Because of that, I will only ever work in one sphere, then use Import/Export to make sure the folder and my .pack line up, and then edit in the other sphere.
Save in RPFM, and use PackFile -> Install to move your .pack to the game folder. Tick it in your mod manager of choice, and load up a fresh campaign!
=== Log File ===
The game-created log files will spit out in your game's folder - ie., steam/steamapps/common/Total War WARHAMMER III/. They are created as "script_log_DDMMYY_HHMM.txt". So if I made one right now, it'd be "script_log_100623_1320.txt", if you were wondering when I wrote this exact line.
The game creates a new log file every time we enter the main menu, every time we enter a campaign, and every time we enter a battle - so if you only just started using script logs, there should be two or three in there depending on if you quit to main menu or to desktop after loading up your new campaign. Navigate to the latest one and open it up.
Worth noting here, I have a file association set up to automatically open .txt files in Visual Studio Code, because I find it easier and quicker to navigate. If you right click the script log -> open with -> target Visual Studio Code, you can get it in that way. You can also simply grab the file from your file explorer and drag it into the main VSCode window, and it will be opened up within.
There's going to be... a ''lot'' of stuff in here. A lot a lot. CA uses double quindrillion calls to `out()` throughout the game. We'll actually find that it's pretty cumbersome to navigate, and later on we'll create our '''own custom log files!''' But for now, let's navigate this chonker.
=== Find Your Text ===
<small>''Sounds kinda like a platitude you'd find on a pillow in Kohl's.''</small>
In our opened new script log, we should search for the words "Hello World!". We can either do that using the Search tab we have set up, or clicking into the script log in the main window and using Ctrl+F.
[[File:First script 10.png|none|thumb|911x911px|Our first trigger of stuff in the game!Hey, did you put a naughty word instead of "Hello World!"? You lil nasty gremlin.]]
WE FUCKING DID IT HELL YES.
And that's it, you're ready to script on your own!
Bye!

Revision as of 12:38, 10 June 2023

In this first step tutorial, we're going to learn:

  • How to load & run a custom Lua script
  • How timing works, at a large level, with scripts
  • How to print out game information to interrogate the state of the game
  • How simple variables work

Let's create our first script together!

Setup

First thing's first, let's crack open RPFM to create our new Mod. In RPFM, I'm going to use MyMod -> New MyMod, to create a new easy-to-use mod system. If you haven't already, follow the setup guides in RPFM for setting up preferences and paths.

First Script 01.png

RPFM will then open up our new MyMod. Within it, use MyMod -> Export, which will unload the contents of the newly created (almost entirely empty) MyMod into your specified MyMods folder. I have this folder pinned to my quick access, but you can also get there with MyMod -> Open MyMod Folder, and navigating down to the newly created MyMod.

Now we can right click this folder and use the newly added "Open with Code" button, which will take the targeted mod folder and open it within Visual Studio Code!

Visual Studio Code Navigation

When we open up in Visual Studio Code, we'll see something that looks a little bit like this - it might differ slightly, that's totally okay!

It might seem like quite a lot at first, but we'll get used to it! Just make sure to set the settings to dark mode if you're not insane.


Before we get too far, let's break down some of the parts of what we're looking at here, and set up our windows so we have everything we need.

Primary Bar

On the very left, we have what VSCode calls the "Primary Bar". It has a few things here - the top has the two files stacked on each other, a search button below that, three buttons we don't have to worry too much about yet, and a Bookmark button. The bottom has our Account and Manage button. If you haven't already, click into the Manage button and set our Profile to the correct one. I have mine saved as "TW Lua".

The part of the primary bar we care most about is the top button, the "Explorer". Think of this as your file navigation - it's effectively the same as navigating the Windows File Explorer, but it has some extra goodies that will be nice for us.

First script 3.png

At the top of the Explorer, we have "Open Editors". This will show us all of our actively opened files - we'll see this shortly!

Below that, we have a tab for the currently opened folder. My folder is named babys_first_script, so that's what shows as the name here. Inside are all of the folders AND files that exist within my folder on my PC. We can open, create, rename, and delete any file within to our heart's content, and edit as long as Visual Studio Code can edit it. VSCode can edit essentially any text file, which is what all code files are, effectively. Based on the type of text file, VSCode will handle it differently - it knows the difference between a .lua and a .json file, and will react accordingly to the specifications of either language.

Below that, we had "TODOs: Tree", which is an extension preloaded with our Profile. This is one of my favorite tabs, because I can leave notes for myself to do stuff later and they will be displayed in this window.

Below that are Outline and Timeline, which I rarely use but can sometimes be nice. Outline will show us the layout of our code, and Timeline will show us previous local history for saving/editing these files, and more details if we're using GitHub or another version control software.

The Search button is also incredibly useful, for reasons that may be obvious.

Menu Bar

On the very top, we have our conveniently named Top Bar, or Menu Bar. You've seen these a billion times before. There's a LOT of stuff up here, but in my five years of using this program for TW Lua I've only really cared about a couple buttons within.

"File" is going to be our best friend, for opening new windows, saving, and creating new files. Edit/Selection/View have a load of commands in them, and they may be good to reference often early on to see shortcuts for saving and other helpful code shortcuts, like duplicating text or renaming stuff.

For now, what we should do is click View -> Problems, which will open up a new bottom bar for us. We only care about the "Problems" tab that shows up.

Customizabilitization

Let's introduce the idea here that VSCode is super duper flexible with its layout. Anything on your screen right now can be put somewhere else, including individual tabs, buttons, anything. We can move Problems as a button to the Primary Bar, the Explorer as a tab on the bottom, and TODOs on the right all alone.

First script 4.png

It's a bit clunky and not exactly what I need, but it's as good time as any to show you how flexible it can get.

For now, I'm going to plop Search and TODOs on my new right sidebar stacked on one another, I'm going to right click one of the tabs at the bottom and deselect "Terminal", "Output", and "Debug Console", and keep my left Primary Bar as is otherwise and open back up the Explorer.

Script Creation

Okay, enough preamble. Let's get it started.

The Plan

Our standard first step for creating a new script is the first step we should do whenever making a new mod or system: having a plan, something we want. For this one, our goal is going to be simple. I want the following:

  • When the game starts, I want to output a message to a text file that tells me the game started, and tells me where my starting faction leader and capital region are.
  • On every turn after that, I want to give my faction some extra gold.
  • If I'm playing as Karl Franz, I want to give my faction another two armies.
  • I swear it's balanced.

Make It

Second button from the left!

In VSC, go to the Explorer, and hover over the currently-opened-folder tab. You can either right click within the white space and press "New Folder", or press the New Folder button on the tab for the currently opened folder.

Name the empty folder that's created `script`. All scripts we write will be in this main folder. With this folder selected, create a new folder again, and call this one "campaign". Do it again, calling the third "mod", making our full path script/campaign/mod.

NOTE: This folder path is one of several that is specified by Creative Assembly in more recent games, for modders to add scripts to which get automatically loaded. This behavior exists in Warhammer II, Warhammer III, Three Kingdoms, and Troy. It may continue for future games, but any game before Warhammer II needs a different way to load scripts. For more information, check out Lua/Loading Scripts.

Within script/campaign/mod, now either right click the folder and press "New Text File", or use the first button on the tab to create a new file. I'm going to name mine "totally_not_cheats.lua". The .lua extension is required, don't skip it!

Start It

With our newly created file, our VSC layout will change ever so slightly. In "Open Editors", we'll see our fresh new baby script created; and in the central window, we'll see a new tab that's named the same as our new file, and an empty text file in the center with just the number "1" in it.

Notice the lil' moon symbol next to our file? That's because Lua is Portuguese for moon!

We'll kick off our script with the first piece of the puzzle, outputting some text to a file so we can read and interface with our scripts.

Let's get it going with a function called `out()`. This is a CA-defined function that they use all over their scripts, to print out to a text file various bits of info about the game, for debugging and logging purposes. It's a really handy system that'll let you know for certain what's actually going on in your scripts at runtime, since we can't truly test our script outside of the game.

To use the `out` function, we just have to write that word, put an open parenthesis, put in what we want to output, and then write a closing parenthesis. Let's try the old standard, and toss in the following:

out( Hello World! )

And we should be goo- oh no! Everything's red! Why are there so many problems?!

If you don't see the above problems or red text, you probably didn't do the setup correctly in the previous lesson. Do it!

Debugging

This will be something we'll see maybe a million times as we go forward, and it's important to not get too stressed out about it early on.

Because of our work in Lua/Setup, we now have access to a right proper debugging system that will yell at us when we do something wrong.

There's a lot of confusing bits here, and honestly, none of the problems listed are going to be specific enough to tell us what to do to fix it - at least in this situation. Some of them are telling us that we're arguing too much, one is talking about undefined globals, and one is just absolute pig latin saying it expected <exp>. None of those mean anything to us, but let's check out something that does - some documentation!

If you hover over the word "out", you'll see a popup that gives us some information about the function `out`. The most important bit is the following:

First script 8.png

This is our function definition. It tells us what the function is called, and what it expects within the parentheses. It's asking for something it calls "output", and output is a string. String is the first of several Lua "types" we'll go over in this tutorial series. A string is text - and it's called such because it's a string of characters that make up a long piece of text. The issue we've had here is that, while `Hello World!` is text, Lua needs to be told that it's a string type. To do that, we just need to wrap quotation marks - either single or double, doesn't matter, but it can't be backticks or "pretty" quotations like what mobile phones default to. I learned that the hard way.

The easiest way to do that is to highlight the entirety of Hello World! and type in the quotation mark, just one. VSCode will plop a quotation mark on either part of your selection, and all SEVEN of our problems will vanish through air.

The white dot on our tab means this is an unsaved file; use Ctrl+S to save it!

Getting It In-Game

We have now an incredibly basic script that I'd like to test out.

Now, the `out` function is defined by Creative Assembly within their games, but it comes disabled by default for retail. That means that while your code runs, it won't actually write anything to a log file. That way they can have their debugging tools, and we can opt into it, but it doesn't automatically happen for every single user.

This disable-by-default can be overridden in different ways, depending on the game. As of the time of writing this guide, my favorite methods are:

  • Using the Mod Configuration Tool's setting for enabling game logging, if available for that game.
  • Using the Script Debug Activator mod, if available for that game.
  • Creating a file in your mod at `script/enable_console_logging`, with no extension. You can use VSCode and use add text file at that destination, add any text inside the file and save it.

These may change or evolve as time goes on.

Loading

Once you've got your script ready, and your game is set up to have script logs printed out, we'll want to load up our .pack file and get it set in game. For the sake of my soul and time, I'm going to assume we know how packs work, creating them and loading them, and the like.

Step one, we've got to save our stuff in VSCode. Use File -> Save All.

Worth a shout here - I altered the shortcut in Visual Studio Code for Save All, since it's maybe my most-used shortcut combination. If you go to Manage -> Keyboard Shortcuts and type in "save all" in the search bar, you can click on that shortcut and type in on your keyboard what you'd prefer. I use Ctrl+Alt+S, but set it to whatever makes you happy.

Then, in RPFM, use MyMod -> Import. Again, I added a keyboard shortcut here through RPFM - I set Import/Export to Ctrl+Alt+I/E. You can get there through PackFile -> Preferences -> Shortcuts.

Import pulls in all the stuff from your MyMod folder into your pack, while export does the opposite. I use these all the time, since I externally edit Lua files but internally edit database tables. You'll want to keep in mind where you're working, because if you edit a Lua file externally, and then a table file internally, without adding the changes from one to the other, you may end up overwriting some of your work. Because of that, I will only ever work in one sphere, then use Import/Export to make sure the folder and my .pack line up, and then edit in the other sphere.

Save in RPFM, and use PackFile -> Install to move your .pack to the game folder. Tick it in your mod manager of choice, and load up a fresh campaign!

Log File

The game-created log files will spit out in your game's folder - ie., steam/steamapps/common/Total War WARHAMMER III/. They are created as "script_log_DDMMYY_HHMM.txt". So if I made one right now, it'd be "script_log_100623_1320.txt", if you were wondering when I wrote this exact line.

The game creates a new log file every time we enter the main menu, every time we enter a campaign, and every time we enter a battle - so if you only just started using script logs, there should be two or three in there depending on if you quit to main menu or to desktop after loading up your new campaign. Navigate to the latest one and open it up.

Worth noting here, I have a file association set up to automatically open .txt files in Visual Studio Code, because I find it easier and quicker to navigate. If you right click the script log -> open with -> target Visual Studio Code, you can get it in that way. You can also simply grab the file from your file explorer and drag it into the main VSCode window, and it will be opened up within.

There's going to be... a lot of stuff in here. A lot a lot. CA uses double quindrillion calls to `out()` throughout the game. We'll actually find that it's pretty cumbersome to navigate, and later on we'll create our own custom log files! But for now, let's navigate this chonker.

Find Your Text

Sounds kinda like a platitude you'd find on a pillow in Kohl's.

In our opened new script log, we should search for the words "Hello World!". We can either do that using the Search tab we have set up, or clicking into the script log in the main window and using Ctrl+F.

Our first trigger of stuff in the game!Hey, did you put a naughty word instead of "Hello World!"? You lil nasty gremlin.

WE FUCKING DID IT HELL YES.

And that's it, you're ready to script on your own!

Bye!