Larian Banner: Baldur's Gate Patch 9
Previous Thread
Next Thread
Print Thread
Page 1 of 2 1 2
Joined: Jul 2014
R
Rhidian Offline OP
addict
OP Offline
addict
R
Joined: Jul 2014
Main Campaign Modding- What we know (so far*)
*More specifically, what I know as of 1.0.81 on 7/27/2014

Because it would be handy to have this information in one place, I'm going to compile the information I have gathered so far about modding the Main Campaign.

First, the two rules that are pertinent to Main Campaign mods (and how to get around them)-

1) Only one mod can be active in a given playthrough at a time. If multiple mods are enabled at game start, then only the top one in the list will be active.

2) Mods by default cannot be swapped out mid-game. If you activate a mod and then load an unmodded save, the game loads as if the mod wasn't enabled.

Workarounds-
1- The solution to this is to just merge the different mods into one. This is only really doable if the mods aren't in a .pak form though, since .pak files don't like to merge easily (unless you unpak them with third party tools).

2- Getting a mod to be effective mid-game (or removing a mod midgame) takes a bit of save file editing.
A) Create a new game with 1 mod enabled. Save it as a new save file when you exit Character Creation.
B) Go to Documents/Larian Studios/Divinity Original Sin/Player Profiles/(Profile Name)/Savegames/(New Save name)/ and copy the meta.lsb there
C) Go to your old unmodded savegame folder and paste the meta.lsb you just copied, replacing it
D) Start up the game again and load your previously unmodded save. It should now have the mod effects. You may need to teleport to reach the map you were previously at, since it'll load in Cyseal regardless of where you were before.


Now on to modding the Main Campaign itself.
1. RootTemplates (for Characters and Items)

RootTemplates are an integral part of every single item and character within the game. If a RootTemplate for a Crate says that the Crate drops MegaBoss loot, then anything that uses that RootTemplate will drop the MegaBoss loot.

Basically, when it comes to modding the existing Main Campaign, you can either change the RootTemplate itself, or change the contents of whatever the RootTemplate is pointing to. This part will deal with the former.

At the top of the Editor you can see a button that says "Create and view templates" Clicking this brings up a list that contains every RootTemplate available for your Main-dependent mod. Be sure to click View -> Sidebar at the top as well.

For this example, I'll be showing how to change the Crate RootTemplate so that it contains MegaBoss loot.

In the Template list, search for "crate" at the top and click the "All" to the side. This should bring up a list of items with "Crate" in their name. The first entry, "Cont_Crate_A" is the one we want to change.

When you click that entry, you'll see information fill up the Sidebar to the right. When modifying an existing Template, the first thing to do is make sure that it gets saved to an appropriate location. In the Sidebar near the top there is an entry called "FileName" Click the entry there, and then the "..." button to the right to select the filepath.

A box opens that asks where you want to save it, with the default opening location pointing to Divinity " Original Sin/Data/Public/Main/RootTemplates/. We want to save it in a different location instead: Divinity - Original Sin/Data/Public/(Mod Name)/RootTemplates/. (Mod Name is the name of your mod followed by a bunch of numbers when you look in your Divinity folder). The filename can be anything you want.

Once you set the correct location, save *twice*. The first time you save, it will give you an error saying you don't have Developer rights. Subsequent saves work fine though, since the Editor is no longer trying to access a protected location.

Now you can get your changes to the RootTemplate to stick. To add MegaBoss loot to the Crate, just go to the "Inventory" entry. By default it contains the "Cont_Crate;" entry. You can just edit it so that it says "Cont_Crate; MegaBoss;" instead. Save again, and now any crate that uses this RootTemplate will drop MegaBoss loot (note: MegaBoss is the name of the specific TreasureTable that big bosses use; random names for inventories won't work).

The other entries in the RootTemplate can be modified in a similar way.


2. Stats- Changing existing stats for characters/items

As I mentioned in Part 1, I said that when it comes to modding existing items you can either change the RootTemplate, or change what the RootTemplate points to. The Stats folder for your mod (or the Main campaign, should your mod not have one) is where the content that the RootTemplates point to reside.

Unfortunately this part can't be done effectively without knowledge of what the pre-existing stat names/values are. These aren't really available in the Editor; the only way to know what a Stat/Equipment value contains is to look at the Main campaign Stats files yourself, which requires third party tools at the moment.

With that being said, first you need the correct folder setup for Stats within your mod. You need to have the following folders-
Code
Data/Public/(Mod Name)/Stats/Generated/Data/
Data/Public/(Mod Name)/Stats/Generated/Links/
Data/Public/(Mod Name)/Stats/Generated/Structure/ (this may or may not need the contents of the Main campaign Structure folder in it)


In general, these two rules can be followed when modding anything concerning the Stats folder-
1) If the original Stats txt file has spaces between lines in it, your Stats.txt can safely have spaces in it.
2) If the original Stats txt file doesn't have spaces between the lines (SkillSet.txt for example), then your Stats txt file can't have spaces between the lines either.

Now say I want to modify the Source Hunters' stats when they begin a new game. Looking at their RootTemplate, it says that Player1 uses the MaleHero Stats and Player2 uses the FemaleHero Stats.

These stats are defined in the "Character.txt" of the Main campaigns" Stats/Generated/Data/ folder:
Code
new entry "MaleHero"
type "Character"
using "_Hero"
data "Weight" "75000"

new entry "FemaleHero"
type "Character"
using "_Hero"
data "Weight" "55000"


Before I continue, I must point out that each of these lines has significance. Let's take the MaleHero for example-
Code
new entry "MaleHero" 

The words "New entry" tells the Game that this is a new Stats entry. The words in the quotation marks afterwards ("MaleHero" is the name that the game and other files can refer to. Should two or more entries have the same name, then the last one loaded will have precedence.

Code
type "Character" 

This tells the game that this Stat entry belongs to a Character. There are other " ypes" that a Stat entry can have, and each of those can contain certain values.

Code
using "_Hero" 

This line is like the "import" statement of programming languages; it says that this Stat entry has the characteristics of whatever it's using (the _Hero entry) before it makes any changes.

An important thing to note is that the entry being used must be in the *same* txt file as whatever is using it. If your mod's entry has " sing "_Hero" " without defining it yourself, then the game will read that as an error and ignore it.

Code
data "Weight" "75000" 

This line is how the Stats file can assign values to the different variables defined by its type. You can look at other entries to see what values can be changed using a data line.

If you wanted to make it so that both player characters had 10 to all stats, you would need to have your txt file look something like this:

Code
new entry "_Hero"
type "Character"
data "Act" "1"
data "Act part" "1"
data "Act strength" "0"
data "Strength" "5"
data "Dexterity" "5"
data "Intelligence" "5"
data "Constitution" "5"
data "Speed" "5"
data "Perception" "5"
data "Armor" "0"
data "Reflexes" "0"
data "Vitality" "100"
data "APMaximum" "7"
data "APStart" "2"
data "APRecovery" "4"
data "Movement" "0"
data "Gain" "None"
data "Sight" "0"
data "Hearing" "0"
data "Initiative" "0"
data "FOV" "90"
data "Weight" "50000"
data "PiercingResistance" "0"
data "SlashingResistance" "0"
data "CrushingResistance" "0"
data "ShadowResistance" "0"
data "CriticalChance" "0"
data "MaxResistance" "200"
data "FireResistance" "0"
data "EarthResistance" "0"
data "WaterResistance" "0"
data "AirResistance" "0"
data "PoisonResistance" "0"
data "WarriorLore" "0"
data "RangerLore" "0"
data "SingleHanded" "0"
data "TwoHanded" "0"
data "Blackrock" "0"
data "Bow" "0"
data "Crossbow" "0"
data "Shield" "0"
data "ArmorMastery" "0"
data "BodyBuilding" "0"
data "Willpower" "0"
data "Sourcery" "0"
data "Telekinesis" "0"
data "FireSpecialist" "0"
data "WaterSpecialist" "0"
data "AirSpecialist" "0"
data "EarthSpecialist" "0"
data "Repair" "0"
data "Sneaking" "0"
data "PickPocket" "0"
data "LockPicking" "0"
data "Loremaster" "0"
data "Crafting" "0"
data "Barter" "0"
data "Charm" "0"
data "Intimidate" "0"
data "Reason" "0"
data "Charisma" "0"
data "Leadership" "0"
data "Luck" "0"
data "PathInfluence" "Lava,70;Fire,40;Electrified,40;BloodElectrified,40;CloudPoison,40;CloudStatic,40;Ooze,30"

new entry "FemaleHero"
type "Character"
using "_Hero"
data "Weight" "55000"
data "Strength" "10"
data "Dexterity" "10"
data "Intelligence" "10"
data "Constitution" "10"
data "Speed" "10"
data "Perception" "10"

new entry "MaleHero"
type "Character"
using "_Hero"
data "Weight" "75000"
data "Strength" "10"
data "Dexterity" "10"
data "Intelligence" "10"
data "Constitution" "10"
data "Speed" "10"
data "Perception" "10"



Changing SkillSets

Say you want your Wizard to start out with Lava Core instead of a simple Flare. SkillSets are what define what skills are available to a character.

Your mod's SkillSet.txt needs to be located in your mod's Stats/Generated/ folder. This is one of the files that can't tolerate empty line breaks, so don't separate any of your entries.

Looking at the Main SkillSet.txt, you can see that the Wizard uses the following skillset-
Code
new skillset "Class_Wizard"
add skill "Projectile_FlareStart"
add skill "Target_TargetedOilSurface"
add skill "Projectile_PoisonDartStart"
add skill "Projectile_StaffOfMagus"


There's only two real lines of note-
Code
new skillset "Class_Wizard" 

As like with the other entries of this type, this line tells the game that it is A) a SkillSet, and B) what name refers to it (can be used in RootTemplate entries).

