-
Notifications
You must be signed in to change notification settings - Fork 0
Development Process
geralt_skills.xml
, geralt_skills_ep1.xml
(Heart of Stone), and geralt_skills_ep2.xml
(Blood and Wine) are where Witcher 3 stores the definitions of Geralt's skills and abilities. You will provide your own custom skill's definition for Witcher 3 to parse in geralt_skills.xml
to allow your skill to be used by all gamers, not just ones with the DLC expansions.
Alternatively, you can also mount your own .xml through a custom DLC to completely avoid potential conflicts with other custom skill mods. This technique is more involved, but yields the best compatibility results. Unfortunately though, it also has the side effect of adding an extra step for your users during installation.
To get to the geralt_skills.xml
file you will need to first follow the official CDPR Mod Kit instructions on uncooking the game. There will be two geralt_skills.xml and you will need to modify both of them.
gameplay\abilities\geralt_skills.xml
gameplay\abilities_plus\geralt_skills.xml
Download: Witcher 3 v1.22 vanilla geralt_skills.xml
This .xml consists mainly of <ability>
and skill
tags. To create your own skill you will need to provide your own definition for both tags in order for The Witcher 3 to correctly parse your skill. In addition to the default attributes that The Witcher 3 uses, Custom Skills Framework requires several more attributes that you will need to define.
If you scroll through the .xml you will find all the <ability>
definitions of Geralt's skills placed inside a broader <abilities>
tag. Inside this section you will add your own <ability>
tag and choose your own name
for your skill. This name
is not your user-friendly name, but rather the CName
object for your skill. You will also need to provide a <tags>
that matches that name. Be sure to check the database for names that are already in use and choose a unique one.
<!-- modReflexBlast -->
<ability name="magic_s25">
<tags>magic_s25</tags>
</ability>
Inside <custom>
<skills>
<definitions def name="GeraltSkills">
you will find all the skill definitions for Geralt. Find the definition of a skill under the path you want to inject your skill into and use its definition as a template for your new one. Copy/paste and modify to fit your new skill
Attribute | Description |
---|---|
skill_name | This is the name of your skill. e.g. magic_s25 (Not the user-friendly name) |
requiredPointsSpent | How many points needed to be spent inside this path before unlocking the ability for Geralt to learn this skill. Determines the relative row the skill is placed under in-game for Sign skills. |
maxLevel | Number of upgrades to the skill |
priority | Determines which skill gets listed first when the requiredPointsSpent are the same |
guiPositionID | A referential tag for Witcher 3 to know where the skill is. Does not actually place the skill in the GUI position set here. The position count goes left to right, up to down. |
localisationName | Provide a unique key value if you plan to use W3Strings Encoder. Otherwise type in your skill name directly if you plan to use modCustomLocalizationFix
|
localisationDescriptionNotAcquired | Tooltip description of skill when not learned yet. |
localisationDescription | Tooltip description of skill when learned level 1. |
localisationDescriptionLevel2 | Tooltip description of skill when learned level 2. |
localisationDescriptionLevel3 | Tooltip description of skill when learned level 3. |
pathType_name | In-game tab of the Characters screen where the skill will be found on. Possible values: Alchemy , Sword , Signs , Perks
|
subpathType_name | Column which the skill will be placed under. Use the same ones as CDPR. |
iconPath | Path to your new icon. e.g. icons\Skills\Signs\magic_s25.png
|
In addition to the Witcher 3 attributes, it is critical you also define the following Custom Skills Framework attributes.
Attribute | Description |
---|---|
skill_type | Required attribute to let Custom Skills Framework know this is your skill. Set to custom
|
group_name | Required attribute links your custom skill to the correct XML group defined in your mod menu XML |
group_var | Required attribute that links to your enable/disable/uninstall option in your mod menu XML |
arg_int_var1 | Optional attribute that links your skill's tooltips dynamically to integer items in your mod menu XML options. Additional attributes include arg_int_var2 , arg_int_var3 , and arg_int_var4 . |
arg_float_var1 | Optional attribute that links your skill's tooltips dynamically to float items in your mod menu XML options. Additional attributes include arg_float_var2 , arg_float_var3 , and arg_float_var4 . |
<skill skill_name="magic_s25" skill_type="custom" group_name="ReflexBlast" group_var="refBlastEnabled" arg_int_var1="adrenCost" arg_int_var2="lvl2Boost" requiredPointsSpent="6" maxLevel="2" priority="20" guiPositionID="16" localisationName="skill_name_reflex_blast" localisationDescriptionNotAcquired="skill_desc_prev_reflex_blast" localisationDescription="skill_desc_reflex_blast" localisationDescriptionLevel2="skill_desc_reflex_blast_2" localisationDescriptionLevel3="skill_desc_reflex_blast_3" cost="1" pathType_name="Signs" subpathType_name="Signs_Aard" iconPath="icons\Skills\Signs\magic_s25.png" linkVertical="blue" linkLeft="blue" linkRight="blue" />
Earlier in geralt_skills.xml, you linked up your <skill iconPath>
attribute to a .png file. You will need to create a .png file for your icon and cook it into your mod with the mod kit.
The iconPath
path is linked to gui_new
. So for example, if your iconPath
is icons\Skills\Signs\magic_s25.png
that will be looking for a file in gui_new\icons\skills\signs
.
Property | Definition |
---|---|
Dimensions | 64 * 64px |
Background color | Transparent |
Foreground color |
#b5bece slate gray-esque |
Border color |
#000000 black |
Border width | 1px |
Once you have your modified geralt_skills.xml and icon .png created, check that they all exist in the following directory structure.
modMyMod-DevFolder\gameplay\abilities\geralt_skills.xml
modMyMod-DevFolder\gameplay\abilities_plus\geralt_skills.xml
-
modMyMod-DevFolder\gui_new\icons\skills\signs\iconName.png
(note: change to match your exact file location)
You will now need to cook and build your mod via the mod kit through the following commands:
wcc_lite cook -platform=pc -mod="%MODDEVDIR%" -basedir="%W3UNCOOKEDDIR%" -outdir="%COOKED%"
wcc_lite buildcache textures -basedir="%MODDEVDIR%" -platform=pc -db="%COOKED%\cook.db"
wcc_lite pack -dir="%MODDEVDIR%" -outdir="%COMPLETEDDIR%"
wcc_lite metadatastore -path="%COMPLETEDDIR%"
The mod menu XML is a robust file capable of creating customizable options for the user of your custom skill. It is also required to allow users to uninstall your skill from their skill trees.
You will create a new .xml file inside this directory
<Witcher 3 Install>\bin\config\r4game\user_config_matrix\pc
For examples of the structure of this file, open any of the .xml files existing in that folder already. The areas to pay special attention to when creating your own are the following:
XML Tag | Description |
---|---|
<Group id=""> |
It is critical to set this ID the same as the one you created in geralt_skills.xml
|
<VisibleVars> |
Inside here you will add <Var> tags for every customizable option you want |
<Var id=""> |
It is critical to create one of these for enabling/disabling your custom skill as defined in the group_var attribute in geralt_skills.xml . Additional tags will need to be created for each of your arg_int_var# and arg_float_var# in geralt_skills.xml
|
###group_var
The group_var
attribute needs to be made as an OptionsArray
. Custom Skills Framework has three modes for users of your custom skills
- 0 = Disabled. Users that want to leave your mod installed, but temporarily remove it from the game can set it to be disabled.
- 1 = Enabled. Users wanting to use your custom skill will need to set it to Enabled.
- 2 = Uninstall. Users wanting to remove your mod will need to run their gamesaves with uninstall and overwrite the save with your custom skill removed from their skill tree before deleting your mod.
<VisibleVars>
<Var id="refBlastEnabled" displayName="rb_enabled" displayType="OPTIONS" tags="customNames;customDisplayName">
<OptionsArray>
<Option id="0" displayName="rb_enabled_Disable">
<Entry varId="refBlastEnabled" value="0"/>
</Option>
<Option id="1" displayName="rb_enabled_Enable">
<Entry varId="refBlastEnabled" value="1"/>
</Option>
<Option id="2" displayName="rb_enabled_Uninstall">
<Entry varId="refBlastEnabled" value="2"/>
</Option>
</OptionsArray>
</Var>
...
...
...
</VisibleVars>
In order for Witcher 3 to understand your attributes in geralt_skills.xml
you will need to define them inside skillFrameworkStringDefinitions.ws
. Witcher 3 needs name
objects for each of the attribute items we created and unfortunately Custom Skills Framework has no programmatic way to create them automatically for you.
You will need to copy the file skillFrameworkStringDefinitions.ws
into modMyMod\content\scripts\game\
. Inside this file you will need to create cases inside the switch for each Custom Skills Framework attribute you used in geralt_skills.xml
.
function StringDefinitionToEnum( s : string ) : name
{
switch(s)
{
case "PhoenixRage" : return 'PhoenixRage'; // group_name
case "phxRageEnabled" : return 'phxRageEnabled'; // group_var for enable
case "maxHealthReq" : return 'maxHealthReq'; // arg_int_var1
case "adrenCost" : return 'adrenCost'; // arg_int_var2
}
}
The Witcher 3 relies on a specific sequence in their skill and enum definitions to create the placement of the skills. You must adhere to these rules in order for your skill to be detected and processed correctly by Witcher 3 and Custom Skills Framework.
Skill Category | Placement |
---|---|
Sword | After S_Sword_s21 , before S_Magic_s01
|
Signs | After S_Magic_s20 , before S_UNUSED2
|
Alchemy | After S_Alchemy_s20 , before S_Skill_MAX
|
Perks | After S_Perk_22 , before S_Perk_MAX
|
In addition to an enum definition, you will also need to add your custom skill to two switch-cases.
Replace ##
with an unused ID by CDPR or other custom skill mods. Please see the database for known IDs already in use.
Skill Category | Case (must use single quotes) | Return |
---|---|---|
Sword | case 'sword_s##' |
return S_Sword_s## |
Signs | case 'magic_s##' |
return S_Magic_s## |
Alchemy | case 'alchemy_s##' |
return S_Alchemy_s## |
Perks | case 'perk_##' |
return S_Perk_## |
Replace ##
with an unused ID by CDPR or other custom skill mods. Please see the database for known IDs already in use.
Skill Category | Case | Return (must use single quotes) |
---|---|---|
Sword | case S_Sword_s## |
return 'sword_s##' |
Signs | case S_Magic_s## |
return 'magic_s##' |
Alchemy | case S_Alchemy_s## |
return 'alchemy_s##' |
Perks | case S_Perk_## |
return 'perk_##' |
If your custom skill is a skill that needs the player to manually activate it, you might find yourself needing to create custom key bindings that your users will need to install. Custom Skills Framework does not provide any piping that helps you create these so you are free to create them however you want. For custom signs skills I recommend either creating a second alternate cast via modifier key or create a double-tap key binding.
You will want to register two listeners in order to create a second alternate cast mode. The first listener should be mapped to the same key as your cast sign (Key: Q by default). The second will be the modifier key like Shift or Ctrl.
class PhxRage
{
public function SetActionForKeyBinding()
{
theInput.RegisterListener( this, 'OnPressButton', 'PhoenixRage' );
theInput.RegisterListener( this, 'OnPressButton', 'PhoenixRageModifier' );
}
event OnPressButton (action : SInputAction)
{
var stamCost : float; // Stamina Cost for Skill
stamCost = thePlayer.GetStatMax(BCS_Stamina) * (StringToFloat(theGame.GetInGameConfigWrapper().GetVarValue('PhoenixRage', 'stamCost')) / 100);
if (theInput.IsActionPressed('PhoenixRageModifier'))
{
if (action.aName == 'PhoenixRage')
{
thePlayer.BlockAction(EIAB_Signs, SkillEnumToName(S_Magic_s26));
if (IsPressed (action)
&& thePlayer.GetEquippedSign() == ST_Igni
&& thePlayer.CanUseSkill(S_Magic_s26)
&& thePlayer.GetStat(BCS_Stamina) >= stamCost)
{
thePlayer.PhoenixRage(); // Trigger skill logic
thePlayer.DrainStamina(ESAT_FixedValue, stamCost, 0, SkillEnumToName(S_Magic_s26));
}
else if (IsReleased (action))
thePlayer.UnblockAction(EIAB_Signs, SkillEnumToName(S_Magic_s26));
}
}
}
}
Here is another key binding you may consider using for your skills. This one is more experimental and has not been heavily tested by me.
class PhxRage
{
public function SetActionForKeyBinding()
{
theInput.RegisterListener( this, 'OnPressButton', 'PhoenixRage' );
}
private var pressTimestamp : float;
private var pressTimestamp2 : float;
event OnPressButton (action : SInputAction)
{
var stamCost : float; // Stamina Cost for Skill
stamCost = thePlayer.GetStatMax(BCS_Stamina) * (StringToFloat(theGame.GetInGameConfigWrapper().GetVarValue('PhoenixRage', 'stamCost')) / 100);
if (action.aName == 'PhoenixRage')
{
if (IsPressed (action))
{
if (pressTimestamp == 0)
pressTimestamp = theGame.GetEngineTimeAsSeconds();
else
pressTimestamp2 = theGame.GetEngineTimeAsSeconds();
}
else if (IsReleased(action) && pressTimestamp != 0 && pressTimestamp2 != 0)
{
if (pressTimestamp >= pressTimestamp2 - 0.5)
{
if (thePlayer.GetEquippedSign() == ST_Igni
&& thePlayer.CanUseSkill(S_Magic_s26)
&& thePlayer.GetStat(BCS_Stamina) >= stamCost)
{
thePlayer.PhoenixRage(); // Trigger skill logic
thePlayer.DrainStamina(ESAT_FixedValue, stamCost, 0, SkillEnumToName(S_Magic_s26));
}
}
pressTimestamp = 0;
pressTimestamp2 = 0;
}
}
}
}
Congratulations! You have completed connecting your custom skill to the Custom Skills Framework. All that is left now is for you to create exciting new skills for Geralt :)
For complete examples of using this framework, please see my Reflex Blast and Phoenix Rage custom skills.