-
Notifications
You must be signed in to change notification settings - Fork 8
YAML Tweaks
TweakXL supports YAML based format to define TweakDB data.
Tweak files must have a .yaml
or .yml
extension,
be placed in the <Cyberpunk 2077>/r6/tweaks
directory,
and can be organized using subdirectories.
At the top level, a tweak file defines flats and records.
# Flats
FreeCameraParams.baseSpeed: 10.0
timeSystem.netrunnerBreach.easeInCurve: DiveEaseIn
# Record
Items.Preset_Katana_Default:
quality: Quality.Random
mass: 20.0
Flats can be scalars, structures or arrays.
Assigning the value to flat is trivial, you just need its name and a compatible value:
PreventionSystem.setup.totalEntitiesLimit: 40
Using a name that doesn't exist, you can create new flats:
MySystem.triggerState: n"Combat"
MySystem.probabilities: [ 0.2, 0.5, 0.3 ]
You must use an explicit value format to specify the type of the new flat.
To change the properties of a record, you need to use record name to refer it, and assign values to the individual properties:
Vehicle.v_standard2_archer_bandit_player:
trafficSuspension: Vehicle.TrafficSuspension_Sport
enableDestruction: false
To change a single property of a record you can use a full property name:
Items.ReversePowerInductor.mass: 1.5
To create a record you need to choose a non-existent name and use $type
attribute
to specify the type name:
Items.MyClothingItem:
$type: Clothing
entityName: my_item
appearanceName: my_item_
displayName: My-Item-Name
Unspecified properties will be filled with default values.
The type name can be in any of the following forms:
Form | Value | Comment |
---|---|---|
Base name | Clothing |
Used in WolvenKit and REDmod. |
Script name | Clothing_Record |
Used in redscript. |
Native name | gamedataClothing_Record |
Used in CET and RED4ext. |
For complex records, such as items or vehicles with hundreds of properties and relations, it can be very difficult to set up a record from scratch. In this case, it's more convenient to clone an already existing record.
To clone a record, you must use $base
attribute to specify the name of the source record:
Items.MyClothingItem:
$base: Items.GenericFaceClothing
entityName: my_item
appearanceName: my_item_
displayName: My-Item-Name
One way to work with related records is to define all the records separately and use record names to define relationships:
# Create UI data
Vehicle.v_my_archer_quartz_data:
$type: VehicleUIData
driveLayout: LocKey#45371
horsepower: 250.0
info: LocKey#53579
mass: 1070.0
productionYear: 2041-2077
# Create vehicle and associate UI data
Vehicle.v_my_archer_quartz:
$base: Vehicle.v_standard2_archer_quartz_player
vehicleUIData: Vehicle.v_my_archer_quartz_data
With inline syntax, you can combine both definitions into one:
Vehicle.v_my_archer_quartz:
$base: Vehicle.v_standard2_archer_quartz_player
vehicleUIData:
driveLayout: LocKey#45371
horsepower: 250.0
info: LocKey#53579
mass: 1070.0
productionYear: 2041-2077
You can also use $base
for inline records:
Vehicle.v_my_archer_quartz:
$base: Vehicle.v_standard2_archer_quartz_player
vehicleUIData:
$base: Vehicle.v_standard2_archer_quartz_gt_inline0
horsepower: 250.0
Usually you don't need to specify the type of the inline record with $type
,
because it's already known from the property type of the parent record.
But you have to use it for polymorphic relationships:
Items.MyWeapon:
$type: WeaponItem
statModifiers:
- $type: ConstantStatModifier
statType: BaseStats.BaseDamage
value: 50.0
- $type: RandomStatModifier
statType: BaseStats.BaseDamage
min: 0.0
max: 10.0
Most types allow multiple formats for defining values, which can be divided into explicit and implicit formats.
The explicit format unambiguously defines the type of a value. It may be used in any context, and must be used in contexts where the expected value type cannot be determined.
This is an example of defining a flat of CName
type using the explicit format:
Example.variable: CName("Value")
The result of this statement depends on what Example.variable
is:
- If it exists and is of
CName
type, then new value will be assigned. - If it exists and is of different type, an error will be thrown that value is not compatible.
- If it doesn't exist, then new flat of type
CName
will be created.
The implicit format doesn't imply a value type. It can only be used when the expected type is known.
This is a similar example using the implicit format:
Example.variable: Value
This statement also depends on what Example.variable
is but the result is different:
- If it exists and is of
CName
type, thenValue
will be treated as a nameCName("Value")
. - If it exists and is of
String
type, thenValue
will be treated as a string"Value"
. - If it doesn't exist, an error will be thrown that
Value
is ambiguous and the type cannot be resolved.
The benefit of the implicit format is simplicity. It's easier to read and edit when working with tweak files by hand.
Record properties are always implicit
When working with records you can always use the implicit format for properties, because all properties are predefined and the type of any property is known.
Native Type: Int32
Value | Mode | Comment |
---|---|---|
220 |
Explicit | Positive decimal integer. |
-1 |
Explicit | Negative decimal integer. |
0xC0DE |
Explicit | Hexadecimal integer. |
Native Type: Float
Value | Mode | Comment |
---|---|---|
50.0 |
Explicit | Positive float. |
-7.25 |
Explicit | Negative float. |
123 |
Implicit | No decimal point: can be a float or an integer depending on the context. |
Native Type: Bool
Value | Mode |
---|---|
true |
Explicit |
false |
Explicit |
Native Type: String
Value | Mode | Comment |
---|---|---|
"This is a string" |
Explicit | Regular string. |
"Quoted \\ backslash" |
Explicit | Quoted strings require escaping. |
This can be a string |
Implicit | Unquoted text can be a string depending on the context. |
Unquoted \ backslash |
Implicit | Unquoted strings don't support escape sequences. |
Native Type: CName
Value | Mode | Comment |
---|---|---|
n"Thing" |
Explicit | Redscript style. |
CName("Thing") |
Explicit | CET style. |
None |
Explicit | Special value to define empty name. |
Thing |
Implicit | Unquoted text can be a name depending on the context. |
Native Type: gamedataLocKeyWrapper
Value | Mode | Comment |
---|---|---|
l"Secondary-Loc-Key" |
Explicit | Redscript style (not supported by redscript). |
LocKey("Secondary-Loc-Key") |
Explicit | CET style using secondary key. |
LocKey(12345) |
Explicit | CET style using primary key. |
LocKey#Secondary-Loc-Key |
Explicit | REDengine style using secondary key. |
LocKey#12345 |
Explicit | REDengine style using primary key. |
Secondary-Loc-Key |
Implicit | Unquoted text can be a key depending on the context. |
12345 |
Implicit | Unquoted integer can be a key depending on the context. |
Native Type: raRef:CResource
Value | Mode | Comment |
---|---|---|
r"path\to\resource.ext" |
Explicit | Redscript style. |
ResRef("path\to\resource.ext") |
Explicit | CET style using path. |
ResRef(123456789) |
Explicit | CET style using hash. |
path\to\resource.ext |
Implicit | Unquoted text can be a path depending on the context. |
123456789 |
Implicit | Unquoted integer can be a hash depending on the context. |
Resource paths
The path can use backslashes\
, double backslashes\\
, or slashes/
.
For example, all of these paths are valid and equal:
path\to\resource.ext
path\\to\\resource.ext
path/to/resource.ext
Native Type: TweakDBID
Value | Mode | Comment |
---|---|---|
t"Package.Item" |
Explicit | Redscript style. |
TweakDBID("Package.Item") |
Explicit | CET style. |
<TDBID:12AB56CD:1F> |
Explicit | Debug style using hash value. |
None |
Explicit | Special value to define empty ID. |
Package.Item |
Implicit | Unquoted text can be an ID depending on the context. |
Structures can be defined in one line using curly braces:
Example.struct: { foo: 1, bar: 2 }
Or multiline:
Example.struct:
foo: 1
bar: 2
In an implicit mode, when the type is known, you can omit selected or all fields. Omitted fields will have default values.
Native Type: Quaternion
Field Type: Float
Value | Mode | Comment |
---|---|---|
{ i: 0.0, j: 0.0, k: -0.9, r: 0.4 } |
Explicit | Allowed in any context. |
{ k: 0.1, r: 0.2 } |
Implicit | Allowed when the value type is known. |
Native Type: EulerAngles
Field Type: Float
Value | Mode | Comment |
---|---|---|
{ roll: 30.0, pitch: 90.0, yaw: 0.0 } |
Explicit | Allowed in any context. |
{ yaw: 150.0 } |
Implicit | Allowed when the value type is known. |
Native Type: Vector3
Field Type: Float
Value | Mode | Comment |
---|---|---|
{ x: 0.5, y: -0.8, z: 0.0 } |
Explicit | Allowed in any context. |
{ z: 2.0 } |
Implicit | Allowed when the value type is known.. |
Native Type: Vector2
Field Type: Float
Value | Mode | Comment |
---|---|---|
{ x: 20.0, y: 40.0 } |
Explicit | Allowed in any context. |
{ x: 250.0 } |
Implicit | Allowed when the value type is known. |
Native Type: Color
Field Type: Uint8
Value | Mode | Comment |
---|---|---|
{ red: 51, green: 102, blue: 0, alpha: 255 } |
Explicit | Allowed in any context. |
{ red: 255, alpha: 127 } |
Implicit | Allowed when the value type is known. |
Arrays can be defined in one line using brackets:
Example.array: [ First, Second, Third ]
Or multiline:
Example.array:
- First
- Second
- Third
Array can be of any scalar or structure type. Arrays are homogeneous, and the element type is defined by the first element.
Value | Mode | Comment |
---|---|---|
[ 1.0, 2.0, 3.0 ] |
Explicit | Array of floats. |
[ { x: 0.5, y: 0.5 } ] |
Explicit | Array of Vector2 structures. |
[ n"First", n"Second" ] |
Explicit | Array of names. |
[ First, Second, Third ] |
Implicit | Can be array of strings, names, lockeys depending on the context. |
[] |
Implicit | An empty array with the actual type determined from the context. |
Arrays can be mutated instead of replaced.
Syntax: !append {value}
!prepend {value}
Adds an element to the end or beginning of target array.
Vehicle.vehicle_list.list:
- !prepend Vehicle.v_012_corpo_suv
- !append Vehicle.v_standard25_villefort_columbus_corpo
Syntax: !append-once {value}
!prepend-once {value}
Adds an element to the end or beginning of target array if it doesn't exist.
Vehicle.vehicle_list.list:
- !append-once Vehicle.v_standard2_villefort_cortes_police
Syntax: !append-from {array}
!prepend-from {array}
Add all elements from another array to the end or beginning of target array, discarding duplicates. The source array can be any flat or record property.
Items.RiflePossibleScopesList.itemPartList:
- !append-from Items.SniperPossibleScopesList.itemPartList
Syntax: !remove {value}
Remove an element from target array.
Vehicle.vehicle_list.list:
- !remove Vehicle.v_sport1_herrera_outlaw_player
By combining removals and additions, you can effectively replace values:
Items.w_silencer_01:
statModifiers:
# Remove original modifier for StealthHitDamageMultiplier
- !remove Items.w_silencer_01_inline0
# Add new modifier with desired values
- !append
$type: ConstantStatModifier
statType: BaseStats.StealthHitDamageMultiplier
modifierType: Additive
value: 3.0
The new record inherits properties with already filled arrays. Array operations can be used to modify inherited arrays. These modifications can also be inherited:
# Get stats from base ashura with extra iconic modifer
Items.Weapon_A:
$base: Items.Base_Ashura
statModifiers:
- !append Quality.IconicItem
# Get stats from Weapon_A with extra recovery modifer
Items.Weapon_B:
$base: Items.Weapon_A
statModifiers:
- !append
$type: ConstantStatModifier
statType: BaseStats.RecoilRecoveryTime
modifierType: Additive
value: -0.15
# Get stats from Weapon_B but without iconic modifier from Weapon_A
Items.Weapon_C:
$base: Items.Weapon_B
statModifiers:
- !remove Quality.IconicItem
You can use anchors to repeat some definitions without the need for copy-paste:
# Define item addition for one vendor
Vendors.wat_lch_clothingshop_01: &AddToVendor
itemStock:
- !append
item: Items.MyClothing
quantity: [ Vendors.IsPresent ]
# Repeat for other vendors
Vendors.wat_kab_clothingshop_01: *AddToVendor
Vendors.wat_nid_clothingshop_01: *AddToVendor
Vendors.wbr_jpn_clothingshop_01: *AddToVendor
To repeat one definition multiple times you must set $instances
attribute to an array of data for each copy
and use placeholders for data substitutions:
Items.my_item_$(color):
$instances:
- { color: black, icon: slot_1 }
- { color: white, icon: slot_2 }
- { color: red, icon: slot_3 }
$base: Items.GenericHeadClothing
entityName: my_item
appearanceName: my_item!$(color)
displayName: my_item_$(color)_name
icon:
atlasResourcePath: mod\icons\my_item.inkatlas
atlasPartName: $(icon)
Templates can be used only at the top level of tweak file.
While using a specialized type for one half of the data,
the game also uses plain strings like LocKey#1234
for the other half.
Originally you have to keep in mind the type of property to select the appropriate format,
and can only use numeric primary keys, which are very difficult to maintain and error prone.
TweakXL allows you to use any localization key explicit format regardless of property type.
Item records use an iconPath
string property to refer to the icon.
This is an example of how icon must be defined originally:
# Icon must have UIIcon. prefix
UIIcon.MyItemIcon:
atlasResourcePath: mod\icons.inkatlas
atlasPartName: icon_my_item
Items.MyItem:
# Must use only the part after UIIcon. prefix
iconPath: MyItemIcon
TweakXL simplifies this using inline syntax:
Items.MyItem:
icon: # This property is not used by the game originally
atlasResourcePath: mod\icons.inkatlas
atlasPartName: icon_my_item
TweakXL will create a UIIcon
record, generate a correct name, and fill iconPath
property.
So you don't have to define an extra record for each item and maintain an extra set of names.