How to implement your original freemode_ped haircuts correctly

Hey all, :wave:

Sooooo, there was something bugging me for a while now, and that is the fact that most of the character creation resources / frameworks feel…incomplete. More specifically, it felt like the hairstyles (default ones) are not…finished.

In this thread / tutorial / thing, I am going to show you how to go from this:
image
image

To this:
image
image

The problem:

The reason why some haircuts look weird on most servers, but ok-ish (at least better) in GTA:O, is because R* uses “Ped Decorations” to fill in the textures. (just like tattoos, clothing details, and some other stuff).
Those decorations are not set with the hair Ped Component, but you have to add them yourself, using AddPedDecorationFromHashes(): AddPedDecorationFromHashes - Natives @ Cfx.re Docs

The data:

ok CritteR, but how do I know which decoration goes for my haircut??

Well…you don’t, but I do (for most of them), and I am going to give you my full list (and implementation).

Behold, my wacky mapping-array-translator-thing:

hairDecorDefault = {'mpbeach_overlays', "FM_hair_fuzz"} --if the haircut is not there, add some hair fuzz. It will not always match, but it's better than having the hair look "glued on".

hairDecor = {
    ['m'] = {
        [0] = {"",""}, --who doesn't like a shiny head from time to time?
        [1] = {"multiplayer_overlays", "FM_M_Hair_001_a"},
        [2] = {"multiplayer_overlays", "NG_M_Hair_002"},
        [3] = {"multiplayer_overlays", "FM_M_Hair_003_a"},
        [4] = {"multiplayer_overlays", "NG_M_Hair_004"},
        [5] = {"multiplayer_overlays", "FM_M_Hair_long_a"},
        [6] = {"multiplayer_overlays", "FM_M_Hair_006_a"},
        [8] = {"multiplayer_overlays", "FM_M_Hair_008_a"},
        [9] = {"multiplayer_overlays", "NG_M_Hair_009"},
        [10] = {"multiplayer_overlays", "NG_M_Hair_013"},
        [11] = {"multiplayer_overlays", "NG_M_Hair_002"},
        [12] = {"multiplayer_overlays", "NG_M_Hair_011"},
        [13] = {"multiplayer_overlays", "NG_M_Hair_012"},
        [14] = {"multiplayer_overlays", "NG_M_Hair_014"},
        [15] = {"multiplayer_overlays", "NG_M_Hair_015"},
        [16] = {"multiplayer_overlays", "NGBea_M_Hair_000"},
        [17] = {"multiplayer_overlays", "NGBea_M_Hair_001"},
        [18] = {"mpbusiness_overlays", "FM_Bus_M_Hair_000_a"},
        [19] = {"mpbusiness_overlays", "FM_Bus_M_Hair_001_a"},
        [20] = {"mphipster_overlays", "FM_Hip_M_Hair_000_a"},
        [21] = {"mphipster_overlays", "FM_Hip_M_Hair_001_a"},
        [22] = {"multiplayer_overlays", "NGInd_M_Hair_000"},
            --23
        [24] = {"mplowrider_overlays", "LR_M_Hair_000"},
        [25] = {"mplowrider_overlays", "LR_M_Hair_001"},
        [26] = {"mplowrider_overlays", "LR_M_Hair_002"},
        [27] = {"mplowrider_overlays", "LR_M_Hair_003"},
        [28] = {"mplowrider2_overlays", "LR_M_Hair_004"},
        [29] = {"mplowrider2_overlays", "LR_M_Hair_005"},
        [30] = {"mplowrider2_overlays", "LR_M_Hair_006"},
        [31] = {"mpbiker_overlays", "MP_Biker_Hair_000_M"},
        [32] = {"mpbiker_overlays", "MP_Biker_Hair_001_M"},
        [33] = {"mpbiker_overlays", "MP_Biker_Hair_002_M"},
        [34] = {"mpbiker_overlays", "MP_Biker_Hair_003_M"},
        [35] = {"mpbiker_overlays", "MP_Biker_Hair_004_M"},
        [36] = {"mpbiker_overlays", "MP_Biker_Hair_005_M"},

        [72] = {"mpgunrunning_overlays", "MP_Gunrunning_Hair_M_000_M"},
        [73] = {"mpgunrunning_overlays", "MP_Gunrunning_Hair_M_001_M"},
        [74] = {"mpvinewood_overlays", "MP_Vinewood_Hair_M_000_M"},
        [75] = {"mptuner_overlays", "MP_Tuner_Hair_001_M"},
        [76] = {"mpsecurity_overlays", "MP_Security_Hair_001_M"},
    },
    ['f'] = {
        [0] = {"",""}, --who doesn't like a shiny head from time to time?
        [1] = {"multiplayer_overlays", "NG_F_Hair_001"},
        [2] = {"multiplayer_overlays", "NG_F_Hair_002"},
        [3] = {"multiplayer_overlays", "FM_F_Hair_003_a"},
        [4] = {"multiplayer_overlays", "NG_F_Hair_004"},
        [5] = {"multiplayer_overlays", "FM_F_Hair_005_a"},
        [6] = {"multiplayer_overlays", "FM_F_Hair_006_a"},
        [7] = {"multiplayer_overlays", "NG_F_Hair_007"},
        [8] = {"multiplayer_overlays", "NG_F_Hair_008"},
        [9] = {"multiplayer_overlays", "NG_F_Hair_009"},
        [10] = {"multiplayer_overlays", "NG_F_Hair_010"},
        [11] = {"multiplayer_overlays", "NG_F_Hair_011"},
        [12] = {"multiplayer_overlays", "NG_F_Hair_012"},
        [13] = {"multiplayer_overlays", "FM_F_Hair_013_a"},
        [14] = {"multiplayer_overlays", "FM_F_Hair_014_a"},
        [15] = {"multiplayer_overlays", "NG_M_Hair_015"},
        [16] = {"multiplayer_overlays", "NGBea_F_Hair_000"},
        [17] = {"mpbusiness_overlays", "FM_Bus_F_Hair_a"},
        [18] = {"multiplayer_overlays", "NG_F_Hair_007"},
        [19] = {"multiplayer_overlays", "NGBus_F_Hair_000"},
        [20] = {"multiplayer_overlays", "NGBus_F_Hair_001"},
        [21] = {"multiplayer_overlays", "NGBea_F_Hair_001"},
        [22] = {"mphipster_overlays", "FM_Hip_F_Hair_000_a"},
        [23] = {"multiplayer_overlays", "NGInd_F_Hair_000"},
        --24
        [25] = {"mplowrider_overlays", "LR_F_Hair_000"},
        [26] = {"mplowrider_overlays", "LR_F_Hair_001"},
        [27] = {"mplowrider_overlays", "LR_F_Hair_002"},
        [29] = {"mplowrider2_overlays", "LR_F_Hair_003"},
        [30] = {"mplowrider2_overlays", "LR_F_Hair_004"},
        [31] = {"mplowrider2_overlays", "LR_F_Hair_006"},
        [32] = {"mpbiker_overlays", "MP_Biker_Hair_000_F"},
        [33] = {"mpbiker_overlays", "MP_Biker_Hair_001_F"},
        [34] = {"mpbiker_overlays", "MP_Biker_Hair_002_F"},
        [35] = {"mpbiker_overlays", "MP_Biker_Hair_003_F"},
        [38] = {"mpbiker_overlays", "MP_Biker_Hair_004_F"},
        [36] = {"mpbiker_overlays", "MP_Biker_Hair_005_F"},
        [37] = {"mpbiker_overlays", "MP_Biker_Hair_005_F"},

        [76] = {"mpgunrunning_overlays", "MP_Gunrunning_Hair_F_000_F"},
        [77] = {"mpgunrunning_overlays", "MP_Gunrunning_Hair_F_001_F"},
        [78] = {"mpvinewood_overlays", "MP_Vinewood_Hair_F_000_F"},
        [79] = {"mptuner_overlays", "MP_Tuner_Hair_000_F"},
        [80] = {"mpsecurity_overlays", "MP_Security_Hair_000_F"},
    },
}