Code
add skill "Projectile_FlareStart" 

This line says what skill should be added to the skillset. The name in the parentheses is defined in the Stats/Generated/Data/SkillData.txt file (or your own mod's SkillData type Data entries). The skill name for Lava Core is Path_CreateLava, so to change Flare to Lava Core this is what the mod's SkillSet.txt would look like-

Code
new skillset "Class_Wizard"
add skill "Path_CreateLava"
add skill "Target_TargetedOilSurface"
add skill "Projectile_PoisonDartStart"
add skill "Projectile_StaffOfMagus"


Note: Preset classes must have 4 skills, no matter what they are. Any more than 4 will be ignored by the game due to how Character Creation works.


Equipment entries

Say we want to change what equipment our default Wizard starts off with. The Equipment file belongs in your mod's Stats/Generated/Equipment.txt file.

First let's look at what the main game uses-
Code
new equipment "Class_Wizard"
add equipmentgroup 
add equipment entry "WPN_Quarterstaff_Sourcehunter_Fire"
add equipmentgroup 
add equipment entry "POTION_Minor_Healing_Potion"
add equipmentgroup 
add equipment entry "POTION_Minor_Healing_Potion"
add equipmentgroup 
add equipment entry "SCROLL_Resurrect_1"
add equipmentgroup 
add equipment entry "SCROLL_Fireball_1"
add equipmentgroup 
add equipment entry "CONT_Backpack_A_SourceHunter"


This time there are three types of lines-
Code
new equipment "Class_Wizard" 

This tells the game that this is an equipment entry and goes by the name "Class_Wizard" (RootTemplates can refer to this name)

Code
add equipmentgroup

This is apparently necessary before each and every item, even for duplicates.

Code
add equipment entry "WPN_Quarterstaff_Sourcehunter_Fire"

This line tells the game that the item named "WPN_Quarterstaff_Sourcehunter_Fire" from the Stats/Generated/Data folder belongs to this Equipment group.

Adding items is as simple as adding more lines. If I want to give my Wizard 10 Blood Stones at the start, I would have this-

Code
new equipment "Class_Wizard"
add equipmentgroup 
add equipment entry "WPN_Quarterstaff_Sourcehunter_Fire"
add equipmentgroup 
add equipment entry "POTION_Minor_Healing_Potion"
add equipmentgroup 
add equipment entry "POTION_Minor_Healing_Potion"
add equipmentgroup 
add equipment entry "SCROLL_Resurrect_1"
add equipmentgroup 
add equipment entry "SCROLL_Fireball_1"
add equipmentgroup 
add equipment entry "CONT_Backpack_A_SourceHunter"
add equipmentgroup
add equipment entry "Quest_Stone_Blood_B"
add equipmentgroup
add equipment entry "Quest_Stone_Blood_B"
add equipmentgroup
add equipment entry "Quest_Stone_Blood_B"
add equipmentgroup
add equipment entry "Quest_Stone_Blood_B"
add equipmentgroup
add equipment entry "Quest_Stone_Blood_B"
add equipmentgroup
add equipment entry "Quest_Stone_Blood_B"
add equipmentgroup
add equipment entry "Quest_Stone_Blood_B"
add equipmentgroup
add equipment entry "Quest_Stone_Blood_B"
add equipmentgroup
add equipment entry "Quest_Stone_Blood_B"
add equipmentgroup
add equipment entry "Quest_Stone_Blood_B"



3. Adding new skills/items to the game

I'll be going over how to add a new Skill here. This includes introducing an item (Skillbook) that allows the Skill to be learned/used. This part uses knowledge from the above sections. The example I will be using is the custom Fire rain skill I added to the game in one of my other mods.

The first thing that all Skills need is a Stats/Generated/Data entry. Within a txt file within your mod's Stats/Generated/Data/ folder, you can copy/paste the pertinent information from the Main campaign's SkillData.txt file to use as a template to which you make changes.

This is the Skill entry I ended up with for my Fire Rain skill-
Code
new entry "UNFAIR_Rain_Fire"
type "SkillData"
data "SkillType" "Rain"
data "Ability" "None"
data "Element" "Fire"
data "ActionPoints" "5"
data "Cooldown" "40"
data "ChargeDuration" "0"
data "Lifetime" "5"
data "SurfaceType" "Fire"
data "SurfaceLifetime" "12000"
data "SurfaceStatusChance" "100"
data "SkillProperties" "CreateSurface,2,5,Fire,100;TargetCreateSurface,1,5,Fire,100;Hasted,200,5"
data "TargetRadius" "15"
data "UseCharacterStats" "No"
data "UseWeaponDamage" "No"
data "AreaRadius" "15"
data "TotalSurfaceCells" "125"
data "SurfaceGrowStep" "5"
data "SurfaceGrowInterval" "150"
data "Icon" "Skill_Fire_Haste"
data "DisplayName" "UNFAIR_Rain_Fire_DisplayName"
data "Description" "UNFAIR_Rain_Fire_Description"
data "StatsDescription" "UNFAIR_Rain_Fire_StatsDescription"
data "FXScale" "100"
data "PrepareAnimationInit" "cast_long_start"
data "PrepareAnimationLoop" "cast_long_loop"
data "PrepareEffect" "FX_Skills_Fire_Summon_Prepare_A"
data "CastAnimation" "cast_long_cast"
data "CastTextEvent" "cast"
data "TargetEffect" "FX_GP_Target_Fire_A"
data "Magic Cost" "1"
data "RainEffect" "FX_Env_RainFire_A"
data "Atmosphere" "Rain"
data "ConsequencesStartTime" "1000"
data "Skillbook" "SKILLBOOK_UNFAIR_Rain_Fire"

new entry "UNFAIR_Rain_Fire_10"
type "SkillData"
using "UNFAIR_Rain_Fire"
data "Level" "10"


Now I won't be going over what every line does; it's just important to know that different skill types (Rain, Projectile, etc) make use of different parameters.

There are a few important lines though-
Code
new entry "UNFAIR_Rain_Fire"
type "SkillData"

As before, this tells the game that this is a SkillData entry, as well as its name (UNFAIR_Rain_Fire).

Further down you see
Code
new entry "UNFAIR_Rain_Fire_10"
type "SkillData"
using "UNFAIR_Rain_Fire"
data "Level" "10"

All skills have something similar; it tells the game which level the Skill is (important for determining how much your levels affect the skill effects), and the "data "Level" "10" " line says this Skill can be learned at Level 10.

Going back up a bit-
Code
data "Element" "Fire"

This tells the game that it belongs to the Pyrokinetic school of magic. If this were set to a different value, it would go into a different skill school instead.

Code
data "DisplayName" "UNFAIR_Rain_Fire_DisplayName"
data "Description" "UNFAIR_Rain_Fire_Description"
data "StatsDescription" "UNFAIR_Rain_Fire_StatsDescription"

These lines are important for getting the game to display the name and description for your custom skills.

Within the Editor, at the top there is the button that says TranslatedStringKey. If you enter that menu, a window pops up. Create a new file.

You will then need to enter a series of Key and Content pairs. For your skills, the Keys are what you put after the "DisplayName" "Description" and "StatsDescription" of your SkillData. My TranslatedStringKey file for my custom Fire rain skill looks something like this-
Code
Key: UNFAIR_Rain_Fire_DisplayName
Content: Wild Fire Rain

Key: UNFAIR_Rain_Fire_Description
Content: Bring forth a fire rain that hastens friend and foe alike.

Key:UNFAIR_Rain_Fire_StatsDescription
Content: Radius: 15m<br>Duration:5 turns<br><br>Creates fire on terrain. Hastens all targets for 5 turns, and creates fire underneath each target at the start of their turn for 5 turns.


The TranslatedStringKey goes into your Mod's Data/Mods/(Mod Name)/Localization/ folder.

----

After creating the SkillData entry (and its Localization file), the next part is to create a Skillbook RootTemplate with which it can be used. Because it's a fire skill, I can create a new RootTemplate from any existing Fire Skillbook Template.

In the Editor, I go to the Template viewer and search the Books_Scrolls for "Skill_Fire" I right click the "Book_Skill_Fire_Haste" entry, and click "Create new from selected." In the box that comes up, I set the FilePath to my mod's RootTemplate folder, and set the Displayname and File Name to whatever I'm wanting. Then I finish its creation.

Selecting the new RootTemplate that was just created, you can set various values for it. But there's one other step to do beforehand, which is to create a Stats entry for this book itself.

In my mod's Stats/Generated/Data folder, I add these to a new (or old) txt file-
Code
new entry "_Skillbooks"
type "Object"
data "ModifierType" "Item"
data "Act" "1"
data "UseAPCost" "6"
data "Value" "7"
data "Weight" "500"
data "Requirements" "Level 4"
data "InventoryTab" "Magical"

new entry "SKILLBOOK_UNFAIR_Rain_Fire"
type "Object"
using "_Skillbooks"
data "Act part" "10"
data "Requirements" "Level 10"


Now I can set the RootTemplate's Stats to "SKILLBOOK_UNFAIR_Rain_Fire" and set it's OnUse action to learn the "UNFAIR_Rain_Fire" skill (note- you might have to reload the Editor to get these options to show up, since it needs to load the new Stats files)

The last step in getting a fully functional Skillbook into the game is to create a txt file in the Stats/Generated/Links/ folder. The Links folder is how the game can link a Stats entry to a RootTemplate.

Here is my mod's Links file for the Fire Rain skill-
Code
object itemstat "SKILLBOOK_UNFAIR_Rain_Fire","ea880242-8ee0-4719-b969-325b252049d3","",0,0,"SkillbookFire",1,1,8,0,8,20


There are really three important parts to this line-
Code
object itemstat "SKILLBOOK_UNFAIR_Rain_Fire","ea880242-8ee0-4719-b969-325b252049d3"

This part is what links my Stat entry to the RootTemplate's GUID (that string of letters and numbers).

Code
"SkillbookFire" 

This is what the game refers to as an "object category". It can be anything you want. This is what the game looks at when it's looking to add random items to something. For example, the Fire vendor will randomly add items with the object category "SkillbookFire" when its determining its inventory; this is what allows my custom item to show up in the game.

Code
1,1,8,0,8,20

MinAmount | Max Amount | Priority | ??? | Min Level | Max Level |

The first two state how many can be stacked in your inventory I believe. I'm not sure about Priority (its 8 or 10 for skillbooks apparently). The Min Level and Max Level describe at what levels your item can be found. In my case, you can find the book starting at level 8. If you set this to 1, you would be able to purchase it from a vendor at the beginning of the game.


Edit:
After several attempts, I finally got this to copy/paste correctly. There's some sort of Encoding difference between the Word Processors I use and this forum.

If there are any unintelligible lines/characters remaining, please let me know so I can fix it.

Last edited by Rhidian; 28/07/14 03:07 AM.
Joined: Jul 2014
journeyman
Offline
journeyman
Joined: Jul 2014
Superb job Rhidian! This is just the kind of info we need. I've bookmarked it, but I hope somebody gives it a sticky.

Well done!

Joined: Dec 2013
stranger
Offline
stranger
Joined: Dec 2013
Excellent tutorial: now we just need to migrate it to the wiki!


Sunjammer
Code Monkey
Divinity Engine Wiki
Joined: Jul 2014
stranger
Offline
stranger
Joined: Jul 2014
Originally Posted by Klixen
Superb job Rhidian! This is just the kind of info we need. I've bookmarked it, but I hope somebody gives it a sticky.

Well done!


Larian still on vacation. Possibly forever.. :P

Rhidian is the [wo]man!

Last edited by Vivikain; 28/07/14 06:50 AM.

"Sorrow - How do you prove that you exist...? Maybe we don't exist..."
-Vivi
Joined: Jul 2014
R
Rhidian Offline OP
addict
OP Offline
addict
R
Joined: Jul 2014
Originally Posted by Vivikain

Rhidian is the man!

Fix'd


There's a few topics on my to-do list, that I'm only just a little bit familiar with.

1. Crafting combinations
I personally have not touched the crafting combinations at all, so I'm not the person who should be explaining how to manipulate or create new ones.

2. Item/Character scripting
I have only briefly touched upon this topic in my other thread. I know how to make a script and attach it to a RootTemplate, but there is still so much that needs to be learned as to what we can really do with it.

3. Globals editing
I have only recently discovered that this was possible (changing a level-specific character in the main campaign). The Mods/(Mod name)/Globals/ is the other place that the items/characters created from the RootTemplates are stored, and to me is a place for research to take place.


Edit:
And then there's the stuff I have 0 experience with (and may or may not be possible)

1. Story/Goals Editing (or creation) for the Main Campaign
The story goals can do so much. Editing the existing goals might not be possible, but we should be able to introduce new Story Goals and use them somehow.

2. Dialog Editing (or creation)
What I said for Story Editing applies here as well.

3. New Map creation for the Main Campaign
Making a new Map/Level should be doable (it should be basically the same as if creating a level for a Non-Main Campaign mod). The question would be how could a new level be linked to the existing campaign?

Last edited by Rhidian; 28/07/14 12:51 PM.
Joined: Jul 2014
R
Rhidian Offline OP
addict
OP Offline
addict
R
Joined: Jul 2014
As of 1.0.132

4. Story Scripts for the Main Campaign
Note: A full listing of the Story scripting functions is useful for this section. Such a listing can be located at http://www.larian.com/forums/ubbthreads.php?ubb=showflat&Number=531151#Post531151

Modifying an existing Main Campaign Script-

The Story for the Main module (or any module for that matter) is where many important functions are defined. Dialog and trading are two examples of important functions defined in the Main module's Story. As modders we can over-write the Story scripts that the Main campaign comes with and/or come up with our own Story scripts.

First, however, a note about the Story and savegames. The way that the Story works is that the game generates it completely at the beginning of the game (when you start a New Game) and saves that generated story with the save file itself. If the Story for a module gets changed, the changes will have no effect until a new game is started (no effect on old savegames with the module).

Because the Story is determined at New Game, the game does incorporate every activated mod's story changes (even if they are #2+ in the load order). This is in contrast to non-Story elements (like RootTemplates) that have no effect if the mod is #2+ in the load order.

Any type of Story Editing can be done via the Story Editor in the Divinity Engine. At the top middle there is an "S" icon that says Story Editor. Click that and a window opens up containing all of the scripts that the Main module uses. You can double click any of the entries and browse them at your leisure.

To over-write a pre-existing story script, right click it in the left box and click "Copy to my mod". You will then be able to make changes and have them be in effect instead of the original version.

For example, what if you want to give the player additional ability/talent/etc points upon arriving at Cyseal? First, take a look at this portion of the CYS_General story script-
Code
IF
CharacterCreationFinished(CHARACTER_NULL)
AND
CurrentLevel(_Lvl)
AND
DB_CharacterCreationLevels(_Lvl)
THEN
CharacterTeleportPartyToTriggerMovieLoadState(TRIGGER_CYS_Start_P1,"","MovieGameIntro");
ItemToCharacter(ITEM_CYS_SHGuide1, CHARACTER_Player1);
ItemToCharacter(ITEM_CYS_SHGuide2, CHARACTER_Player2);
CharacterAddGold(CHARACTER_Player1,200);
CharacterAddGold(CHARACTER_Player2,200);
DB_CYSDoTutorial(1);


The game by default gives the party gold and their items after finishing character creation, in addition to teleporting them to the beach. I need to go into an explanation of the syntax before any changes are made though, since the scripting language is different from other programming languages in several respects.

Beginning with the first lines-
Code
IF
CharacterCreationFinished(CHARACTER_NULL)

The "IF" line is what tells the game to do something if the event immediately afterwards is called. In this case, it's the CharacterCreationFinished((Character)) event. If the CharacterCreationFinished event is called, each and every script that checks IF that event was called will trigger (though whether they do anything depends on further checks detailed later on) in the order that they are present in the scripts. This will be important later on when it comes to making our own scripts.

Code
AND
CurrentLevel(_Lvl)

The "AND" keyword is what represents the additional steps that must be taken to check whether the "THEN" lines later on are called. Notice the "_Lvl" located within the parentheses. It is an Integer variable that is created locally when the event is called, and is what stores the value returned by the CurrentLevel function. Anything that starts with an underscore in Story scripts is a variable, whose type is determined by the function that creates it. This variable can be used by any further "AND" and "THEN" statements that appear within the same IF-THEN block.

Code
AND
DB_CharacterCreationLevels(_Lvl)

DB stands for DataBase, and is a way for the game to store variables for retrieval at a later time. They have additional uses too, such as for checking whether something has already been stored in a database. In this case, because the DB is located after an "AND" and the _Lvl variable has already been defined, it is checking whether the value in _Lvl is within the DataBase with the name CharacterCreationLevels. If _Lvl wasn't previously defined, it would be outputting the first value in that DataBase into the _Lvl variable instead (ie retrieving a stored variable). DataBases can be created and used pretty much anywhere in Story scripting, and the type of data stored is determined when the first entry is entered.

Code
THEN
CharacterTeleportPartyToTriggerMovieLoadState(TRIGGER_CYS_Start_P1,"","MovieGameIntro");
ItemToCharacter(ITEM_CYS_SHGuide1, CHARACTER_Player1);
ItemToCharacter(ITEM_CYS_SHGuide2, CHARACTER_Player2);
CharacterAddGold(CHARACTER_Player1,200);
CharacterAddGold(CHARACTER_Player2,200);
DB_CYSDoTutorial(1);

The "THEN" keyword is what tells the game that if all of the previous IF-ANDs were true, then everything afterwards occurs. These are the actions that are performed in sequential order. Note that all of these end in semi-colons, while the IF-ANDs did not have semi-colons. The other thing to note here is the DataBase line "DB_CYSDoTutorial(1);". Because it is in a THEN action, this line is *creating* a database entry with the value 1.

A side note on Databases before continuing on, the other two uses for Databases can be in the IF section as well as an additional use in the THEN section. Here's a part of the script that uses a Database in the IF line-
Code
IF
DB_Companion(_Companion)
AND
CharacterIsFemale(_Companion,1)
THEN
DialogSetCharacterEvent("EVENT_player_is_female",_Companion,0);

In this case, this script portion triggers whenever a database entry is created for the Companion database with a single variable type.

The other use for databases is the NOT DB_Name(_Variable) in the THEN section, which deletes the specific database entry if it exists.

Now say that I want to give the players 9001 gold instead of the measly 200 gold when they arrive at the beach. I can modify the script to look like so-
Code
IF
CharacterCreationFinished(CHARACTER_NULL)
AND
CurrentLevel(_Lvl)
AND
DB_CharacterCreationLevels(_Lvl)
THEN
CharacterTeleportPartyToTriggerMovieLoadState(TRIGGER_CYS_Start_P1,"","MovieGameIntro");
ItemToCharacter(ITEM_CYS_SHGuide1, CHARACTER_Player1);
ItemToCharacter(ITEM_CYS_SHGuide2, CHARACTER_Player2);
CharacterAddGold(CHARACTER_Player1,9001);
CharacterAddGold(CHARACTER_Player2,9001);
DB_CYSDoTutorial(1);

This is a simple change; other changes can be easily made or added to this portion to change what happens upon arriving at Cyseal beach. You can even change where the party arrives at after character creation by pointing to a different Trigger with the first THEN action.

The final step before the changes are actually within the game is to "Generate Definitions and Build Story" which is under the File menu. The Definitions only need to be generated once, and the Story needs to be built anytime you want changes to stick. You'll notice that the various functions become color coded after the Definitions are built.
Each of the colors has a specific place they can be used:
Purple- IF statement only, these are events that can be responded to
Red- AND statements only. These are interim calculations/variable getters that can be used for various things.
Green- THEN statements only. These are all actions.
Black- Their use locations can vary.


Making your own Scripts-

Instead of modifying an existing script, you can also create your own to use in the main campaign. All told it's not too much different, since the syntax still must be followed and only certain commands can be used.

To create your own Script, just click the "New Script" button at the top left of the Story Editor. Enter a name for your script (with no spaces) and you'll have yourself an empty script.

I didn't note this above, but there are three main sections to a Script file. The INIT section (top), KB section (middle), and EXIT section (bottom). The INIT section is used primarily to create Database entries at the beginning of the game (upon New Game). The KB section houses all of the events that occur mid-game. I have never seen the EXIT section used, so I'm not sure what it is used for.

For this part of the tutorial I will be basing the examples off of my "Journey of the Lone Wolf" mod, which demonstrate several concepts.

First the example code in its entirety-
INIT Section-
Code
DB_LoneWolfAbility(CHARACTER_Player1,2,2);
DB_LoneWolfAbility(CHARACTER_Player1,3,2);
DB_LoneWolfAbility(CHARACTER_Player1,4,2);
DB_LoneWolfAbility(CHARACTER_Player1,5,2);
DB_LoneWolfAbility(CHARACTER_Player1,6,3);
DB_LoneWolfAbility(CHARACTER_Player1,7,3);
DB_LoneWolfAbility(CHARACTER_Player1,8,3);
DB_LoneWolfAbility(CHARACTER_Player1,9,3);
DB_LoneWolfAbility(CHARACTER_Player1,10,4);
DB_LoneWolfAbility(CHARACTER_Player1,11,4);
DB_LoneWolfAbility(CHARACTER_Player1,12,4);
DB_LoneWolfAbility(CHARACTER_Player1,13,4);
DB_LoneWolfAbility(CHARACTER_Player1,14,4);
DB_LoneWolfAbility(CHARACTER_Player1,15,4);
DB_LoneWolfAbility(CHARACTER_Player1,16,4);
DB_LoneWolfAbility(CHARACTER_Player1,17,4);
DB_LoneWolfAbility(CHARACTER_Player1,18,4);
DB_LoneWolfAbility(CHARACTER_Player1,19,4);
DB_LoneWolfAbility(CHARACTER_Player1,20,4);

DB_LoneWolfAttribute(CHARACTER_Player1,3,1);
DB_LoneWolfAttribute(CHARACTER_Player1,5,1);
DB_LoneWolfAttribute(CHARACTER_Player1,7,1);
DB_LoneWolfAttribute(CHARACTER_Player1,9,1);
DB_LoneWolfAttribute(CHARACTER_Player1,11,1);
DB_LoneWolfAttribute(CHARACTER_Player1,13,1);
DB_LoneWolfAttribute(CHARACTER_Player1,15,1);
DB_LoneWolfAttribute(CHARACTER_Player1,17,1);
DB_LoneWolfAttribute(CHARACTER_Player1,19,1);
DB_LoneWolfAttribute(CHARACTER_Player1,21,1);

DB_LoneWolfTalent(CHARACTER_Player1,5,1);
DB_LoneWolfTalent(CHARACTER_Player1,9,1);
DB_LoneWolfTalent(CHARACTER_Player1,13,1);
DB_LoneWolfTalent(CHARACTER_Player1,17,1);
DB_LoneWolfTalent(CHARACTER_Player1,21,1);


KB Section-
Code
PROC
ProcLoneWolfAbilities((CHARACTER)_Player)
AND
CharacterGetLevel(_Player, _charLevel)
AND
DB_LoneWolfAbility(_Player, _charLevel, _abilPoints)
THEN
CharacterAddAbilityPoint(_Player, _abilPoints);

PROC
ProcLoneWolfAttributes((CHARACTER)_Player)
AND
CharacterGetLevel(_Player, _charLevel)
AND
DB_LoneWolfAttribute(_Player,_charLevel,_attPoints)
THEN
CharacterAddAttributePoint(_Player, _attPoints);

PROC
ProcLoneWolfTalents((CHARACTER)_Player)
AND
CharacterGetLevel(_Player, _charLevel)
AND
DB_LoneWolfTalent(_Player, _charLevel, _talPoints)
THEN
CharacterAddTalentPoint(_Player, _talPoints);


IF
CharacterLeveledUp(_Player)
AND
_Player.isPlayer()
THEN
ProcLoneWolfAbilities(_Player);
ProcLoneWolfAttributes(_Player);
ProcLoneWolfTalents(_Player);

PROC
ProcCheckLoneWolfStart((CHARACTER)_Player)
AND
CharacterHasTalent(_Player,"LoneWolf",1)
THEN
CharacterAddTalentPoint(_Player,1);

PROC
ProcCheckLoneWolfStart((CHARACTER)_Player)
AND
NOT CharacterHasTalent(_Player,"LoneWolf",1)
THEN
CharacterAddTalent(_Player,"LoneWolf");

IF
CharacterCreationFinished(CHARACTER_NULL)
AND
CurrentLevel(_Lvl)
AND
DB_CharacterCreationLevels(_Lvl)
THEN
CharacterAddAttributePoint(CHARACTER_Player1,6);
CharacterAddAbilityPoint(CHARACTER_Player1,5);
CharacterAddTalentPoint(CHARACTER_Player1,2);
ProcCheckLoneWolfStart(CHARACTER_Player1);


As you can see, I incorporate various aspects into this piece of scripting. I will be walking through what this code does step by step, starting with what it does first-
Code
IF
CharacterCreationFinished(CHARACTER_NULL)
AND
CurrentLevel(_Lvl)
AND
DB_CharacterCreationLevels(_Lvl)
THEN
CharacterAddAttributePoint(CHARACTER_Player1,6);
CharacterAddAbilityPoint(CHARACTER_Player1,5);
CharacterAddTalentPoint(CHARACTER_Player1,2);
ProcCheckLoneWolfStart(CHARACTER_Player1);

The IF-ANDs are identical to the IF-ANDs located within the CYS_General file in the example from modifying an existing story script. This is taking advantage of the fact that each and every CharacterCreationFinished event is called sequentially, and is guaranteed to work at the same time that the CYS_General script does (because the ANDs aren't changed by the THENs). The CharacterAdd functions are predefined actions that I use to give additional Attribute/Ability/Talent points to Player 1 at the beginning of the game.

But what about the "ProcCheckLoneWolfStart(CHARACTER_Player1);" line? This looks like a function, but is not one of the predefined functions in any Story Editor function list. This is a function that I create elsewhere in the Script-

Code
PROC
ProcCheckLoneWolfStart((CHARACTER)_Player)
AND
CharacterHasTalent(_Player,"LoneWolf",1)
THEN
CharacterAddTalentPoint(_Player,1);

PROC
ProcCheckLoneWolfStart((CHARACTER)_Player)
AND
NOT CharacterHasTalent(_Player,"LoneWolf",1)
THEN
CharacterAddTalent(_Player,"LoneWolf");

PROCs are essentially user-defined functions. They are actions that can be called anywhere you want in other scripts, so long as they are defined within the same Story. Notice that I have two definitions with slightly different AND statements; this is also taking advantage of the fact that every event (like the PROC) is checked *sequentially*. In this case, the second block that adds Lone Wolf to the character will never occur before the first code block that is checking whether the character has Lone Wolf, so the first one will only ever trigger if the conditions are fulfilled prior to the PROC being called.

It's important to note the differences in the syntax for the PROC line itself-
Code
PROC
ProcCheckLoneWolfStart((CHARACTER)_Player)

PROC is like IF, except it's defining a function that is called. The name of the function can be anything really; the important part is what's inside the parentheses. The (CHARACTER)_Player is stating that the function is accepting any variable with the CHARACTER variable type, and storing that value in a variable called _Player to be used by the PROC. If I wanted the PROC to accept an integer, I could say ProcNumber((INTEGER)_Num). The Variable type is within parentheses and in all upper case letters, while the variable name is immediately following it (starting with an underscore).

With this information in mind, the rest of the code is fairly straightforward-
Code
IF
CharacterLeveledUp(_Player)
AND
_Player.isPlayer()
THEN
ProcLoneWolfAbilities(_Player);
ProcLoneWolfAttributes(_Player);
ProcLoneWolfTalents(_Player);

This block of code is triggered whenever a player levels up. I call three Proc functions in the THEN section as a result. These Procs are essentially the same, so I will only go into detail about one of them-

Code
PROC
ProcLoneWolfTalents((CHARACTER)_Player)
AND
CharacterGetLevel(_Player, _charLevel)
AND
DB_LoneWolfTalent(_Player, _charLevel, _talPoints)
THEN
CharacterAddTalentPoint(_Player, _talPoints);

There are a few important things to note in this PROC. First, a CHARACTER type variable called _Player is being passed to this PROC when called. Because _Player is defined, the CharacterGetLevel uses that _Player and stores their level in the _charLevel variable (integer) which is defined by this function.

Then comes the DataBase line. By the time that this is reached, _Player and _charLevel are already defined, while _talPoints is not. As such, the game checks the Database (defined in the INIT section) for any entry that uses both the _Player and _charLevel numbers, and outputs the third entry in the Database into the new variable. If Player 1 levels up to level 5, for example, the game would match it with this line: "DB_LoneWolfTalent(CHARACTER_Player1,5,1);" and output 1 to _talPoints. If Player 1 levels up to 6 instead, because there is no appropriate Database entry it will not do anything and the THEN actions will not occur.

As with any Story scripts, make sure to Generate Definitions at least once and Build Story after making any changes to get them to stick in-game. Much more can be done with scripts; these examples have just been to demonstrate the core concepts of IF/PROC/AND/THEN and Databases, which are used for everything in Story scripting. A list of pre-defined functions is handy to reference when Story scripting.

Joined: Aug 2014
T
stranger
Offline
stranger
T
Joined: Aug 2014
I'm having trouble modifying RootTemplates as instructed in the original post. I change the save filepath as directed and save twice, the first save gives an error as the instructions indicate it will and the second save appears to work. I can then make changes such as adding megaboss loot to a crate, to use the example from the original post but when I exit the editor and open the mod again the RootTemplate appears to have reset to its original settings in terms of inventory and save path.

Edit: It's also probably worth mentioning that I am saving after making the changes as well, in both the RootTemplate viewer and in the editor's main window.

Last edited by TurtlesAWD; 31/08/14 06:37 PM.
Joined: Jul 2014
R
Rhidian Offline OP
addict
OP Offline
addict
R
Joined: Jul 2014
Is there a file being created in your mod's RootTemplate folder after saving? The creation of the file is what indicates that your RootTemplate overwriting should be working. In general, the file won't be created the first time you try to save (and it throws the error) while it should be the second time.

If the file isn't being created then that would cause the game to revert back to the original upon loading the mod. If it is being created successfully, try saving again after making the changes one more time and see if it sticks.

Joined: Jul 2014
R
Rhidian Offline OP
addict
OP Offline
addict
R
Joined: Jul 2014
5. CharScripts and ItemScripts

Anatomy of a charScript/itemScript

When it comes to scripting within Divinity - Original Sin, there are basically two types of scripts: Story scripts and CharScripts/ItemScripts. The syntax and commands used are different for the most part between these two types of scripts.

This section deals with CharScripts/ItemScripts. Technically these are separate things, but they use the same structure and commands. The only difference is that CharScripts are used by Characters and ItemScripts are used by items.

First though is how to get a CharScript/ItemScript into a module in the first place. Outside of the editor, you will want to create a Scripts folder within the Public/(Mod Name)/ for your mod. Within that folder you can make an empty .charScript or .itemScript file with the name of your choice.

Next open up the Editor and load up your mod (if it is not done so already). Go to the Resources manager; you will see a list of folders, including one with your mod's name on it. Click that folder and then click the "Create Package" icon (yellow box with a green plus sign next to it). You can name the package anything you want.

Click your new package, and then click the "Import Resource" button on the top left. In the window that pops up, click the charScript/itemScript icon and navigate it towards the empty script file within the Public/(Mod Name)/Scripts/ folder. Once that is done, your charScript/itemScript is now available for use in your module.

To actually attach a charScript to a character, you need to view a Character Template within the Sidebar and click the "Scripts" button. In the window that pops up, you can add your script to the character. ItemScripts can be added to Item Templates.

Now to look at an actual CharScript. For this part, I will use a much shortened version of the DefaultCharacter.charScript that comes with the game.
Code
#INCLUDE Base
INIT

USING Base

CHARACTER:__Me
FLOAT3:%PeaceReturnPosition=null
CHARACTER:%currentSetTargetDefault=null
FLOAT:%setTargetDefaultBestScore=10000
INT:%defaultEvaluateTarget=1
INT:%EvaluateScores=0


EVENTS
EVENT DontAttackAlliesOrInvisibles // to solve charmed chars getting back to normal but still targeting as if they were charmed
VARS
	CHARACTER:_Target
ON
	OnTurn()
ACTIONS
	IF "c1&(c2|c3)"
		CharacterGetEnemy(_Target,__Me)
		CharacterIsAlly(__Me,_Target)
		CharacterHasStatus(_Target,INVISIBLE)
	THEN
		CharacterSetEnemy(__Me,null)
		Set(%defaultEvaluateTarget,1)
	ENDIF
	
	
BEHAVIOUR 

REACTION ReturnToPeacePosition,15000
USAGE PEACE
CHECK "!c1"
	IsEqual(%PeaceReturnPosition,null)
ACTIONS
	CharacterMoveTo(%PeaceReturnPosition,1,1,1,0)
	CharacterEvent(__Me,"ClearPeaceReturn")
	
REACTION Combat_AttackSetEnemy, 7
USAGE COMBAT
VARS
	CHARACTER:_Enemy
	FLOAT:_dist
CHECK "c1&(c2|(c3&!c4))"
	CharacterGetEnemy(_Enemy,__Me) // returns false if null
	CharacterCanSee(__Me,_Enemy)
	GetInnerDistance(_dist,__Me,_Enemy)
	IsGreaterThen(_dist,4.0)
ACTIONS
	CharacterAttack(_Enemy)
INTERRUPT
ON
	OnMovementFailed(_)
ACTIONS
	DelayReaction("Combat_AttackSetEnemy",3)

There are essentially three major portions to a charScript/itemScript file.
1. INIT section- This is where 'global' variables used by the script are initialized (global means anything within the script can use it)
2. EVENTS section- These store the various events that the character/item can respond to
3. BEHAVIOUR section- These hold REACTIONs, which are how the characters behave ingame.

Let's begin with the INIT section, since this is always at the top of the script file-
Code
#INCLUDE Base
INIT

USING Base

CHARACTER:__Me
FLOAT3:%PeaceReturnPosition=null
CHARACTER:%currentSetTargetDefault=null
FLOAT:%setTargetDefaultBestScore=10000
INT:%defaultEvaluateTarget=1
INT:%EvaluateScores=0


Code
#INCLUDE Base
INIT
USING Base

The #INCLUDE and USING indicate that this file is using the contents of another Script file (in this case, the Base.charScript file). This is the game's way of 'extending' charScript functionality. For the most part these two are optional; they don't need to be included if you're not including contents of another charScript.

The INIT, however, is vital in that it indicates that this is the INIT section. It goes after any #INCLUDEs and before anything else.

Code
CHARACTER:__Me

This is a unique variable declaration for a charScript file. The __Me is referring to whichever character that is using the Script. The equivalent line for an itemScript is
Code
ITEM:__Me


Note that the syntax here is VARIABLETYPE (in capital letters) followed by a colon (:) followed by the name of the variable. This is consistent even with the next variable declarations-
Code
FLOAT3:%PeaceReturnPosition=null
CHARACTER:%currentSetTargetDefault=null
FLOAT:%setTargetDefaultBestScore=10000
INT:%defaultEvaluateTarget=1
INT:%EvaluateScores=0

Note that all of these variable names begin with a percentage sign (%), and the variable names are followed by an =(value). These are declaring a variable and assigning a value whenever the script is first initialized in-game (usually whenever the map that the character using the script appears on is loaded).

Before continuing, there is one other important aspect to know about the variable declarations-
Code
EXTERN FLOAT:%setTargetDefaultBestScore=10000

The EXTERN keyword can be used to allow the value to be set outside of the script when it is being assigned to a character/item.


Events section-
Code
EVENTS
EVENT DontAttackAlliesOrInvisibles
VARS
	CHARACTER:_Target
ON
	OnTurn()
ACTIONS
	IF "c1&(c2|c3)"
		CharacterGetEnemy(_Target,__Me)
		CharacterIsAlly(__Me,_Target)
		CharacterHasStatus(_Target,INVISIBLE)
	THEN
		CharacterSetEnemy(__Me,null)
		Set(%defaultEvaluateTarget,1)
	ENDIF

The EVENTS section is where most of the scripting will occur. This is where most of the items in the AI scripting part of the Wiki can be used.
Code
EVENTS

This single line starts off the EVENTS section. It is always included if there are any EVENTS

Code
EVENT DontAttackAlliesOrInvisibles

This is an event declaration. These always start with EVENT, and are followed by any unique name that you want. Just make sure that this name doesn't overlap with any other EVENT used by the same character.

Code
VARS
	CHARACTER:_Target

Between the EVENT declaration and the ON section (coming next) can be the Variable declaration section. In this section you include the data type and name of any and all variables used within the EVENT (unless they are global variables from the INIT section). The syntax here is similiar to the INIT section, but instead of a (%) an underscore is used before the variable name (and these are not initialized to any value). If the EVENT doesn't use any other variables, this section isn't necessary.

Code
ON
	OnTurn()

The ON section is required for all EVENTS. This is the part that says when the EVENT should trigger. It's not shown here, but multiple things can trigger the event if they are separated by a line
Code
ON
	OnTurn()
	OnInit()

The various triggers can be found on the wiki.

Code
ACTIONS
	IF "c1&(c2|c3)"
		CharacterGetEnemy(_Target,__Me)
		CharacterIsAlly(__Me,_Target)
		CharacterHasStatus(_Target,INVISIBLE)
	THEN
		CharacterSetEnemy(__Me,null)
		Set(%defaultEvaluateTarget,1)
	ENDIF

The second required part of an EVENT is the ACTIONS section, which tells the game what happens when the event triggers.

Code
IF "c1&(c2|c3)"
		CharacterGetEnemy(_Target,__Me)
		CharacterIsAlly(__Me,_Target)
		CharacterHasStatus(_Target,INVISIBLE)

An IF statement can be within the ACTIONS section. All IF statements must contain an IF, THEN, and ENDIF at a minimum. Note how there is "c1&(c2|c3)" within quotes immediately after the IF; these are the conditions that the IF statement is checking before deciding whether to proceed to the THEN section.

But what is "c1&(c2|c3)" referring to? These are the lines immediately following the IF statement-
Code
c1 - CharacterGetEnemy(_Target, __Me)
c2 - CharacterIsAlly(__Me, _Target)
c3 - CharacterHasStatus(_Target, INVISIBLE)

The & within the conditional means AND, and the | means OR. In total, as long as c1 is true and either c2 or c3 are true, the IF statement will trigger.

If you wanted to add another conditional, you could have it like so-
Code
IF "c1&(c2|c3)&c4"
		CharacterGetEnemy(_Target,__Me)
		CharacterIsAlly(__Me,_Target)
		CharacterHasStatus(_Target,INVISIBLE)
		CharacterHasStatus(_Target,INVISIBLE)

Note that certain script commands can only be used within an IF statement (like all of the ones used in this example). The IF statement is where variable values can be obtained from commands.

After the conditionals comes the THEN section-
Code
THEN
		CharacterSetEnemy(__Me,null)
		Set(%defaultEvaluateTarget,1)
ENDIF

Notice that here is where global variables within the script are used. The Set(%defaultEvaluateTarget,1) is how the value of 1 can be assigned to that particular variable.


BEHAVIOUR section- (Full Disclosure: I haven't used BEHAVIOURs before, so take my analysis of this part with a grain of salt)
Lastly there is the BEHAVIOUR section, which houses the different behaviours NPCs can have within the game.
Code
BEHAVIOUR 

REACTION ReturnToPeacePosition,15000
USAGE PEACE
CHECK "!c1"
	IsEqual(%PeaceReturnPosition,null)
ACTIONS
	CharacterMoveTo(%PeaceReturnPosition,1,1,1,0)
	CharacterEvent(__Me,"ClearPeaceReturn")
	
REACTION Combat_AttackSetEnemy, 7
USAGE COMBAT
VARS
	CHARACTER:_Enemy
	FLOAT:_dist
CHECK "c1&(c2|(c3&!c4))"
	CharacterGetEnemy(_Enemy,__Me) // returns false if null
	CharacterCanSee(__Me,_Enemy)
	GetInnerDistance(_dist,__Me,_Enemy)
	IsGreaterThen(_dist,4.0)
ACTIONS
	CharacterAttack(_Enemy)
INTERRUPT
ON
	OnMovementFailed(_)
ACTIONS
	DelayReaction("Combat_AttackSetEnemy",3)


There are a few key differences between this section and the EVENTS section.
Code
BEHAVIOUR

This starts off the section

Code
REACTION Combat_AttackSetEnemy, 7

Here the syntax is REACTION followed by the reaction name, then a comma, and then an integer denoting the "Priority" of the reaction. The Priority is the key difference between this section and the EVENTS. In general, only one Behaviour can be active at a time, and the Behaviour that is active is the one with the highest Priority. Reactions with a priority of less than (or equal to?) 0 will never occur unless something raises the Priority higher than 0. In this example, the reaction with the priority of 15000 will be prioritized over the reaction with a priority of 7.

Code
USAGE COMBAT

This is the other key difference. Reactions can be set to occur either in PEACE (USAGE PEACE) or in COMBAT (USAGE COMBAT).

Code
VARS
	CHARACTER:_Enemy
	FLOAT:_dist
CHECK "c1&(c2|(c3&!c4))"
	CharacterGetEnemy(_Enemy,__Me) // returns false if null
	CharacterCanSee(__Me,_Enemy)
	GetInnerDistance(_dist,__Me,_Enemy)
	IsGreaterThen(_dist,4.0)
ACTIONS
	CharacterAttack(_Enemy)

The VARS and ACTIONS sections are practically identical to how they work in the EVENTS section. However, instead of an ON section, Reactions can use a CHECK section instead which follows most of the same rules as IF statements do with regards to conditionals.

Code
INTERRUPT
ON
	OnMovementFailed(_)
ACTIONS
	DelayReaction("Combat_AttackSetEnemy",3)

The Interrupt is basically an Event-like addon to a Reaction that tells the Character to do something special when an event is triggered.


Story and charScripts/itemScripts working together

Story scripts and charScripts/itemScripts can work together to great effect. This part will be a case study on a short example that I came up with.

charScript code-
Code
INIT

CHARACTER:__Me
INT:%hasRespawned=0

EVENTS
EVENT LetThereBeLife
ON
	OnInit()
ACTIONS
IF "!c1"
	IsEqual(%hasRespawned, 0)
THEN
	CharacterEvent(__Me,"TakeThatWhichWasGiven")
ENDIF
IF "c1&c2"
	IsEqual(%hasRespawned, 0)
	CharacterIsDead()
THEN
	CharacterEvent(__Me, "RezMePlz")
ENDIF
	
	
EVENT LetThereBeDeath
ON
	OnCharacterEvent(__Me, "TakeThatWhichWasGiven")
ACTIONS
	CharacterDie(__Me)


Story code-
Code
IF
CharacterEvent(_Char, "RezMePlz")
THEN
CharacterResurrect(_Char);
CharacterSetVarInteger(_Char, "hasRespawned", 1);


When the charScript is first initialized, %hasRespawned is set to 0. By itself, the variable %hasRespawned will always be 0 because there is nothing in the charScript to change it.

Note that when the character that the script is attached to is dead and the game is loaded up (ie triggering OnInit()), the LetThereBeLife EVENT will trigger the CharacterEvent(__Me, "RezMePlz") command because %hasRespawned is 0. At that time, the Story code picks up that event and executes its code, resurrecting the dead character and setting %hasRespawned to 1.

Code
CharacterSetVarInteger(_Char, "hasRespawned", 1);

The syntax of this line is important in that the % is omitted from the variable name in the middle. The CharacterSetVar commands and CharacterGetVar commands can be used to interact with the Global variables within a given character's charScript.

After %hasRespawned is set to 1, then the next time that the game is loaded (or map changes) then the first part of the LetThereBeLife EVENT will trigger, eventually resulting in the character dying because the change to the %hasRespawned variable persists.


Note: This section of the tutorial doesn't cover everything there is to charScript/itemScripting. Notably, Generics in charScript/itemScripts (ie the stuff that uses _$) are not covered (because I have 0 experience in making them for D:OS). Overwriting Main campaign charScripts are not covered here as well.

Joined: Oct 2005
T
journeyman
Offline
journeyman
T
Joined: Oct 2005
Just a little addition to conditions you just forgot: You can negate them with '!'

And for BEHAVIOURs: The Priority can be changed with SetPriority(STRING BehaviourName, INT value). If you want a behaviour to occur only once you set the priority to 0 in the script and you do this: When the behaviour should occur you use SetPriority(STRING BehaviourName, INT value) in the ACTIONs part to set it higher and the last statement in the behaviour section should be to set the priority back to 0. Example:

Code
EVENT ShouldDrinkPotion
ON
	OnDamage(_,_,_,_)
ACTIONS
	IF "c1"
		ItemGetFromInventory(_, __Me, Minor_Healing_Potion)
	THEN
		SetPriority(DrinkPotion, 100)
	ENDIF

Code
REACTION DrinkPotion, 0
USAGE COMBAT
CHECK "c1"
	CharacterCanDrinkPotion(__Me, Minor_Healing_Potion, 0)
ACTIONS
	CharacterDrinkPotion(Minor_Healing_Potion)
	SetPriority(DrinkPotion, 0)



Furthermore in my experience some script commands can only be used in the BEHAVIOUR section (the one I know is CharacterDrinkPotion(Potion)).

Last edited by Raze; 09/09/14 11:16 PM. Reason: update code with that from later post
Joined: Jul 2014
L
stranger
Offline
stranger
L
Joined: Jul 2014
Rhidian, you're amazing. Does anyone have the authority to sticky this??

Joined: Oct 2005
T
journeyman
Offline
journeyman
T
Joined: Oct 2005
I just saw that there are some mistakes in my code from my earlier post and I can't edit it so here it is without the mistakes.

<moved to earlier post>

Last edited by Raze; 09/09/14 11:16 PM.
Joined: Sep 2014
S
stranger
Offline
stranger
S
Joined: Sep 2014
One thing I've been confused with when it comes to the mods and especially merging two or more together to try to get multiple to work for the campaign is the "meta.lsx" file. I am unsure as to what this file does exactly, and if it's needed at all, however sometimes I encounter errors and changing this file really only changed the mod name on the mod menu, but it made me unable to load the savegame again.

Any info I could get on this would be appreciated.

Joined: Aug 2014
M
Mag Offline
journeyman
Offline
journeyman
M
Joined: Aug 2014
Excellent info. I wish I would have read this before I started modding.

Joined: Jul 2014
R
Rhidian Offline OP
addict
OP Offline
addict
R
Joined: Jul 2014
Originally Posted by SuperMarioXX
One thing I've been confused with when it comes to the mods and especially merging two or more together to try to get multiple to work for the campaign is the "meta.lsx" file. I am unsure as to what this file does exactly, and if it's needed at all, however sometimes I encounter errors and changing this file really only changed the mod name on the mod menu, but it made me unable to load the savegame again.

Any info I could get on this would be appreciated.


The meta.lsx file basically says
1) What modules are activated in the save game
2) What the in-game time is
3) What map you are on

