This guide will explain how to use a simple Osiris script to give every player character a Passive without having to modify class progression tables. There are a few benefits to this technique:
However, if your mod is an overhaul of a class that already exists, then I'd recommend using the intended method (modifying class progression tables) instead of Osiris.
If you haven't used Osiris before, I've tried to make this guide a good starting point! All you need to do is copy some code and change a few names to make them unique to your mod, but if you're interested, I've also included optional sections that explain the basics of what the script does. If you want more detailed and technical explanations, you can reference my other guide Understanding Osiris Rules.
I will also not go into detail about how to create new Passives in this guide. You can find the lists of existing Passives in the Stats Editor, mostly under the Shared and SharedDev folders, in the Stats->Passive files.
Now let's get started!
The first thing we need to do is create a file where we can write our Osiris script (called a goal). You can open the Story Editor with the button above the game preview that looks like a book with an "S" on it (for more detail, see the official Story Editor guide).
On the left side of the Story Editor window, right-click on one of the top-level scripts (such as _GLO_Analytics), and then select "Add New Item...". Do NOT add a new sub item.
This will prompt you to name the script. The name needs to be unique from every other game and mod script, so a good rule of thumb is to include an abbreviation of your username and your mod's name. This means the script name will have three parts:
Let's pick the first part of the name, which should give an idea about what the script will do. We want to give everyone a passive, so GivePassive is a short and descriptive name. Also, game scripts that affect everyone start with GLO_ (short for global), so we end up with GLO_GivePassive.
Next, pick abbreviations for your username and the mod's name. For example, I shorten my username "WorldWalker42" to WW. And, if the name of this mod was something like "Universal Passives", I would shorten that to UP.
By combining all three parts to get GLO_GivePassive_WW_UP, I'm very confident that this script name is unique and will not conflict with any other mod. My unique identifier for this mod, WW_UP, will be used several more times in the script, so for clarity I will replace it with IDENTIFIER from now on.
Every time you see IDENTIFIER, you'll need to replace it with what you've picked for your own mod. For example, the name of your script should be something like GLO_GivePassive_IDENTIFIER.
Now that we have a file for our script, let's actually write it! Double-click your script name in the list on the left side of the window and the file will open on the right side of the window. For now, there are just three empty text fields.
First, we need something to indicate when the script is just starting to run. We can do this in the top text field (the INIT section) by adding the following line:
DB_IDENTIFIER_Initialized(0);
Remember to replace IDENTIFIER with the unique identifier for your mod.
Now in the middle text field (the KB section), we add the following rules that will give players a Passive when the mod starts running or when they join the party:
IFLevelLoaded(_)ANDDB_IDENTIFIER_Initialized(0)THENTimerLaunch("IDENTIFIER_InitDelay", 0);IFTimerFinished("IDENTIFIER_InitDelay")THENNOT DB_IDENTIFIER_Initialized(0);DB_IDENTIFIER_Initialized(1);IFDB_Players(_Player)ANDDB_IDENTIFIER_Initialized(1)ANDHasPassive(_Player, "DraconicAncestry_Red", 0)THENAddPassive(_Player, "DraconicAncestry_Red");
And that's the whole script! You just need to replace every IDENTIFIER and also replace DraconicAncestry_Red with the Passive you want to give. Make sure to leave the quotation marks around the name of the Passive.
The final step is to open the "File" dropdown menu and choose "Generate Definitions, Build and Reload". Once it's done getting the script ready to run in the game, you can go test your mod, and every player character should now have the Passive.
In this section, we'll talk about some of the mechanics of how the script works. If you aren't interested, feel free to skip to the next section!
First, the INIT section defines the fact 0 in the database DB_IDENTIFIER_Initialized. Because the INIT section is only executed once when the script starts running (which will be the first time a game is loaded with our mod) and 0 is often used to mean false, this fact indicates that the script is in an 'uninitialized' state. This doesn't mean anything beyond what we do with it, but as you'll see, it will be essential for us to give the Passive to preexisting characters.
Let's move on to the KB section. The first rule has two conditions:
0 is in the database DB_IDENTIFIER_InitializedBoth of these conditions should always be true as soon as a newly modded save file is loaded, which will execute the rule's action. The action starts a timer named IDENTIFIER_InitDelay for 0 milliseconds, which will finish and trigger the TimerFinished event on the very next frame.
Why delay our script initialization by one frame? Because Osiris executes scripts/goals in alphabetical order, and trying to add a passive to a player on the first frame seems to only work (or not work) depending on the location of your script/goal in the overall program/story. You can intentionally name your script something that will execute at the end and then remove the timer for a small efficiency boost, but for the sake of keeping this guide simple, I've opted to include the timer. By making the script wait until the second frame of our loaded game, we're guaranteed that it will work, no matter what the name of our script is.
Let's keep going. The second rule executes when the timer finishes, and all it does is replace the fact 0 with the fact 1 in the database DB_IDENTIFIER_Initialized. Just like 0 is often used to mean false, 1 is used to mean true. This indicates that our script has just gone from being uninitialized to being initialized, and any rule that has DB_IDENTIFIER_Initialized(1) as a condition will be triggered for evaluation. We can use this to trigger rules that will perform the setup we want.
The third rule does exactly this. It has three conditions:
When the script starts running, the condition DB_IDENTIFIER_Initialized(1) will trigger this rule for evaluation and cause it to execute once for every player who is already in the party. From then on, the condition DB_Players(_Player) will trigger the rule again whenever a new player joins. The result is that everyone gets the Passive!
You might want to give characters a passive only when they reach a certain level. We can do this with Osiris too, although it won't create a notification or appear in the level-up screen.
The setup for the script does not change. If your script already has the INIT section...
DB_IDENTIFIER_Initialized(0);
...and the first two rules of the KB section...
IFLevelLoaded(_)ANDDB_IDENTIFIER_Initialized(0)THENTimerLaunch("IDENTIFIER_InitDelay", 0);IFTimerFinished("IDENTIFIER_InitDelay")THENNOT DB_IDENTIFIER_Initialized(0);DB_IDENTIFIER_Initialized(1);
...then you don't need to add them again. However, if you skipped Step 2 of this guide, you will need to include them now for the rest of the script to work.
Now we can get to the new rules. They're a little more complicated, but the only things you need to do is just replace IDENTIFIER with the one you picked for your mod in Step 1, GiantKiller with the Passive you want, and potentially the comparison on line 20 to control at which level you want to give the passive (currently set to level 3).
IFDB_Players(_Player)ANDDB_IDENTIFIER_Initialized(1)THENPROC_IDENTIFIER_GiveLeveledPassive(_Player);IFLeveledUp(_Player)ANDDB_IDENTIFIER_Initialized(1)THENPROC_IDENTIFIER_GiveLeveledPassive(_Player);PROCPROC_IDENTIFIER_GiveLeveledPassive((GUIDSTRING)_Player)ANDHasPassive(_Player, "GiantKiller", 0)ANDGetLevel(_Player, _Level)AND_Level >= 3THENAddPassive(_Player, "GiantKiller");
That's it! Just remember to choose "Generate Definitions, Build and Reload" from the "File" dropdown before testing your mod.
These rules are structured a little differently because now we need to give the Passive in any of these three events:
Because we need to respond to more events, it's nice to be able to reuse Osiris logic. That's why we make a standalone procedure PROC_IDENTIFIER_GiveLeveledPassive that decides whether a player is eligible for the Passive, and then all we have to do is call the procedure whenever a player might be eligible.
Notice how the first new rule looks very similar to the last rule in Step 2. Whenever a player joins the party, or when the script first starts running, it will call the procedure. If this player is at or above level 3, the procedure will give them the passive. That covers the first two events we need to worry about.
The second new rule covers the last event we need to worry about - a player leveling up. Whenever this happens after the script has been initialized (which should always be the case, but I like to check for it to make sure nothing went wrong), it will also call the procedure.
The procedure itself is very simple. It just makes sure the player doesn't already have the Passive and that they're at least level 3, and then gives them the Passive!
We can do other fun things with similar Osiris rules. Let's say we want to give a certain category of NPCs a Passive, like buffing every guard in the game, or giving enemy Paladins a cool new ability.
We have to do this a little differently because the massive number of NPCs means that we don't have as convenient access to them as we do with player characters. Instead of looking for eligible characters as soon as the game loads, I prefer to wait for them to be involved in some sort of event. An easy event to wait for is the start of combat, because they often won't need the passive before then anyway. Whenever a character joins combat, we can just check if they have the tag we care about, like GUARD or PALADIN.
We don't need any script setup to do this. All we need is a single rule in the KB section of a script:
IFDB_Is_InCombat(_Character, _)ANDNOT DB_Players((CHARACTER)_Character)ANDIsTagged((GUIDSTRING)_Character, (TAG)GUARD_0b52f35e-fb1f-4865-bcd2-5d21ef7343cd, 1)ANDHasPassive(_Character, "DarkOnesBlessing", 0)THENAddPassive(_Character, "DarkOnesBlessing");
All you need to change here is the Passive DarkOnesBlessing to the one you want to give (and remember to leave the quotation marks around the name), and also change the tag to the one you want.
Unfortunately, we can't just use the name of a tag (like GUARD), we need to use the tag's name and GUID (like GUARD_0b52f35e-fb1f-4865-bcd2-5d21ef7343cd). To find this, you can go to the Tag Editor by clicking the button directly to the right of the Story Editor button. Filter the results down to the name you want and then right-click on it to select "Copy as (TAG)TagUUID". Paste this into the script and you're good to go! There are tags for many different kinds of NPCs, all of the default classes, and much more.
That's it for now! There's so much that can be done with Osiris, and I hope this empowers you to make more of the mods you want. The script(s) discussed here can be expanded, customized, or made more efficient depending on your purpose and level of comfort with Osiris.
Happy modding!