Backgrounds tutorial
Contents
Introduction
This tutorial shows you how to modify the character backgrounds that can be selected during character generation. This is likely only of use if you are creating your own module or campaign as there would be no point to changing the background in the single player campaign unless you were also going to make extensive additions to support the new backgrounds (e.g a completely new origin story). So the example we will use for this tutorial is a new stand alone module in which we completely replace the existing backgrounds. Changing backgrounds is especially useful if your module is set in a different lore to the main Dragon Age game or if you simply want to reduce the scope of the number of different backgrounds.
We will also demonstrate some important principals that can be followed when modifying other core rules such as available races or classes. Most importantly we will add the new backgrounds in a way that doesn’t require any editing of core resources. By leaving the core resources alone we make our changes compatible with future patches which might otherwise overwrite our work. Furthermore keeping the core resources in tact allows us to make different modifications to different modules within the same toolset.
It is recommended that the reader should have some experience with scripting or coding in order to carry out the operations involved. The more you are relying on just copy pasting these instructions the more likely you are to make mistakes that you won’t know how to fix.
The backgrounds demo module
Any basic module will do for the purposes of this tutorial. If you want to create a test module for this tutorial then just create a simple module with a single area and a starting location. Make sure that the only thing selected in the module’s hierarchy is Core game resources. This will ensure that the module is a stand alone module rather than an add-in to the single player campaign or some other module. For the purposes of this tutorial we will assume the module is called Backgrounds demo and I will prefix my resources with bdm_ to be sure that they are distinct from any that are already in existence.
We are going to completely replace the existing backgrounds with 3 new ones of our own. You can use new backgrounds in your module however you see fit. You could create entire origin stories for each one like in the single player campaign (possibly a major undertaking depending on length) or you can just use them to influence dialogue and plot options in the main part of your game.
For our demo we are creating a module in which the player starts off in a small human village (we will not be creating the details of this module so this is just for the purpose of an example). There is a wizard in this village who plays a pivotal role in the story. We allow the player to start as the wizard’s apprentice (if they are a mage) or as his servant (if they are human or elven and not a mage) or as a wandering traveller who has come to visit the wizard (if they are not human and not a mage). So the three backgrounds will be:
- Servant
- Apprentice
- Traveller
Creating the background strings
Before we start modifying the 2DA files we are going to create some strings. Open the string editor in the toolset and create the following strings in your ‘Backgrounds demo’ talk table:
Your string IDs might be different to these so be sure to reference the right string ID in the instructions that follow. Simply replace these string Ids with your own.
IMPORTANT! There is currently a bug in the game that causes it to crash if a background description ID has a value greater than 109 912 680. There is a community fix for this. Alternatively, you can go into your module’s properties and change the start ID for strings to be less than the critical value. After that the string editor will generate IDs in a range that won’t crash. Reset the start ID to its original value after you create the background descriptions (that way you have less to alter once Bioware fix this, and less risk of incompatibility with other mods).
You may have noticed that we have given two different names and descriptions for the traveller background depending on the PC’s race. We will say more about that in the following section.
- String editor |
Modifying the 2DAs
The next thing we need to do is to modify the worksheets in backgrounds.xls. Be sure to make a copy of the spreadsheet first so that you leave the original intact. See LINK for general information about editing 2DA files. The first worksheet is called ‘backgrounds’; modify it to look like the following:
Note that since 2DAs combine together to form M2DAs, and we don’t want the existing backgrounds in our game, we have overwritten the last two slots as unused1 and unused2, even though we won’t be using them. The name and desc string refs on this tab will be overwritten by the race specific ones on the names tab so we can leave those blank (AFAIK these string refs are not used providing the ones on the names tab are provided - also the tooltip string ref seems not to be used anywhere – if you prefer you can set these to something just to be safe).
The three race columns determine which backgrounds can be selected by which race and the 3 class columns determine which background can be selected by which class. So, as you can see from the picture our Servant background is accessible to elf or human warriors and rogues, the Apprentice background can be selected by elf or human mages and the Traveller background by elf or dwarf warriors and rogues.
The last three tabs indicate a starting ability for each background by race. Note that by default these columns are not hooked up into the character generation script (instead they are hard coded in the script) and are not used, but we will correct that later on when we do the scripting. You have to be careful here because the columns relate to the race IDs, so 1=dwarf, 2=elf and 3=human, which is in a different order to the other race columns in this table. The number in the column is an ability ID as given in ABI_base.xls. For this demo we have just given the same skill for each race for a given background. Servants get combat tactics, Apprentices get persuade and Travellers get survival.
The next tab table to modify is background_names.
Make sure that the IDs here match the IDs in your string table. Note that for Apprentices and Servants we use the same strings regardless of race, but for the Traveller background we use different strings. This is to illustrate how you can divide a single background up into multiple names and descriptions. Internally they are both stored as the same background ID but during character generation a different name, description and icon can be shown to the player. In the single player game this was done with the Noble background by splitting it into human and dwarf Noble variations.
NOTE – you must be careful when splitting a single background into different parts for each race. You must make both the names and descriptions different! If you don’t make the descriptions different then the engine will not recognise them as different even if they have different names.
Now modify the background_desc table as follows
Again note the distinct descriptions for the Traveller background.
Now for the background_icons table. For this demo we will just use some of the existing icons but you may want to create your own icons to add here.
The background_defaults table must have an entry for each race, background and class combination that is allowed. The ID column must be calculated as follows:
(1000 * raceID) + (100 * classID) + backgroundID
The label column should give a description of the row (AFAIK this is not used anywhere). For this simple demo we will have each background combination spawn in the same place (the module default start location) but if you wanted different origins to spawn in different places then you can use the next two columns to specify this (AFAIK you have to write your own scripts to accomplish this though).
In the template column you can put the name of a creature resource that you want to use as a template for this race/background/class combination. In the picture you can see that we have just used some default templates that shipped with the toolset but you can easily create your own. Note that only the inventory is copied across to the PC so if you want to create your own templates you do not need to alter anything on the creature except for their inventory.
The name columns take string Ids. If you want to add your own names then you’ll have to create new strings in the string editor.
Finally we have the ability column. As with the skill columns in the backgrounds table these IDs correspond to entries in the ABI_base.xls file. It should be noted that race, class and background are all possible sources of starting abilities so before you decide what to put in this column you should familiarise yourself with what skills are already being added in different parts of character generation in order to ensure that you don’t duplicate an ability somewhere.
The final table is the chargen_preload table. The two columns here should exactly match the entries from the ID and Template columns of the previous table.
- 2DA - 2DA#Excel file formatting - Background.xls |
Compiling and fixing the GDAs
Now compile backgrounds.xls and place the resulting GDAs into your AddIns\module_name\module\override directory. Never place anything in your AddIns\module_name\core\override directory or it will affect other modules than the one you are editing.
There used to be a bug that prevented large string IDs from compiling correctly. This is now fixed. For the record, the old workaround was to edit the GDA files in the toolset to correct the string IDs manually, but you should now see that the string IDs are correct.
- Compiling 2DAs - GDA |
Creating the backgrounds plot
The background plot is used for keeping track of a PC’s background and can be used to affect conversation options.
The single player game uses a plot called gen00pt_backgrounds to keep track of the PC background. You can find it in the \_Global\Generic folder of the plots tab if you want to take a look. We will create a similar plot for our custom backgrounds. Create a new plot and call it bdm_000pt_backgrounds (My naming convention is to use 000 to indicate a resource that isn’t tied to any particular area – feel free to use your own convention but remember to update any references later in the tutorial). Add main plot flags as indicated in the picture below:
You could also add some defined flags if you wanted. Defined flags can be used to return some combination of the main flags such as TRAVELLER or add some extra condition like FEMALE_APPRENTICE or ELVEN_APPRENTICE. Defined flags are beyond the scope of this tutorial however.
- Designer Resources - Plot |
Creating the scripts
We are not going to edit any of the original core game scripts, we will only be adding new scripts of our own. As we discuss how to do this you should examine the core scripts that are referred to, but under no circumstances should you make any changes to those files. By leaving the core scripts as they are we make our modification more compatible with any future patches that may be released. If you modified the core scripts and then a patch was released that overwrote those scripts you would lose your changes. Also keeping your modification contained within your own module allows you to make different modifications to different modules within the one toolset.
Module Script
Lets start off with a script to handle the character generation events that are normally handled by the module_core script. Create a new script called bdm_module_core. It is up to you what directory structure to use for your scripts but it can be a good idea to keep all your scripts in a directory with your module name to make them distinct from the core scripts. Then if you are emulating the functionality of a particular core script you can mirror the original directory structure underneath your main module folder. So in this case the module_core script is contained in the \_Core Scripts folder so we will put our bdm_module_core in \Backgrounds demo\_Core Scripts (the underscore ensures the core directories come first – looks like Bioware accidentally created one without it! I’m pretty sure Core and _Core are meant to be the same directory). Organising things this way will really help with keeping track of your scripts.
The bdm_module_core script should contain the following code:
#include "global_objects_h" void main() { event ev = GetCurrentEvent(); int nEvent = GetEventType(ev); //extract event type from current event int nEventHandled = FALSE; //keep track of whether the event has been handled switch(nEvent) { case EVENT_TYPE_MODULE_START: { // ----------------------------------------------------------------- // Initiate character generation. // ----------------------------------------------------------------- PreloadCharGen(); //preloads resources for character generation StartCharGen(GetHero(),0,TRUE); //initiates character generation break; } default: { // ----------------------------------------------------------------- // Handle character generation events sent by the engine. // ----------------------------------------------------------------- if ((nEvent >= EVENT_TYPE_CHARGEN_START && nEvent <= EVENT_TYPE_CHARGEN_END) || nEvent == EVENT_TYPE_PLAYERLEVELUP ) { HandleEvent(ev, R"bdm_sys_chargen.ncs"); nEventHandled = TRUE; } break; } } if(!nEventHandled) { // if this event wasn't handled by this script, let the core script try HandleEvent(ev, RESOURCE_SCRIPT_MODULE_CORE); } }
Now go to File > Manage modules and open the properties for your module and set the script property to point at this script.
Our new script handles some of the events that are usually handled by the module_core script. Once it has handled the event it chooses whether or not to pass the event on to module_core. The first event that we handle is EVENT_TYPE_MODULE_START, which is where we initiate character generation. In this case we pass the event on to the core script to handle after we have done what we need to.
NOTE: you may find that you don't need to change any other scripts. It's not clear what the scripts in the rest of this section are trying to do - see discussion.
2DA Constants
Before we create the next event script we are going to need some new constants defined. Create a new script called bdm_2da_constants_h and put it in folder \Backgrounds demo\_Core Includes. Now add the following to this script:
// ----------------------------------------------------------------------------- // Backgrounds - rules/backgrounds.xls // ----------------------------------------------------------------------------- const int BACKGROUND_SERVANT = 1; const int BACKGROUND_APPRENTICE = 2; const int BACKGROUND_TRAVELLER = 3;
These constants provide a way for us to refer to the background IDs that we defined in the 2DA files without having to remember the numbers. Constants like this also make your code more readable. If you need to add more new items to 2DAs when you are modding something else you can also add constants for them here. From now on we will be using these background constants instead of the original single player background constants that you can find in the 2da_constants_h script. That means we’d better find all the places where those constants are used and update them with our own scripts.
CharGen System
The second thing we handle is actually a group of events. If you look in the module_core script you will see that right down the bottom in the default event handler is some code that looks just like the code we have added in our default event, with a couple of little differences. Instead of passing the character generation events to the sys_chargen.ncs script we are going to pass it to our own script called bdm_sys_chargen.ncs which we will write shortly. If we catch one of those character generation events then we want to handle it ourselves and prevent the core script from handling it so we set nEventHandled = TRUE. Note that any other event that is caught by the default handler but isn’t a character generation event is going to go straight on through to the core script.
Create another new script and call it bdm_sys_chargen and put it in a folder called \Backgrounds demo\_Systems. Put the following code in it:
#include "log_h" #include "bdm_sys_chargen_h" const int CHARGEN_QUICKSTART_QUICK = 0; const int CHARGEN_QUICKSTART_NORMAL = 1; const int CHARGEN_QUICKSTART_ADVANCED = 2; void _RunChargen(int nRace, int nClass, object oChar, int nBackground) { Chargen_InitializeCharacter(oChar); Chargen_SelectGender(oChar,GENDER_MALE); Chargen_SelectRace(oChar,nRace); Chargen_SelectCoreClass(oChar,nClass); Chargen_SelectBackground(oChar, nBackground,FALSE); int nEquipIdx = Chargen_GetEquipIndex(nRace, nClass, nBackground); Chargen_InitInventory(oChar,0,nEquipIdx); Chargen_SpendAttributePoints(oChar,PROPERTY_ATTRIBUTE_STRENGTH, 3,FALSE); Chargen_SpendAttributePoints(oChar,PROPERTY_ATTRIBUTE_DEXTERITY, 2,FALSE); } void main(){ event ev = GetCurrentEvent(); int nEventType = GetEventType(ev); object oChar = GetEventObject(ev,0); int nMode; int nInt0 = GetEventInteger(ev,0); int nInt1 = GetEventInteger(ev,1); // ------------------------------------------------------------------------- // Debug Data. // ------------------------------------------------------------------------- Log_Trace(LOG_CHANNEL_EVENTS_CHARGEN,"bdm_sys_chargen","Chargen Event:" + Log_GetEventNameById (nEventType) + " " + ToString(nInt0) + "," + ToString(nInt1), oChar); int nEventHandled = FALSE; switch(nEventType) { // ---------------------------------------------------------------------- // This fires when the player selects the icon corresponding to any of // the available backgrounds // // nInt0 - Constant BACKGROUND_* integer // ---------------------------------------------------------------------- case EVENT_TYPE_CHARGEN_SELECT_BACKGROUND: { int nBackground = nInt0; // ----------------------------------------------------------------- // Set the background on the player and reinitialize plot flags // for the background // ----------------------------------------------------------------- Chargen_InitializeCharacter(oChar,TRUE); Chargen_SelectGender(oChar,GetCreatureGender(oChar)); Chargen_SelectRace(oChar,GetCreatureRacialType(oChar)); Chargen_SelectCoreClass(oChar,GetCreatureCoreClass(oChar)); bdm_Chargen_SelectBackground(oChar, nBackground, FALSE); bdm_Chargen_SetupPlotFlags(oChar); // ----------------------------------------------------------------- // Generate the index into the equipment template 2da and // then load the starting equipment based on the data returned. // ----------------------------------------------------------------- int nClass = GetCreatureCoreClass(oChar); int nRace = GetCreatureRacialType(oChar); int nEquipIdx = Chargen_GetEquipIndex(nRace, nClass, nBackground); Chargen_InitInventory(oChar,0,nEquipIdx); nEventHandled = TRUE; break; } case EVENT_TYPE_CHARGEN_END: { nMode = nInt0; int nQuickStart = nInt1; // 0 - quickstart // 1 - normal \ // 2 - advanced / treat as the same Log_Trace(LOG_CHANNEL_CHARACTER,"bdm_sys_chargen","MODE: " + IntToString(nMode) + ", Quick Start: " + IntToString(nQuickStart)); if(nMode == CHARGEN_MODE_CREATE && nQuickStart == CHARGEN_QUICKSTART_QUICK) { Log_Trace(LOG_CHANNEL_CHARACTER,"bdm_sys_chargen","Setting default values for player character"); int nRandClass = abs((GetLowResTimer() % 3) + 1); if(nRandClass == CLASS_ROGUE || nRandClass == CLASS_WARRIOR) { _RunChargen(RACE_HUMAN, nRandClass, oChar, BACKGROUND_SERVANT); WR_SetPlotFlag(PLT_BDM_000PT_BACKGROUNDS, BDM_GEN_BACK_SERVANT, TRUE); } else // mage { _RunChargen(RACE_HUMAN, nRandClass, oChar, BACKGROUND_APPRENTICE ); WR_SetPlotFlag(PLT_BDM_000PT_BACKGROUNDS, BDM_GEN_BACK_APPRENTICE, TRUE); } Chargen_SetNumTactics(oChar); SetCanLevelUp(oChar,Chargen_HasPointsToSpend(oChar)); SendEventModuleChargenDone("", ""); nEventHandled = TRUE; } break; } } //end switch if(!nEventHandled) { HandleEvent(ev, R"sys_chargen.ncs"); } }
There are two events in the original sys_chargen script that we need to replace because they deal with backgrounds. The first of these is EVENT_TYPE_CHARGEN_SELECT_BACKGROUND and the other is EVENT_TYPE_CHARGEN_END. For the second event we only need to change the case where the character is being created in quickstart mode, so we just handle that section of the code. The objective here is to change only as much as we need to and then pass any remaining events through to the original sys_chargen script. Take some time to compare the new script with Bioware's sys_chargen script .
You will notice that we include a script called bdm_sys_chargen_h and that we use some functions with a bdm_ prefix. Those functions are contained in that include file which we’ll write now. Essentially these are modifications of a couple of functions from the sys_chargen_h include file. Create a new script and call it bdm_sys_chargen_h. Put it in folder \Backgrounds demo\_Systems\Includes and add the following code:
#include "sys_chargen_h" #include "wrappers_h" #include "bdm_2da_constants_h" #include "plt_bdm_000pt_backgrounds" void bdm_Chargen_SelectBackground(object oChar, int nBackground, int bUnApply = FALSE) { Log_Chargen("bdm_Chargen_SelectBackground","-- " + (bUnApply?"Un":"") +"Selecting BG: " + ToString(nBackground),oChar); // ------------------------------------------------------------------------- // 1. Set the background variable // - Create creature property (or check what we used so far // - We don't set backgrounds on non player generated chars. // ------------------------------------------------------------------------- if(bUnApply) { SetCreatureProperty(oChar, PROPERTY_SIMPLE_BACKGROUND, 0.0, PROPERTY_VALUE_BASE); } else { SetCreatureProperty(oChar, PROPERTY_SIMPLE_BACKGROUND, IntToFloat(nBackground), PROPERTY_VALUE_BASE); } // ------------------------------------------------------------------------- // 2. Give one skill // - retrieve the skill that is granted by the background from backgrounds.xls // - give it to the player. // ------------------------------------------------------------------------- int nAbility = ChargenGetBackgroundSkill(GetCreatureRacialType(oChar), nBackground); if(nAbility) { _AddAbility (oChar, nAbility, bUnApply); } } void bdm_Chargen_SetupPlotFlags(object oChar) { int nRace = GetCreatureRacialType(oChar); int nBackground = GetPlayerBackground(oChar); Log_Trace(LOG_CHANNEL_CHARACTER,"bdm_sys_chargen_h","Setting plot flags, race: " + IntToString(nRace) + ", background: " + IntToString(nBackground)); // First, init all flags (debug setup) WR_SetPlotFlag(PLT_BDM_000PT_BACKGROUNDS, BDM_GEN_BACK_SERVANT,FALSE); WR_SetPlotFlag(PLT_BDM_000PT_BACKGROUNDS, BDM_GEN_BACK_APPRENTICE,FALSE); WR_SetPlotFlag(PLT_BDM_000PT_BACKGROUNDS, BDM_GEN_BACK_DWARF_TRAVELLER,FALSE); WR_SetPlotFlag(PLT_BDM_000PT_BACKGROUNDS, BDM_GEN_BACK_ELF_TRAVELLER,FALSE); switch (nBackground) { case BACKGROUND_SERVANT: { WR_SetPlotFlag(PLT_BDM_000PT_BACKGROUNDS, BDM_GEN_BACK_SERVANT,TRUE); break; break; } case BACKGROUND_APPRENTICE: { WR_SetPlotFlag(PLT_BDM_000PT_BACKGROUNDS, BDM_GEN_BACK_APPRENTICE,TRUE); break; break; } case BACKGROUND_TRAVELLER: { switch(nRace) { case RACE_DWARF: WR_SetPlotFlag(PLT_BDM_000PT_BACKGROUNDS, BDM_GEN_BACK_DWARF_TRAVELLER,TRUE); break; case RACE_ELF: WR_SetPlotFlag(PLT_BDM_000PT_BACKGROUNDS, BDM_GEN_BACK_ELF_TRAVELLER,TRUE); break; } break; } } }
Lets examine these two functions.
Comparing the first function, bdm_Chargen_SelectBackground, to its original version we have actually made a more generic version that should work with any backgrounds. We have removed the hard coded values for the background abilities and now use the ChargenGetBackgroundSkill function which actually gets the background skill from the backgrounds 2DA file that we edited earlier (recall the last three columns that give a skill depending on background and race). If you need fancier logic than just reading the values form the GDA (e.g. like Bioware’s logic for the Noble background) then you can always go back to hard coding them the way Bioware did.
The second function is bdm_Chargen_SetupPlotFlags which does just what it says. Remember the plot we created earlier? Here we set the appropriate flag based on race and background.
Starting Locations
In order for characters with different backgrounds to appear in different locations, you must do a little additional scripting.
First create a new script titled bdm_campaign_h and put the following code in it:
#include "wrappers_h" #include "utility_h" #include "log_h" #include "bdm_sys_chargen_h" void bdm_Campaign_ChargenDone(string sParam1, string sParam2); void bdm_Campaign_ChargenDone(string sParam1, string sParam2) { string sArea; string sWP; object oHero = GetHero(); Log_Trace(LOG_CHANNEL_SYSTEMS, "Campaign_ChargenDone", "START, param1: " + sParam1 + ", param2: " + sParam2); if(sParam1 != "" && sParam2 != "") { sArea = sParam1; sWP = sParam2; } else { int nRace = GetCreatureRacialType(oHero); int nClass = GetCreatureCoreClass(oHero); int nBackground = FloatToInt(GetCreatureProperty(oHero, PROPERTY_SIMPLE_BACKGROUND)); int nEquipIndex = Chargen_GetEquipIndex(nRace, nClass, nBackground); sArea = GetM2DAString(TABLE_STARTING_EQUIPMENT, "StartArea", nEquipIndex); sWP = GetM2DAString(TABLE_STARTING_EQUIPMENT, "StartWP", nEquipIndex); } UT_DoAreaTransition(sArea, sWP); }
Now add the following case statement into the bdm_module_core script you created earlier. The code must go above the default case statement:
case EVENT_TYPE_MODULE_CHARGEN_DONE: { Log_Trace(LOG_CHANNEL_SYSTEMS, "EVENT_TYPE_MODULE_CHARGEN_DONE", "START"); string sParam1 = GetEventString(ev, 0); string sParam2 = GetEventString(ev, 1); bdm_Campaign_ChargenDone(sParam1, sParam2); break; }
The module script will get the area and waypoint assigned to your character from the GDA, then send it to the bdm_Campaign_ChargenDone function, which in turn will transport your character in the right location.
- Script |
Testing that everything works
Now compile your scripts and export your module. Also, make sure that you export your talk table.
IMPORTANT! Always make sure you empty your \Dragon Age\packages\core\override folder after doing an export. Due to a bug in the current toolset your single player game will become damaged if you don’t do this before playing.
You should now be able to run your game and see the backgrounds you created appear in character generation. If you want to test that the background plots have been set correctly, add an NPC to the start area and create a conversation for them in which each line is switched according to a background plot flag. You can also check that the character is receiving the abilities that we set in the 2DAs.
NOTE There seems to be no way to change the popup message that comes up at the beginning of character generation. Altering the Pre-chargen string ID in the loadhints GDA does not seem to do anything.
- Exporting a module |
Optional extras
There are a couple of other Bioware scripts that reference the background constants. They are not essential to modify because they are not used anywhere by default. The genev_tutorial script uses a background check but unless you plan on firing the tutorial from your module then you won’t have need of it. Also there is at least one debug script that makes use of the original single player backgrounds (to grant a background plot flag to the PC) so if you want similar debug functionality you may have to roll your own function.
Race & Gender descriptions
If your module is not set in Ferelden, you may wish to change the descriptions of the Race and Gender buttons that appear during Character Creation.
The Race descriptions are in the Description column of the RACE_base 2DA, so you can replace them with new strings. As before, until the bug is fixed, use a string id in the 100000000 range.
Unfortunately, the gender description is hard-coded in the game as string id 377283. Fortunately, you can make a local version of this string, which only applies to your campaign.
Alternatively, if you an advanced user who is familar with the UI Tutorial, it's set in the GUI's Localization.as file as LOC_GENDER_DESCRIPTION = 377283, which is then used in the RaceGenderScene.as file. after editing the Localization.as file, you compile it and inject it into the appropriate .gfx'es.
Conclusion
In this tutorial we have demonstrated a way to modify the backgrounds used in the game without altering any of the core scripts. It is expected that a similar approach could be taken to modding other aspects of the game such as adding classes or races. In these cases the scripts that were created in this tutorial could be extended to handle other parts of the character generation process such as class and race selection.
Language: | English • русский |
---|