If you're trying to merge a mod into an existing save, you would need to create a new game with the desired mod(s) enabled, and then copy/paste the meta.lsx over (and overwrite the old one). How are you changing the meta.lsx file, and what mods are you trying to merge?

Joined: Sep 2014
S
stranger
Offline
stranger
S
Joined: Sep 2014
Originally Posted by Rhidian

The meta.lsx file basically says
1) What modules are activated in the save game
2) What the in-game time is
3) What map you are on

If you're trying to merge a mod into an existing save, you would need to create a new game with the desired mod(s) enabled, and then copy/paste the meta.lsx over (and overwrite the old one). How are you changing the meta.lsx file, and what mods are you trying to merge?


I had the blank "Modified Main" mod you described in the previous campaign modding thread, and then I tossed in Source Difficulty and Respawning Mobs and maybe a few others (Rare Skillbooks Always Spawn, Crafting Tweaks and Enhancements), and none of them worked to my knowledge. It had been using the Modified Main meta.lsx file, and when I changed it to say the Source Difficulty meta.lsx the game stopped working.

Were the mods I tried to merge just unmergeable?

Joined: Jul 2014
R
Rhidian Offline OP
addict
OP Offline
addict
R
Joined: Jul 2014
Originally Posted by SuperMarioXX
Originally Posted by Rhidian

