肖像模组制作

本页面所适用的版本可能已经过时,最后更新于2.8



The character screen

Portraits are built by superposing layers of basic elements: background, generic head, facial elements (cheeks, hair, nose, ...), clothes, headgear, etc.

Each of these basic elements come with multiple variations or frames.

The choice of frame is either based on:

  • the character DNA,
  • or properties of the character (i.e triggers)

Children are an exception as they are created using a single image, hence children look similar.

Characteristics

There are two kind of characteristics: DNA and properties. Both are strings of letters (and possibly zeros for properties). They can be seen in-game by using console command charinfo.

DNA

DNA is a string of 11 letters inherited from the parents and doesn't change after birth.

For historical characters, their DNA string can be set in character history:

125501 = {
	name="Temujin" # AKA: Genghis Khan
	dynasty=11100
	dna="bfimkolbecc"
	(...)
}

Otherwise randomly generated characters will get random DNA strings, which are persisted in the game saves.

Index Used for
d0 Neck
d1 Chin
d2 Mouth
d3 Nose
d4 Cheeks
d5 Unused
d6 Eyes
d7 Ears
d8 Hair Color
d9 Eye Color
d10 Unused

So Genghis Khan's dna = "bfimkolbecc" means:

  • Frame b for the neck (2nd frame in mongol_male_neck_1.dds)
  • Frame f for the chin (6th frame in mongol_male_chin_1.dds)
  • Frame i for the mouth (9th frame in mongol_male_mouth_1.dds)
  • Frame m for the nose (13th frame in mongol_male_nose_1.dds)
  • etc...

In case there are less frames than the index of the letter in alphabet, it wraps around. For instance, for chins there are usually 4 frames, so a = 1st frame and e = 1st frame as well.

Properties

Properties are a string of up to 14 letters (in vanilla). If the string is shorter, zeros will be added at the end.

Only hair (p1) and beards (p4) can be changed in-game via the Customization Pack, the other property values are derived from portrait_properties.txt file.

  • p0: Background
  • p1: Hair
  • p2: Base Head
  • p3: Clothes
  • p4: Beard
  • p5: Headgear
  • p6: Imprisoned
  • p7: Scars tier I
  • p8: Red Dots
  • p9: Boils
  • p10: Blinded
  • p11: Player
  • p12: Mask
  • p13: Eyepatch
  • p14: Chinese makeup
  • p15: Chinese makeup 2
  • p16: Chinese empress jewelry
  • p17: Immortality
  • p18: Special crowns behind
  • p19: Special crowns
  • p20: Freckles
  • p21: Physique
  • p22: Pale
  • p23: Black eye
  • p24: Harelip
  • p25: Scars tier II
  • p26: Scars tier III
  • p27: Blood splatter
  • p28: Tattoos
  • p29: Warpaint
  • p30: Demon Child
  • p31: Frankish culture male overlayer
  • p32: Frankish culture female overlayer
  • p33: Frankish culture female underlayer and hair

It is possible to add new properties with the following:

  • Define a sprite in a .gfx file, for instance GFX_custom
spriteType = {
	name = "GFX_custom"
	texturefile = "gfx\\characters\\shared\\new_property.dds"
	noOfFrames = 2
	norefcount = yes
	can_be_lowres = yes
}
  • Configure p18 in portrait_properties.txt (the number of properties is determined dynamically from there):
# p18
18 = {
	0 = {
		factor = 50
	}
	1 = {
		factor = 50
	}
}
  • Add a layer in portraits.gfx: "GFX_custom:p18"

Rendering

Based on player's experimentations, here is a description of how the rendering of portraits works in-game. Note that part of the logic has also been re-implemented in Portrait Builder tool, for an out-of-game rendering.

Children

