Skip to content

Development Process

cvax edited this page Jul 9, 2016 · 9 revisions

Step 1. geralt_skills.xml

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.

Uncooked XML location

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

Adding new custom skill

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.

<ability>

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.

Ex: "magic_s25" is being used for the custom skill Reflex Blast
<!-- modReflexBlast -->
<ability name="magic_s25">
	<tags>magic_s25</tags>
</ability>

<skill>###

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.
Ex: "magic_s25" is being used for the custom skill Reflex Blast
<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" />

Step 2. Custom icons

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.

Format for Icon

Property Definition
Dimensions 64 * 64px
Background color Transparent
Foreground color #b5bece slate gray-esque
Border color #000000 black
Border width 1px

Step 3. Cooking with Mod Kit

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%"

Step 4. Mod menu XML

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.
Ex.
<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>

Step 5. String Definitions

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.

Ex: switch-case for Phoenix Rage
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
	}
}

Step 6. playerTypes.ws

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.

enum ESkill

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.

function SkillNameToEnum(n : name) : ESkill

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_##

function SkillEnumToName(s : ESkill) : name

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_##'

Step 7. Key bindings

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.

Second Alternate Cast Mode via Modifier Key

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));
			}
		}
	}
}

Double-Tap Cast Mode

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;
			}
		}
	}
}

Step 8. Program custom skill logic

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.