This is basically:

hairDecor[Ped_Type][Hair Drawable ID] = {_decorCollection, _decorOverlay}

What I did basically, I opened up openIV, searched for hair overlays through all the DLCs overlay .xmls, and matched them with hair component drawable ids that I thought matched what R* uses.

Some of them are missing, and I suspect R* uses a generic decoration for them, like the hair fuzz, or “FM_M_Hair_long_a”.

My solution:

I just added this code, after setting up my hair component:

if hairDecor[char.gender][char.hair] ~= nil then
        AddPedDecorationFromHashes(ped, hairDecor[char.gender][char.hair][1], hairDecor[char.gender][char.hair][2])
    else
        AddPedDecorationFromHashes(ped, hairDecorDefault[1], hairDecorDefault[2])
    end

In my case:

-- ped = PlayerPedId()
-- char.gender = "m" or "f", depending on freemode_ped used.
-- char.hair = GetPedDrawableVariation(ped, 2)

Notes:

When doing this in a, let’s say, barber shop menu, keep in mind that AddPedDecorationFromHashes(), as the native implies, ADDS decorations, not REPLACE them!
I, personally, use ClearPedDecorations(ped) before setting the hair decorator. This will 100% cause problems if you have tattoos on your peds, but that’s a bug future CritteR will have to deal with.

