Guide
In this guide, I will explain how to give every player character a Passive ability without modifying class progression tables. There are a few benefits to this technique:
- It doesn't overwrite a resource that might conflict with other mods.
- It will work with classes added by other mods without requiring a dependency.
- We can use a very similar technique to do some cool stuff with NPCs too.
However, if your mod is an overhaul of an existing class or if you only want to add a Passive to one or two specific classes, I'd recommend using the intended method (modifying class progression tables) instead of this technique.
So, what is the technique? It's a very simple Osiris script. If you don't understand Osiris, that's okay! All you need to do is copy some code and change a few names to be unique to your mod. And if you do want to learn Osiris, I hope this will be an easy way to get experience with a complete script. I won't define fundamental Osiris concepts because I want to keep this guide as simple as possible, but I will include optional sections that explain the basics of what the script is doing.
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!
Step 1: Create a Script
The first thing we need to do is create a file where we can write our Osiris script (called a goal). Open the Story Editor with one of the buttons above the game preview, highlighted in the image below. For more detail on using the Story Editor, see the 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:
- An indication of the script's purpose
- Your username abbreviation
- Your mod name abbreviation
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
.
Step 2: Adding Rules
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:
IF
LevelLoaded(_)
AND
DB_IDENTIFIER_Initialized(0)
THEN
TimerLaunch("IDENTIFIER_InitDelay", 0);
IF
TimerFinished("IDENTIFIER_InitDelay")
THEN
NOT DB_IDENTIFIER_Initialized(0);
DB_IDENTIFIER_Initialized(1);
IF
DB_Players(_Player)
AND
DB_IDENTIFIER_Initialized(1)
AND
HasPassive(_Player, "DraconicAncestry_Red", 0)
THEN
AddPassive(_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.
Optional: Osiris Explanation
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:
- A level has just been loaded (such as when a save file is loaded or when moving between areas in the game)
- The fact
0
is in the databaseDB_IDENTIFIER_Initialized
Both 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 we're planning to access the database of players to give everyone a Passive, but Osiris executes scripts/goals in alphabetical order, and so whether you have access to DB_Players
on the first frame depends on the location of your script/goal in the overall program/story. You can intentionally name your script something that will execute after the players become available 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 access to the players, 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:
- There is a player in the party for whom this rule has not executed (this condition will also trigger the rule whenever a new player is added to the party).
- The script is initialized (this condition will also trigger the rule when the script becomes initialized).
- The player does not already have the passive (which will never trigger the rule but does prevent it from trying to give players the Passive a second time if they leave and rejoin the party).
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!
Giving the Passive at a Specific Level
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...
IF
LevelLoaded(_)
AND
DB_IDENTIFIER_Initialized(0)
THEN
TimerLaunch("IDENTIFIER_InitDelay", 0);
IF
TimerFinished("IDENTIFIER_InitDelay")
THEN
NOT 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 thing you need to do is still just replace IDENTIFIER
with the one you picked for your mod in Step 1 and GiantKiller
with the Passive you want.
IF
DB_Players(_Player)
AND
DB_IDENTIFIER_Initialized(1)
THEN
PROC_IDENTIFIER_GiveLeveledPassive(_Player);
IF
LeveledUp(_Player)
AND
DB_IDENTIFIER_Initialized(1)
THEN
PROC_IDENTIFIER_GiveLeveledPassive(_Player);
PROC
PROC_IDENTIFIER_GiveLeveledPassive((GUIDSTRING)_Player)
AND
HasPassive(_Player, "GiantKiller", 0)
AND
GetLevel(_Player, _Level)
AND
_Level >= 3
THEN
AddPassive(_Player, "GiantKiller");
That's it! Just remember to choose "Generate Definitions, Build and Reload" from the "File" dropdown before testing your mod.
Optional: Osiris Explanation
These rules are structured a little differently because now we need to give the Passive in any of these three events:
- The script starts running and there are already players at level 3+
- A character joins the party at level 3+
- A player levels up to 3+
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!
Targeting Other Kinds of Characters
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, we have 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 probably won't use the passive before then anyway. Whenever combat starts, we can just look for characters involved with the fight who 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:
IF
CombatStarted(_CombatGuid)
AND
DB_Is_InCombat(_Character, _CombatGuid)
AND
NOT DB_Players((CHARACTER)_Character)
AND
IsTagged((GUIDSTRING)_Character, (TAG)GUARD_0b52f35e-fb1f-4865-bcd2-5d21ef7343cd, 1)
AND
HasPassive(_Character, "DarkOnesBlessing", 0)
THEN
AddPassive(_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.
Conclusion
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.
If you have a question or found a mistake in this guide, please leave a comment. Otherwise, happy modding!
Discussion 8 comments
A guide on how to give every player character a Passive without having to modify class progression tables. Other possibilities are discussed as well.
mod.io uses essential cookies to make our site work. With your consent, we may also use non-essential cookies to enhance your experience and understand how you interact with our services. The latter will be set only upon approval. or read our Cookies Policy.