Section 1: Introduction, Variables, Functions, Event-Based Programming
Introduction
First, open up Visual Studio Code. If you don't have it, download and install it. This is the preferred IDE for MWSE modding.
Once you have VSCode open, click File -> Open Folder..., then navigate to Morrowind\Data Files\MWSE
, and select the folder. You will be asked, "Do you trust the authors of the files in this folder?" Click "Yes, I trust the authors".
If you don't already have the Lua and vscode-lua-format extensions installed, you'll see popups at the bottom left corner of the screen asking you to install them. Install both of them.
Next, we'll save this as workspace. Click File -> Save Workspace As... Save.
Now, we are going to create the folder for our MWSE mod. On the left side of the screen, we have the folders within the MWSE
folder. Mods usually go under the mods
folder. Expand that folder. So long as your mod has a main.lua
, you can put it anywhere within the mods
folder. However, we will use the mods/modderName/modName
naming convention. This avoids conflicting files and keeps everything tidy.
For example, in this mod, the path of main.lua will be MWSE/mods/Amalie/craftableBandage/main.lua
.
You can name your folders any way you want, we used camelCase, that is, no separator between words, first letter lowercased, and the first letter of all other words capitalized. But you can use snake_case craftable_bandage
or Normal Craftable Bandage
. Just remember, don't put a dot in your folder name. Do not do this craftable.bandage
, for example.
Comments
Before we do any coding, let's learn about how to comment in Lua first. Oftentimes, you want to add information about the mod and its author at the top of the script.
When Lua code is run, comments will be skipped. Any line starting with a double dash --
is considered a comment. This is a single line of comment.
-- Mod: Craftable Bandage
You can also do multi-line comment with --[[]]
. That is, double dash followed by two opening brackets with two closing brackets following the comment. Let's add some information about our mod.
--[[
Mod: Craftable Bandage
Author: Amalie
This mod allows you to craft OAAB bandages with novice bushcrafting skill.
It serves as an alternative to alchemy and restoration.
]]
Variables
Now let's learn how to create a variable in Lua. A variable holds some type of data which can be manipulated and referred to. This mod is Craftable Bandage, so we will create a local variable called bandageId
and set it to the id of one of the OAAB bandages.
local bandageId = "AB_alc_HealBandage02"
In Lua, variables are global by default but you probably don't want that most of the time. The local
here specifies that this is a local variable, as opposed to a global variable.
bandageId
is the variable name.
The equal symbol =
is the assignment operator and we're assigning a string to bandageId
. A string is a collection of characters like AB_alc_HealBandage02
and it is enclosed in double or single quotation marks.
One thing you'll notice is that you don't need to put a semicolon ;
at the end of each line in Lua.
Functions
Let's move on to functions. A function is a block of code which only runs when it is called. Let's define an empty function called registerBushcraftingRecipe
.
local function registerBushcraftingRecipe()
end
If you are following along the tutorial, you can see in the IDE, both the function and the variable we just created are greyed out. Hovering over it, it tells us that they are unused function and unused local. That's because we haven't called them or used them anywhere in the script yet. Let's do that now.
To call a function, type the name of the function followed by a pair of parentheses.
registerBushcraftingRecipe()
Right now, when the function is called, it's not doing anything. So let's use this function to print some information to MWSE.log.
mwse.log("[Craftable Bandage] Registering bushcrafting recipe...")
This is one of the most common ways to write information to MWSE.log and we will look at another way (a better way) to do logging later. For now, let's run Morrowind.exe and check the log. You should be able to see [Craftable Bandage] registering bushcrafting recipe...
in the log.
MWSE.log can hold lots of useful information for modders if they know how to read and write it. The line starts with [Craftable Bandage]
is the line our script wrote. Lines starts with [Crafting Framework]
and [Ashfall]
are the lines Crafting Framework (CF) and Ashfall wrote. Our line printed before CF implies that our function was run before CF and Ashfall started running.
But we want to register the bandage recipe when CF is registering bushcrafting recipes. How do we do that? Well, let's learn about event-driven programming.
Event-Driven Programming
In event-driven programming, or event-based programming, events are "fired" when an action takes place. Your code listens for them and handles them accordingly. You can look up all the events MWSE provides here.
Let's look at the initialized
event. This fires when game code has finished initializing, and all masters and plugins have been loaded. Let's copy the example code provided in the document page and paste it in our main.lua.
--- @param e initializedEventData
local function initializedCallback(e)
end
event.register(tes3.event.initialized, initializedCallback)
Here, tes3.event.initialized
is the event name and initializedCallback
is the function to call when initialized
event is fired. Same as before let's write some information to print out to MWSE.log.
mwse.log("[Craftable Bandage] Initialized")
Usually, you want to register your mod's events inside initializedCallback
. And in our case, we need to register the "Ashfall:ActivateBushcrafting:Registered"
event and registerBushcraftingRecipe()
will be the event callback function.
event.register("Ashfall:ActivateBushcrafting:Registered", registerBushcraftingRecipe)
What this means is when Ashfall is registering their bushcrafting recipes, our registerBushcraftingRecipe()
function will run. So let's test this. Launch the game and read the MWSE.log.
If we search the log for "Bandage", you'll could only find the "Initialized"
log but not the "registering recipe"
log. That's because, the print order of the logs, you know that our mod was initialized after Crafting Framework finished registering all the MenuActivator. That's why our event callback never runs.
We can fix this by tweaking the priority
when registering our initialized
event.
event.register(tes3.event.initialized, initializedCallback, { priority = 100 })
Functions registered with higher priority
will run first. The default is 0. Setting the priority
to be anything higher than 0 can make sure our initializedCallback
will run before The Crafting Framework registered all of the recipes.
Launch the game again. Now you should be able to see in your log that our bandage recipe was registered right when The Crafting Framework was registering Ashfall's Bushcrafting MenuActivator.
That's it for today. You've learned how to comment, how to create variables and functions, and the concept of event-driven programming. See you in the next section.
What your main.lua should look like
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Next - Section 2: Libraries, Logging, Crafting Framework Recipes