10 Likes

Thank you for this !

1 Like

I just looked at the decompiled scripts, it seems that mpBeach_overlays needs a B in uppercase, I don’t know if this change anything, but I guess it is worth mentioning it.

most names are typed with uppercase, but from my testing, it’s not case sensitive.

Looking at the decompiled scripts can definitely help you fill in the blanks in the hairstyles array, though :smiley:

Ok, I wasn’t sure about this, but now I am :wink:

Yes I was trying to get more informations :slight_smile:


I just found this gist which should help fill in the blanks : GTA:V Hair Overlay Values · GitHub

By the way I tested all values from this gist and they seem right, you can add them to your topic.

Hi ! How can I emplement this to esx_barbershop ? And make it save to database ? Im struggling a bit…

how do i add the fade and to which script can u maybe make a tutorial or help me

This is just a code snippet.

Ped decorations should be added / re-added every time you change the peds hair. You also need to keep in mind that ped decorations are added, not set. You will need to reset them every time you want to change them (which will also reset the tattoos).

If your server has a scripter, it should be able to implement this in your code. If you are using default resources from a framework, you might want to ask the maintainers of that framework to update it.

i dont understand

Now it’s much better, thanks!

Hi there!

I found this script from March 2021, which uses the GET_SHOP_PED_APPAREL_FORCED_COMPONENT_COUNT/ GET_FORCED_COMPONENT natives to find the collection and overlay hashes used in GTA:O for a given hair drawable ID.