The meta.lsx file basically says
1) What modules are activated in the save game
2) What the in-game time is
3) What map you are on

If you're trying to merge a mod into an existing save, you would need to create a new game with the desired mod(s) enabled, and then copy/paste the meta.lsx over (and overwrite the old one). How are you changing the meta.lsx file, and what mods are you trying to merge?


I had the blank "Modified Main" mod you described in the previous campaign modding thread, and then I tossed in Source Difficulty and Respawning Mobs and maybe a few others (Rare Skillbooks Always Spawn, Crafting Tweaks and Enhancements), and none of them worked to my knowledge. It had been using the Modified Main meta.lsx file, and when I changed it to say the Source Difficulty meta.lsx the game stopped working.

Were the mods I tried to merge just unmergeable?


It sounds to me like you're overwriting the meta.lsx within the a Data/Mods/Modified_Main... folder. The meta.lsx files within the Data/Mods/ folder is not the one I am referring to; the meta.lsx that gets replaced is the SaveGame's meta.lsx file instead, which should be in Documents/Larian Studios/Divinity Original Sin/Profiles/(Name)/(Savefile)/.

Joined: Sep 2014
S
stranger
Offline
stranger
S
Joined: Sep 2014
Yes, that is the meta.lsx file I was referring to; so I guess you can't merge two mods that both contain a different meta.lsx file as far as the mods folder is concerned?

