|
| 1 | +--- |
| 2 | +title: Make a Baldur's Gate 3 Mod With Lua |
| 3 | +author: Julien Kris |
| 4 | +uid: |
| 5 | +datePublished: |
| 6 | +description: Learn how to use Lua to build your own mod for Baldur's Gate 3. |
| 7 | +header: |
| 8 | +published: false |
| 9 | +tags: |
| 10 | + - lua |
| 11 | + - games |
| 12 | + - windows |
| 13 | + - mods |
| 14 | +--- |
| 15 | + |
| 16 | +<BannerImage |
| 17 | + link="" |
| 18 | + description="Title Image" |
| 19 | + uid={true} |
| 20 | + cl="for-sidebar" |
| 21 | +/> |
| 22 | + |
| 23 | +# Make a Baldur's Gate 3 Mod With Lua |
| 24 | + |
| 25 | +<AuthorAvatar |
| 26 | + author_name="Julien Kris" |
| 27 | + author_avatar="" |
| 28 | + username="julien" |
| 29 | + uid={true} |
| 30 | +/> |
| 31 | + |
| 32 | +<BannerImage |
| 33 | + link="" |
| 34 | + description="Title Image" |
| 35 | + uid={true} |
| 36 | +/> |
| 37 | + |
| 38 | +{/* <RoundedImage link="https://i.imgur.com/jkgf7of.gif" description="" /> */} |
| 39 | + |
| 40 | +**Prerequisites:** Lua basics, Baldur's Gate 3 |
| 41 | +**Read Time:** 45 minutes |
| 42 | + |
| 43 | +# Introduction: Frogify Your Enemies |
| 44 | + |
| 45 | +Anyone who's played Baldur's Gate 3 knows it features some truly epic battles. |
| 46 | + |
| 47 | +<RoundedImage link="https://i.imgur.com/kop1His.png" description="Gale casting a fire spell in Baldur's Gate 3" /> |
| 48 | + |
| 49 | +[Baldur’s Gate 3](https://en.wikipedia.org/wiki/Baldur%27s_Gate_3) (BG3) is a 2023 RPG by Larian Studios, adapted from the fifth edition of Dungeons & Dragons. It's earned over 10 million players due to its expansive worldbuilding, fleshed out characters, and stunning visuals. |
| 50 | + |
| 51 | +But have you ever wanted to make Baldur’s Gate 3 *a little sillier*? 🐸 What if, every time combat started, your terrifying enemies suddenly transformed… into frogs? |
| 52 | + |
| 53 | +<RoundedImage link="https://i.imgur.com/hsBhlGp.png" description="Baldur's Gate 3 frog" /> |
| 54 | + |
| 55 | +We're going to do just that by making our own mods! |
| 56 | + |
| 57 | +**Mods** (short for modifications) are a way of writing code to alter features, graphics, or gameplay of an existing game, even for huge titles like Baldur's Gate 3. |
| 58 | + |
| 59 | +Since BG3 Lua modding is only supported on Windows, this tutorial is aimed at Windows users. If you're a Mac user, scroll down to our Resources section for some links! |
| 60 | + |
| 61 | +<RoundedImage link="swap image before publishing" description="Image of enemies turning into frogs in combat" /> |
| 62 | + |
| 63 | +By the end of this tutorial, you’ll know how to: |
| 64 | +- Write a BG3 mod with Lua |
| 65 | +- Apply polymorph statuses during combat |
| 66 | +- Test your mod in-game |
| 67 | + |
| 68 | +## Getting Started |
| 69 | + |
| 70 | +Make sure you have Baldur's Gate 3 installed on your machine! If not, start [installing](https://store.steampowered.com/app/1086940/Baldurs_Gate_3) it in the background while you create your mod. |
| 71 | + |
| 72 | +If you don't already have a code editor installed, download [Visual Studio Code](https://code.visualstudio.com). Otherwise, you can use another code editor of choice. |
| 73 | + |
| 74 | +### BG3 Script Extender |
| 75 | + |
| 76 | +**BG3 Script Extender (BG3SE)** is a program that adds Lua scripting and console support to Baldur's Gate 3. Download it from [GitHub](https://github.com/Norbyte/bg3se). |
| 77 | + |
| 78 | +Right click on the downloaded .zip file, select [Extract All], and extract the files to a new folder called in your Documents directory, and name it somehting like `ModdingTools`. Next to your `ModdingTools` folder, create a new folder called `Mods`. We'll be keeping our modding apps in the `ModdingTools` folder, and our working files in the `Mods` folder. |
| 79 | + |
| 80 | +Here's the structure you'll be working with inside the Mods folder (you'll learn how to create the meta.lsx files and both .lua files in a bit!) |
| 81 | + |
| 82 | +``` |
| 83 | +Mods/FrogifyEnemies/Mods/FrogifyEnemies |
| 84 | +├── meta.lsx |
| 85 | +└── Public/ |
| 86 | + └── FrogifyEnemies/ |
| 87 | + └── Scripts/ |
| 88 | + ├── Init.lua |
| 89 | + └── Frogify.lua |
| 90 | +``` |
| 91 | + |
| 92 | + |
| 93 | +## Set up the .lsx file |
| 94 | +An **.lsx** (XML) file tells Baldur’s Gate 3 about your mod, including its name, author, folder location, version, and unique ID, so the game can recognize and load it correctly. |
| 95 | + |
| 96 | +Create a new plain text file in VS Code (or your favorite code editor) and save it as **meta.lsx** inside your **FrogifyEnemies** folder. |
| 97 | + |
| 98 | +A **UUID (Universally Unique Identifier)** is a 128-bit value used to uniquely identify information across systems or databases without significant risk of duplication. It looks something like: `87654321-DCBA-4321-DCBA-0987654321AB`. |
| 99 | + |
| 100 | +It'll make sure your mod is unique and doesn't interfere with another mod installed on the same system. Generate your own by using this [online UUID generator](https://www.uuidgenerator.net/version4). |
| 101 | + |
| 102 | +```xml |
| 103 | +<save> |
| 104 | + <version>1</version> |
| 105 | + <region id="ModuleSettings"> |
| 106 | + <node id="root"> |
| 107 | + <children> |
| 108 | + <node id="ModuleShortDesc"> |
| 109 | + <attribute id="UUID" value="87654321-DCBA-4321-DCBA-0987654321AB"/> |
| 110 | + <attribute id="Folder" value="FrogifyEnemies"/> |
| 111 | + <attribute id="Name" value="Frogify Enemies"/> |
| 112 | + <attribute id="Author" value="YourName"/> |
| 113 | + <attribute id="Description" value="Turns all enemies into frogs when combat starts."/> |
| 114 | + <attribute id="Version64" value="1"/> |
| 115 | + </node> |
| 116 | + </children> |
| 117 | + </node> |
| 118 | + </region> |
| 119 | +</save> |
| 120 | +``` |
| 121 | +The **meta.lsx** file tells the game info about your mod, including what the mod is called, who made it, where its files live, and how to tell it apart from other mods. |
| 122 | + |
| 123 | +It contains the following components: |
| 124 | + |
| 125 | +- `UUID`: A unique ID number for your mod. |
| 126 | +- `Folder`: The name of the folder that holds your mod’s files (inside the **Mods/** directory). |
| 127 | +- `Name`: The display name of the mod. This is what shows up in BG3’s Mod Manager or in-game. |
| 128 | +- `Author`: The creator’s name (you!). |
| 129 | +- `Description`: A short blurb about what your mod does. |
| 130 | +- `Version`: A number you can bump up when you release updates so the game (and users) know it’s a new version. |
| 131 | + |
| 132 | +Without this file, the game doesn’t know your mod exists. |
| 133 | + |
| 134 | +## Write your Mod |
| 135 | + |
| 136 | +The core logic: transform enemies into frogs when combat starts. |
| 137 | + |
| 138 | +As you saw in your file folder structure, you'll create two Lua scripts: |
| 139 | + |
| 140 | +The first script is **Init.lua**: |
| 141 | + |
| 142 | +```lua |
| 143 | +-- Require your main logic file |
| 144 | +Ext.Require("Frogify.lua") |
| 145 | + |
| 146 | +-- Print when Init itself loads |
| 147 | +print("Init.lua loaded") |
| 148 | + |
| 149 | +-- Hook into session start |
| 150 | +Ext.Events.SessionLoaded:Subscribe(function () |
| 151 | + print("Frogify Enemies mod loaded!") |
| 152 | +end) |
| 153 | + |
| 154 | + |
| 155 | +``` |
| 156 | + |
| 157 | +Here, we are loading a save file, and once it's finished loading, we tell the game to run the code inside (in this case, a print message that says the mod has loaded!). |
| 158 | + |
| 159 | +The second script is **Frogify.lua**. |
| 160 | + |
| 161 | +```lua |
| 162 | +-- Subscribe to session loaded to confirm mod is active |
| 163 | +Ext.Events.SessionLoaded:Subscribe(function() |
| 164 | + Print("Frogify Enemies mod loaded!") |
| 165 | + |
| 166 | + -- Listen for combat starting |
| 167 | + Ext.Osiris.RegisterListener("EnteredCombat", 2, "after", function(combatGuid, characterGuid) |
| 168 | + -- Only affect enemies |
| 169 | + if Osi.IsEnemy(characterGuid) == 1 then |
| 170 | + -- Transform enemy into a frog |
| 171 | + Osi.ApplyStatus(characterGuid, "POLYMORPH_FROG", -1, 1) |
| 172 | + Print("Frogified enemy: " .. characterGuid) |
| 173 | + end |
| 174 | + end) |
| 175 | +end) |
| 176 | +``` |
| 177 | + |
| 178 | +Here, we are using `Ext.Osiris.RegisterListener("EnteredCombat", ...)` to listen for the event “a character has entered combat.” |
| 179 | + |
| 180 | +`characterGuid` is the game’s internal ID for that specific character. Similar to UUID from before, **GUID** stands for Globally Unique Identifier. But unlike the UUID, this isn't a randomly generated string of numbers, but a predetermined identifier that the game automatically feeds into the function when the `EnteredCombat` event fires. |
| 181 | + |
| 182 | +`if Osi.IsEnemy(characterGuid) == 1 then` checks: “Is this character an enemy?” (1 = yes, 0 = no). |
| 183 | + |
| 184 | +`Osi.ApplyStatus(..., "POLYMORPH_FROG", -1, 1)` applies the “frog polymorph” effect. -1 means it lasts forever until combat ends. `POLYMORPH_FROG` is a built-in string ID that the game understands. |
| 185 | +Finally, `print("Frogified enemy: " .. characterGuid)` writes a debug message in the console so you can confirm the frogification was successful. |
| 186 | + |
| 187 | +<RoundedImage link="swap image before publishing" description="Combat begins, enemies turn into frogs" /> |
| 188 | + |
| 189 | +## Export your Mod to a .pak |
| 190 | + |
| 191 | +Before your mod shows up in Baldur’s Gate 3, you’ll usually need to package it into a **.pak** file. This isn’t done in BG3 itself, you’ll use external tools made by the modding community. |
| 192 | + |
| 193 | +A **.pak** file is a type of “package” file, primarily used in video games, that bundles multiple game data files like graphics, textures, sounds, and other assets into a single file for easier management and distribution. |
| 194 | + |
| 195 | +### BG3 Modders Multitool |
| 196 | + |
| 197 | +Download [BG3 Modder’s Multitool](https://github.com/ShinyHobo/BG3-Modders-Multitool), which is a beginner-friendly open source tool that lets you unpack BG3’s files, browse models, and export your own mod into **.pak** format. |
| 198 | + |
| 199 | +Extract the contents of the .zip folder into the `ModdingTools` folder so you can run the tool separately from your mod files. |
| 200 | +- Open BG3 Modder’s Multitool (installed outside the game). |
| 201 | +- Select your project folder (`Mods`). |
| 202 | +- Click Build Mod, which generates a .pak file. |
| 203 | + |
| 204 | +The **.pak** should be saved here: |
| 205 | + |
| 206 | +``` |
| 207 | +C:\Users\<YourName>\AppData\Local\Larian Studios\Baldur's Gate 3\Mods\FrogifyEnemies\ |
| 208 | +``` |
| 209 | + |
| 210 | +## Load and Test Your Mod |
| 211 | + |
| 212 | +Open BG3 Mod Manager, and drag your **.pak** mod from Inactive Mods → Active Mods. |
| 213 | + |
| 214 | +Click [Save Load Order] and [Export]. |
| 215 | + |
| 216 | +Time to launch Baldur’s Gate 3 via Steam!! Start a new game or load an existing save. Then, enter combat! As soon as the battle begins, enemies should polymorph into frogs before your very eyes. |
| 217 | + |
| 218 | +To debug, open the Script Extender console (<kbd>~</kbd> key) to check for errors. |
| 219 | +Now you can go right into launching Baldur's Gate 3! Start a new game or load a save. |
| 220 | +Find an enemy whose model you swapped. It should now appear as a frog! |
| 221 | + |
| 222 | + |
| 223 | +## Bonus Challenge: Random Animal Mode |
| 224 | +Let's randomize between frogs, chickens, or sheep. 🐸🐔🐑 |
| 225 | + |
| 226 | +Open **Frogify.lua**, which contains the combat logic for polymorphing enemies, and adds randomization. |
| 227 | + |
| 228 | +```lua |
| 229 | +local animals = {"POLYMORPH_FROG", "POLYMORPH_CHICKEN", "POLYMORPH_SHEEP"} |
| 230 | +local choice = animals[math.random(#animals)] |
| 231 | + |
| 232 | +Osi.ApplyStatus(characterGuid, choice, -1, 1) |
| 233 | +print("Transformed enemy into: " .. choice) |
| 234 | +``` |
| 235 | + |
| 236 | +`local animals = {...}` creates a list of different polymorph status IDs. `POLYMORPH_FROG`, `POLYMORPH_CHICKEN`, `POLYMORPH_SHEEP` are all predefined effects in the game. |
| 237 | + |
| 238 | +`local choice = animals[math.random(#animals)]` provides the number of items in the `animals` list (3 in this case). Then, it picks a random number between 1 and 3 (since Lua uses 1-based index) and selects the animal polymorph at that position in the `animals`list. |
| 239 | + |
| 240 | +`Osi.ApplyStatus(characterGuid, choice, -1, 1)` applies the randomly selected polymorph effect to the enemy, to be applied indefinitely (`-1`) and immediately (`1`). |
| 241 | + |
| 242 | +Finally, it prints a debug message showing which animal the enemy transformed into. |
| 243 | + |
| 244 | +There are even more animals you can polymorph your enemies into! Search the word "POLYMORPH" in [Norbyte's BG3 Search Engine](https://bg3.norbyte.dev/search) to explore more options. |
| 245 | + |
| 246 | +# Conclusion |
| 247 | + |
| 248 | +Congrats! You just made your first Baldur’s Gate 3 mod! 🎉 |
| 249 | + |
| 250 | +<RoundedImage link="https://i.imgur.com/PafW349.png" description="Baldur's Gate 3 party victoriously looking out at a cliff" /> |
| 251 | + |
| 252 | +We started by frogifying enemies, and extended it into a randomizer. For an even bigger challenge, you can explore more polymorphs ([cheese wheel](https://www.reddit.com/r/BG3/comments/1ceqelg/i_got_turned_into_a_cheese_wheel/), anyone?), or get wild with custom models. |
| 253 | + |
| 254 | +Have fun breaking Faerûn! |
| 255 | + |
| 256 | +# Additional Resources |
| 257 | + |
| 258 | +Here are some more resources to explore: |
| 259 | + |
| 260 | +- [accessibly named link to full source code on github](codedex/projects/etc) |
| 261 | +- [BG3 Modding Wiki](https://bg3.wiki/wiki/Modding:Modding_resources) |
| 262 | +- [Norbyte's BG3 Search Engine](https://bg3.norbyte.dev/search) |
| 263 | +- [Manual Modding in BG3 for MacOS Users](https://www.youtube.com/watch?v=8BNi0uNOvrE) |
| 264 | + |
| 265 | + |
| 266 | +Share your projects with the team [@codedex_io](https://www.twitter.com/codedex_io)! Let us know what you come up with! |
| 267 | +<span style={{ fontSize: "16px", lineHeight: 1 }}> |
| 268 | + <img |
| 269 | + src="https://i.imgur.com/xbx0UAG.png" |
| 270 | + alt="emoji" |
| 271 | + style={{ height: "1em", width: "auto", verticalAlign: "middle" }} |
| 272 | + /> |
| 273 | +</span> |
0 commit comments