I tested something similar in a C# SP script and it turned out that, out of a total of 160 <gender, hairstyle> pairs as of 1.61 (IDs 0->78 for male, except #23, and 0->82 for female, except #24):

  • for 114, it works (including #0 for both genders, although the overlay is not found with those calls, but by falling back on the default);
  • for 34, it doesn’t find any valid overlay hash, but there are visually identical haircuts for which it finds (male: 1->15; female: 1->16, 18, 55, 58);
  • for 6, it doesn’t find any valid overlay hash, but GTA:O doesn’t set hair decorations either, not even "FM_Hair_Fuzz", at least on PC (male: 22&58; female: 23&61, 79, 80);
  • for 6, it doesn’t find any valid overlay hash, but GTA:O does set hair decorations (male: 16&52, 17&53; female: 21&59).

Therefore, those last 6 (which are actually 3 unique hairstyles) are the only ones whose in-game overlays are unidentifiable by this means.

This method also reveals some mismatches (read: bugs), which are reproducible in GTA:O (1.61). The following identical female haircuts are matched to different overlays: 17&55(*), 19&57, 20&58(*), 37&74, 38&75. In all cases, the lower drawable ID is the one matched properly, judging by hair length and symmetry. However, barber shops in GTA:O set the greater ID.
(*) The script does not find valid hashes for 55 and 58, but I tested them in GTA:O. Particularly, for 58 the game does not set any decoration.

You may give that code a try. If you wish to stick to the hardcoded version though, this is what you should change in order to have the original mapping:

--['m']
        [1] = {"multiplayer_overlays", "NG_M_Hair_001"}, --found for 37
        [3] = {"multiplayer_overlays", "NG_M_Hair_003"}, --found for 39
        [5] = {"multiplayer_overlays", "NG_M_Hair_005"}, --found for 41
        [6] = {"multiplayer_overlays", "NG_M_Hair_006"}, --found for 42
		[7] = {"multiplayer_overlays", "NG_M_Hair_007"}, --found for 43
        [8] = {"multiplayer_overlays", "NG_M_Hair_008"}, --found for 44
        [10] = {"multiplayer_overlays", "NG_M_Hair_010"}, --found for 46
        [11] = {"multiplayer_overlays", "NG_M_Hair_011"}, --found for 47
        [12] = {"multiplayer_overlays", "NG_M_Hair_012"}, --found for 48
        [13] = {"multiplayer_overlays", "NG_M_Hair_013"}, --found for 49
        [77] = {"mpsum2_overlays", "MP_Sum2_Hair_000_M"}, --new
        [78] = {"mpsum2_overlays", "MP_Sum2_Hair_002_M"}, --new

--['f']
        [3] = {"multiplayer_overlays", "NG_F_Hair_003"}, --found for 41
        [5] = {"multiplayer_overlays", "NG_F_Hair_005"}, --found for 43
        [6] = {"multiplayer_overlays", "NG_F_Hair_006"}, --found for 44
        [13] = {"multiplayer_overlays", "NG_F_Hair_013"}, --found for 51
        [14] = {"multiplayer_overlays", "NG_F_Hair_014"}, --found for 52
        [15] = {"multiplayer_overlays", "NG_F_Hair_015"}, --found for 53; you have 'M', clearly a typo
        [16] = {"mphipster_overlays", "FM_F_Hair_017_a"}, --found for 54 (same as found for 17, so likely affected by the mismatch of 55, 57, 58)
        [17] = {"mphipster_overlays", "FM_F_Hair_017_a"},
        [18] = {"mpbusiness_overlays", "FM_Bus_F_Hair_b"}, --found for 56 (same as found for 19, so likely affected by the mismatch of 55, 57, 58)
        [19] = {"mpbusiness_overlays", "FM_Bus_F_Hair_b"},
        [20] = {"mphipster_overlays", "FM_F_Hair_020_a"},
        [28] = {"mplowrider2_overlays", "LR_F_Hair_003"},
        [36] = {"multiplayer_overlays", "NG_F_Hair_014"},
        [37] = {"mpbiker_overlays", "MP_Biker_Hair_006_F"},
        [38] = {"mpbiker_overlays",	"MP_Biker_Hair_005_F"},
        [81] = {"mpsum2_overlays", "MP_Sum2_Hair_001_F"}, --new
        [82] = {"mpsum2_overlays", "MP_Sum2_Hair_003_F"}, --new
1 Like

Hi, recently i changed My clothes data, and now barber, shop or skin menu sets limit to 77 hairs, can’t see all the hairs supposed to be there, can i have some help for it?

How can i add this to my fivem server?