For child portraits:

  1. Look directly for a spriteType entry in /interface/portraits/* with name PORTRAIT_<culturegfx>_child_<sex>, where:
    • <culturegfx> is the graphical_culture defined for the character culture or culture group
    • <sex> is either male or female

Feudal

The game renders feudal adult portraits as follows:

  1. Look for a portraitType entry in /interface/portraits/portrait.gfx (or any other portraitType under /interface/*) with name PORTRAIT_<culturegfx>_<sex><_period><age>, where:
    • <culturegfx> is the graphical_culture defined for the character culture or culture group (common/cultures/00_cultures.txt)
    • <sex> is either male or female
    • <age> is an integer: 0 for young (16 <= age < 30), 1 for middle age (30 <= age < 50), 2 for old age (age >= 50)
    • <_period> is either _early if year is before 950, _late if year is after 1250, or empty otherwise (note: actual dates are defines EARLY_PORTRAIT_AND_UNIT_BEFORE_YEAR and LATE_PORTRAIT_AND_UNIT_AFTER_YEAR)
  2. In case no portraitType entry matches, try some defaulting: no date, no age, fallback ethnicities in the graphical_cultures list, ethnicity from parent culture group, or westerngfx in last resort.
  3. If a portraitType entry matches, apply the layers in order. For each layer defined as GFX_TYPE:[d|p]INDEX:
    1. Look for GFX_TYPE in spriteTypes (/interface/portraits/portrait_sprites.gfx or any other spriteType under /interface/*) and read the number of frames N.
    2. Resolve the frame to use:
      • If letter is d, look for the letter in character DNA at INDEX position, and convert the letter to a number (modulo N)
      • If letter is p, resolve the property for value INDEX, defined in /interface/portrait_properties.txt. The choice is made based on a weighted random, but the first factor that evaluates to >= 100 will be automatically chosen.
  4. Define a skin tone for the ethnicity in common/graphicalculturetypes/graphicalculturetype.txt, so that it can "manage" mixed-race children.

Theocracy

The rendering is the same as feudal characters, except for headgear and clothing layers:

  1. Look for character religion definition and read the frame index directly there (portrait_properties.txt is not used):
    • religious_clothing_head: index of the frame to use for the religious head
    • religious_clothing_priest: index of the frame to use for other priests
  2. Use frame at index inside hardcoded sprites GFX_religious_male_clothes, GFX_religious_male_headgear, etc. instead of the sprites defined in portrait.gfx

Republic

The rendering is the same as feudal characters, except for headgear and clothing layers. It uses hardcoded sprites GFX_merchant_male_clothes, GFX_merchant_male_headgear, etc. instead of the sprites defined in portrait.gfx


Clothing override

With patch 2.7 a new mechanism was added to override the clothing layers based on rules.

The only examples in vanilla are from \interface\portraits\society_clothes.gfx in dlc070.zip

The general structure is the following:

portraitType = {
	name = "PORTRAIT_<override_type>_clothing_male"

	weight = {
		additive_modifier = {
			value = 10000
			# Conditions (in portrait scope) for modifier to apply
		}
	}

	layer = { # GFX_empty can be used to disable a layer
		"GFX_xxx_male_clothing_behind:c0"
		"GFX_xxx_male_headgear_behind:c1"
		"GFX_xxx_male_clothing_front:c2"
		"GFX_xxx_male_headgear_mid:c3"
		"GFX_xxx_male_clothing_infront:c4"
		"GFX_xxx_male_headgear_front:c5"
	}

	allow_property_values = {
		<property_index> = {
			<frame_index> = {
				# Conditions (in portrait scope) for frame to be used
			}
		}
	}
}

Notes:

  • c0 to c7 are the "culture indexes" that reference clothing layers (normally driven by properties p3 and p5) to be overridden from the base portraitType:
    • c0 = clothes behind
    • c1 = headgear behind
    • c2 = clothes
    • c3 = headgear mid
    • c4 = clothes infront
    • c5 = headgear
    • c6 = headgear behind hairlayer
    • c7 = headgear hairlayer

Note: extra culture index cannot be added dynamically by modders.

  • allow_property_values block can be used in a similar way to portrait_properties.txt to select the frames for p3 (clothes) and p5 (headgear) in the overridden sprites of the layer block.

Portrait scope

A new scope is used, which is a sort of limited version of a character scope, with conditions prefixed with portrait_.

Condition Type Used in vanilla Description Example
portrait_age child/oldage Age of character compared to a portrait threshold (child, young, middle or old). As the thresholds are configurable, the numeric values are not directly hardcoded here. portrait_age > child

portrait_age < oldage

portrait_is_female bool Whether character is a female portrait_is_female = yes
portrait_has_trait trait Whether character has the trait portrait_has_trait = vaishnavist_hindu
portrait_religion religion X [Seen in the .exe - to be tested]
portrait_culture culture Character culture portrait_culture = greek
portrait_culture_group culture_group portrait_culture_group = latin
portrait_gfx_culture ? X [Seen in the .exe - to be tested]
portrait_government government X Whether character has the specified government. Seems to only work on landed characters.
portrait_tier tier portrait_tier = king
portrait_title_tier ? X [Seen in the .exe - to be tested]
portrait_society society portrait_society = monastic_order_benedictine
portrait_society_rank int Character rank if member of a society portrait_society_rank = 1
portrait_clothing bool Warning: portrait_clothing = no appears to be broken. portrait_clothing = yes
portrait_offmap offmap_power Whether the character rules the given offmap. portrait_offmap = offmap_china
portrait_is_patrician bool X Whether the character is in a merchant republic and their employer patrician or has a republic government form. portrait_is_patrician = yes
portrait_in_command bool X Whether the character is in command and leading any troops. portrait_in_command = yes

Custom ethnicities

It is possible to create new ethnicities by creating new assets and/or mixing existing vanilla or DLC assets in portraitTypes layers.

For instance a customgfx ethnicity with norsegfx faces and byzantinegfx clothes.

New assets

主条目:Graphics modding

If creating image sprites from scratch, you need to:

  • In folder gfx/characters/ create 2 folders (gothic_male & gothic_female) to store the sprites (ex: gothic_male_clothes.dds with 12 frames)
  • In folder interface/ create a file portraits_gothic_spritetypes.gfx, with one entry per image sprite:
spriteTypes = {
  spriteType = {
    name = "GFX_gothic_male_clothes"
    texturefile = "gfx\\characters\\gothic_male\\gothic_male_clothes.dds"
    noOfFrames = 12
    norefcount = yes
    can_be_lowres = yes
  }
  ...
}

DLC assets

Reusing DLC assets is easier than creating new one, but because the DLC assets cannot themselves be bundled into the mod,[2] users who don't own the DLC(s) containing these assets (ex: the Norse Portraits DLC) will see gothicgfx portraits with clothes but no heads.

The reason is that, as soon as some portraitTypes entry for gothicgfx are defined, the game will render them, even if the layers refer to unknown assets and will not show up properly The fallback to another ethnicity in graphical_cultures list (or default westerngfx) only happens if no portraitType entry is found at all !

There are some solutions to this problem:

  1. Bundling the portraitTypes definitions for that ethnicity outside the main mod, in a sub-mod. This way players:
    • can activate the sub-mod ONLY if they have the required DLC(s).
    • will see the fallback ethnicities if they don't have the required DLC(s)
  2. Providing fallback portraitTypes for that ethnicities that do not use DLC assets. Players have to download those extra fallback portraitTypes and use the ones for which they don't have the required DLC.

New ethnicity

To define a new gothicgfx ethnicity, here are the steps:

  • In main mod folder common/graphicalculturetypes/ add a new .txt file with the line:
gothicgfx = {
  skin_tone = 0
}
  • In main mod common/cultures/00_cultures.txt file, modify or add a new gothic culture with the following lines
graphical_cultures = { gothicgfx occitangfx }

Note: if using sub-mods, it is important that the last culturegfx in the list be one of the vanilla base types (occitangfx, muslimgfx, byzantinegfx, westerngfx). In case it is westerngfx, it can be omitted as it is the default, or used as 3 layers of ethnicities. If using fallback portraitTypes, this is not required.

  • In sub-mod (or main mod, depending on the solution chosen) folder interface/ add a new file portraits_gothic.gfx, with the following structure (copied from another vanilla portraits.gfx):
spriteTypes = {
  spriteType = {
    name = "PORTRAIT_gothicgfx_child_male"
  }
  spriteType = {
    name = "PORTRAIT_gothicgfx_child_female"
  }
  portraitType = {
    name = "PORTRAIT_gothicgfx_male"
  }
  portraitType = {
    name = "PORTRAIT_gothicgfx_male1"
  }
  portraitType = {
    name = "PORTRAIT_gothicgfx_male2"
  }
  portraitType = {
    name = "PORTRAIT_gothicgfx_female"
  }
  portraitType = {
    name = "PORTRAIT_gothicgfx_female1"
  }
  portraitType = {
    name = "PORTRAIT_gothicgfx_female2"
  }
}
  • For each portraitType, reference the correct spriteType for each layer (custom, vanilla or DLC spriteType):
portraitType = {
  name = "PORTRAIT_gothicgfx_female"
  effectFile = "gfx/FX/portrait.lua"
  layer = { # GFX_TYPE:[d|p]INDEX:COLOR_LINK:DONT_REFRESH_IF_VALID:CULTURE_INDEX
    "GFX_character_background:p0"
    "GFX_byzantine_female_clothes_behind:p3:c0"
    "GFX_byzantine_female_headgear_behind:p5:c1"
    "GFX_norse_female_hair_behind:p1:h:y"
    "GFX_norse_female_base:p2"
    "GFX_norse_female_neck:d0"
    "GFX_norse_female_mouth:d2"
    "GFX_norse_female_nose:d3"
    "GFX_norse_female_chin:d1"
    "GFX_norse_female_cheeks:d4"
    "GFX_norse_female_eyes:d6"
    "GFX_norse_female_eyes2:d6:e"
    "GFX_character_scars:p7:y"
    "GFX_character_reddots:p8"
    "GFX_character_boils:p9"
    "GFX_character_blinded_eyes:p10"
    "GFX_norse_female_ear:d7"			
    "GFX_byzantine_female_clothes:p3:c2"
    "GFX_empty:p5:c3"
    "GFX_norse_female_hair:p1:h:y"
    "GFX_empty:p3:c4"
    "GFX_byzantine_female_headgear:p5:c5"
    "GFX_character_imprisoned:p6"
    "GFX_player_overlay:p11"
}

Static portraits

To display static pre-rendered portraits, there are a few techniques:

Misc layer

Using a miscellaneous layer (for instance blinded_eyes) and add frames for pre-rendered portraits:[3]

  • Move the layer to the end of layer list in portraits.gfx
  • Increase the number of frames in portrait_sprites.gfx (ex: GFX_character_blinded_eyes)
  • Edit sprite (ex: blinded_eyes.dds) to add the extra frame(s)
  • Add an entry in portrait_properties.txt for each frame inside the property corresponding to the layer (ex: p10 blinded), with a trigger based on unique trait or character flag:
# p10 blinded
10 = {
	0 = {
		factor = 100
		modifier = {
			factor = 0
			trait = blinded
		}
		modifier = {
			factor = 0
			#Either a character flag or a unique trait.
		}
	}
	
	1 =  {
		factor = 100
		modifier = {
			factor = 0
			NOT = {
				trait = blinded
			}
		}
		modifier = {
			factor = 0
			#Either a character flag or a unique trait.
		}

	}
	2 =  {
		factor = 100
		modifier = {
			factor = 0
			NOT = {
				#Either a character flag or a unique trait.
			}
		}
	}
}

Occluded flag

Using occluded = yes in character history; this is used for Muhammad (The Prophet). Big limitation is that everyone with occluded = yes will have same portrait. It uses a single frame texture with hardcoded name Muhammad.tga:

spriteType = {
  name = "GFX_Muhammad"
  texturefile = "gfx\\characters\\Muhammad.tga"
  noOfFrames = 1
  norefcount = yes
}

Custom ethnicity

Using a custom ethnicity, and setting it in character history, or via event using set_graphical_culture command. Because the commands requires a culture as argument, a custom culture definition must be added too. Also the ethnicity being randomly inherited from one of the parents at birth, some on_action events may be needed to control things.

The steps are:

  • Create a custom culture in common/cultures/
special = {
  graphical_cultures = { specialgfx }
  ...
}
  • Define a portrait in interface/ for the custom ethnicity, with the fixed frame layer based on p2 property
spriteType = {
  name = "GFX_special"
  texturefile = "gfx\\characters\\special.tga"
  noOfFrames = 1
  norefcount = yes
}

portraitType = {
  name = "PORTRAIT_specialgfx_male"
  effectFile = "gfx/FX/portrait.lua"
  layer = { # GFX_TYPE:[d|p]INDEX:COLOR_LINK:DONT_REFRESH_IF_VALID:CULTURE_INDEX
    "GFX_character_background:p0"
    "GFX_special:p2"
    "GFX_character_imprisoned:p6"
  }
  hair_color = { { 10 10 10 } { 50 50 50 } { 255 255 255 } } #Must be included.
  eye_color = {	{ 255 255 255} } # Leaving these out will crash the game.
}

portraitType = {
  name = "PORTRAIT_specialgfx_female"
  ... #The rest is same as above. Both *_male and *_female portraitType are required, even for static portraits.
}
  • Use set_graphical_culture = special command and graphical_culture = specialgfx condition in events.

External links

See also

References