Joined: Jul 2014
R
Rhidian Offline OP
addict
OP Offline
addict
R
Joined: Jul 2014
The Mods meta.lsx file tells the game what the unique ID (and other identifying information) for a mod is. This ID is also what is stored in a Save File in the portion that lists what mods are present in the save.

So for the Mods folder merging, the meta.lsx of the original mod stays the same. I haven't tested Mods folder merging myself, but of the other folders in Mods, I am certain that the Localization folder can be safely merged, and am confident that the Globals can be merged fairly safely. I'm not so sure about the Story Goals.

Joined: Sep 2014
S
stranger
Offline
stranger
S
Joined: Sep 2014
None of it seems to matter if the meta.lsx file isn't also copied over from that mod, which is pretty much limiting me to one mod per campaign runthrough. So I can't have say, custom portraits and respawning enemies, or higher movement speed and refreshing vendors, or much less all of them at once. Unless you know of a workaround, or maybe I'm just messing up, but I can't get any mods to work but the one with it's meta.lsx file when trying to merge.

I may have just discovered something that I totally was ignoring. You've been mentioning "RootTemplates" and I just now found where those are stored. I haven't been merging them. I'm going to try that now.

Last edited by SuperMarioXX; 19/09/14 06:30 PM.
Page 1 of 2 1 2

Link Copied to Clipboard
Powered by UBB.threads™ PHP Forum Software 7.7.5