# Jota Dev Multicharacter

<figure><img src="/files/KfniT4muMdQEMcKWIeqO" alt=""><figcaption></figcaption></figure>

How to start the script for its correct operation:

**Put in your server.cfg**

```markdown
# JotaDev Multicharacter
ensure jc_multicharacter
```

**General script settings:**

{% tabs %}
{% tab title="Setting" %}
{% code expandable="true" %}

```lua
Config = {}

Config.Debug = false -- Debug mode, do not activate unless necessary or authorized by suppor

Config.Framework = '' -- 'ESX' or 'OLDESX' | 'QBCore' | 'QBox'

--[[  
Configure your language using the following:
    'es' -> Spanish
    'en' -> English
    'fr' -> French
    'de' -> German
    'it' -> Italian
    'pt' -> Portuguese
]]
Config.Locale = 'en' 

Config.ServerName = 'Jota Dev RP' -- Enter your server name

Config.CommandAdminPanel = 'adminchar' -- Command to access the admin panel

Config.StaffGroups = {
  esx = { 'admin', 'superadmin' },
  qbcore = { 'admin', 'god' },
  qbox = { 'admin', 'god' }
}

Config.StaffIdentifiers = {
    'license:abcdefghijklmnopqrstuvwxyz0123456789',
    'steam:00000000000000000',
    'discord:000000000000000'
}

Config.CommandLogOut = 'jlogout' -- Command to log out and change characters

Config.DefaultSlots = 2 -- Here you must configure the minimum number of slots a new player will have upon entering the server
Config.MaxSlots = 4 -- Maximum number of slots available to each user on the server

Config.AllowCharacterDelete = false -- If you activate this option, all players will have a button to delete their character

Config.CharacterSpots = { -- Here you will have the option to configure the character's sport when it is new or selected
    pedCoords = vector4(571.8934, -410.384, -69.64, 182.47),
    camCoords = {
        x = 572.6017,
        y = -414.900,
        z = -69.60,
        rotX = 0.0,
        rotY = -1.01,
        rotZ = 12.0,
        fov = 35.0
    },
    animation = {
        dict = "random@street_race",
        anim = "_car_b_lookout",
    },
    sceneceTaxyWelcome = true, -- Activates or deactivates the taxi scene
    createCharacterAppearanceDelay = 10 -- Option for players to position themselves correctly before starting the character creator -> false if you don't want to use it
}

Config.SpawnPoints = {
    allSpawnsNoNew = false, -- When a user already has a created character, it allows the spawn positions to be displayed
    spawnSelector = true, -- This option activates or deactivates the spawn selector; you can spawn directly at the last location by selecting your character
    {
        label = 'spawn_paleto_label',
        description = 'spawn_paleto_desc',
        imagen = 'img/spawn/Paleto.png',
        coords = vector4(-247.77, 6331.55, 32.43, 225.19),
        taxi = {
            spawn = vector4(-290.23, 6240.84, 31.41, 318.26),
            route = {
                vector3(-251.972, 6278.466, 31.415),
                vector3(-241.149, 6335.761, 32.413),
                vector3(-232.073, 6297.169, 31.388),
                vector3(-224.984, 6306.779, 31.467),
                vector3(-221.907, 6315.160, 31.473),
                vector3(-239.456, 6334.420, 32.361)
            }
        },
        css = {
            top = "57%",
            left = "42%"
        }
    },

    {
        label = 'spawn_airport_label',
        description = 'spawn_airport_desc',
        imagen = 'img/spawn/Aeropuerto.png',
        coords = vector4(-1038.03, -2736.89, 20.169, 331.77),
        taxi = {
            spawn = vector4(-1089.55, -2607.62, 20.163, 156.0),
            route = {
                vector3(-1095.85, -2651.97, 20.176),
                vector3(-1063.80, -2707.64, 20.190),
                vector3(-1034.71, -2728.99, 20.104)
            }
        },
        css = {
            top = "65%",
            left = "93% "
        }
    },

    {
        label = 'spawn_sandy_label',
        description = 'spawn_sandy_desc',
        imagen = 'img/spawn/Sandy.png',
        coords = vector4(1839.237, 3672.474, 34.276, 206.37),
        taxi = {
            spawn = vector4(1795.874, 3694.572, 34.240, 118.35),
            route = {
                vector3(1790.951, 3671.817, 34.233),
                vector3(1805.744, 3645.611, 34.296),
                vector3(1832.541, 3646.949, 34.303),
                vector3(1842.091, 3667.583, 33.679)
            }
        },
        css = {
            top = "35%",
            left = "57%"
        }
    }

}

Config.StartItemList = { -- Items given to you when a player is a new user
    {name = 'phone', amount = 1}
}

Config.Nationalities = { -- Nationalities that can select new players
    "España",
    "Argentina",
    "México",
    "Estados Unidos",
    "Francia",
    "Alemania",
    "Italia",
    "Brasil",
    "Colombia",
    "Chile",
    "Perú",
    "Venezuela",
    "Portugal"
}

Config.StaticID = { -- Static ID configuration
    min = 1000, -- Minimum ID that will exist
    max = 9999, -- Maximum ID that will exist on the server
    start = 1001 -- First ID that is registered
}

Config.CinematicScenes = { -- Scenes that exist when you are not selecting a character
    {
        label = "Escena 4",
        duration = 12000,
        ped = {
            coords = vector4(969.2969, 40.15713, 115.16, 54.64),
            animDict = "amb@world_human_leaning@female@wall@back@holding_elbow@idle_a",
            animName = "idle_a",
            scenario = nil
        },
        camera = {
            points = {
                {
                    coords = vector3(967.2592, 45.97265, 116.16),
                    rot = vector3(-10.0, 15.0, 207.16),
                    fov = 40.0,
                    transitionTime = 4000
                },
                {
                    coords = vector3(966.4938, 40.06847, 116.66),
                    rot = vector3(-10.0, -15.0, 260.38),
                    fov = 40.0,
                    transitionTime = 6000
                }
            }
        }
    },
    {
        label = "Escena 1",
        duration = 12000,
        ped = {
            coords = vector4(972.1172, 77.07100, 115.86, 332.93),
            animDict = "switch@trevor@annoys_sunbathers",
            animName = "trev_annoys_sunbathers_loop_guy",
            scenario = nil
        },
        camera = {
            points = {
                {
                    coords = vector3(971.7915, 77.62639, 116.10),
                    rot = vector3(-100.0, 10.0, 0.61),
                    fov = 40.0,
                    transitionTime = 7000
                },
                {
                    coords = vector3(971.7915, 77.62639, 119.46),
                    rot = vector3(-100.0, -150.0, 0.58),
                    fov = 40.0,
                    transitionTime = 7000
                }
            }
        }
    },
    {
        label = "Escena 2",
        duration = 12000,
        ped = {
            coords = vector4(967.9945, 76.50695, 115.20, 308.85),
            animDict = "timetable@jimmy@mics3_ig_15@",
            animName = "mics3_15_base_tracy",
            scenario = nil
        },
        camera = {
            points = {
                {
                    coords = vector3(972.7063, 73.80249, 116.16),
                    rot = vector3(-10.0, 15.0, 50.0),
                    fov = 40.0,
                    transitionTime = 8000
                },
                {
                    coords = vector3(968.6668, 73.60807, 116.67),
                    rot = vector3(-10.0, -15.0, 15.87),
                    fov = 40.0,
                    transitionTime = 6000
                }
            }
        }
    },
    {
        label = "Escena 2",
        duration = 12000,
        ped = {
            coords = vector4(968.6526, 70.65459, 115.17, 346.14),
            animDict = "timetable@ron@ig_3_couch",
            animName = "base",
            scenario = nil
        },
        camera = {
            points = {
                {
                    coords = vector3(966.5704, 73.36096, 116.17),
                    rot = vector3(-10.0, 0.0, 205.0),
                    fov = 40.0,
                    transitionTime = 4000
                },
                {
                    coords = vector3(971.5499, 72.00302, 116.17),
                    rot = vector3(-10.0, 0.0, 122.0),
                    fov = 40.0,
                    transitionTime = 6000
                }
            }
        }
    },
    {
        label = "Escena 3",
        duration = 12000,
        ped = {
            coords = vector4(948.9595, 8.817829, 115.21, 328.5),
            animDict = "timetable@ron@ig_5_p3",
            animName = "ig_5_p3_base",
            scenario = nil
        },
        camera = {
            points = {
                {
                    coords = vector3(951.6001, 10.21133, 116.92),
                    rot = vector3(-10.0, 0.0, 133.77),
                    fov = 40.0,
                    transitionTime = 6000
                },
                {
                    coords = vector3(949.0665, 11.34231, 116.91),
                    rot = vector3(-8.0, 0.0, 189.38),
                    fov = 40.0,
                    transitionTime = 6000
                }
            }
        }
    },
    {
        label = "Escena 5",
        duration = 11000,
        ped = {
            coords = vector4(984.8383, 59.92016, 115.16, 323.66),
            scenario = "WORLD_HUMAN_GUARD_STAND",
            animDict = nil,
            animName = nil
        },
        camera = {
            points = {
                {
                    coords = vector3(985.6467, 62.77779, 116.16),
                    rot = vector3(0.0, 0.0, 173.9),
                    fov = 38.0,
                    transitionTime = 5000
                },
                {
                    coords = vector3(986.4858, 60.93440, 116.16),
                    rot = vector3(5.0, 0.0, 116.0),
                    fov = 40.0,
                    transitionTime = 3000
                }
            }
        }
    },
}

Config.ClothingExports = function(cacheped, skinData)
    if not cacheped or not skinData then
        print('[ERROR] Invalid parameters for clothing export')
        return false
    end
    
    if GetResourceState('rcore_clothing') == 'started' then
        local success, result = pcall(function()
            if type(skinData) ~= 'table' then return end

            exports['rcore_clothing']:setPedSkin(cacheped, skinData)
        end)

        if not success then
            print('[ERROR] rcore_clothing export failed:', result)
        end

        return success
    end

    if GetResourceState('qs-appearance') == 'started' then
        local success, result = pcall(function()
            if type(skinData) ~= "table" then
                return
            end

            skinData.model = nil

            exports['qs-appearance']:setPedAppearance(cacheped, skinData)
        end)

        if not success then
            print('[ERROR] qs-appearance export failed:', result)
        end

        return success
    end

    if GetResourceState('illenium-appearance') == 'started' then
        local success, result = pcall(function()
            if type(skinData) ~= "table" then
                return
            end

            skinData.model = nil

            exports['illenium-appearance']:setPedAppearance(cacheped, skinData)
        end)

        if not success then
            print('[ERROR] illenium-appearance export failed:', result)
        end

        return success
    end

    if GetResourceState('origen_clothing') == 'started' then
        local success, result = pcall(function()
            local ped = cacheped
            if not DoesEntityExist(ped) then return end

            local appearance = nil

            if selectedCharacter and selectedCharacter.citizenid then
                appearance = exports['origen_clothing']:getAppearance(selectedCharacter.citizenid)
            end

            if not appearance then
                appearance = exports['origen_clothing']:GetAppearanceData(ped)
            end

            if not appearance then
                return
            end

            exports['origen_clothing']:SetAppearance(ped, appearance)
        end)

        if not success then
            print('[ERROR] origen_clothing SetAppearance failed:', result)
        end

        return success
    end

    if GetResourceState('bl_appearance') == 'started' then
        local success, result = pcall(function()
            if not cacheped or not DoesEntityExist(cacheped) then return end
            if type(skinData) == 'table' and next(skinData) then
                exports.bl_appearance:SetPedAppearance(cacheped, skinData)
            elseif cacheped == PlayerPedId() then
                exports.bl_appearance:SetPlayerPedAppearance()
            end
        end)
        if success then
            return true
        end
        if not (type(result) == 'string' and result:find('No such export')) then
            print('[ERROR] bl_appearance export failed:', result)
        end
    end
    
    if GetResourceState('fivem-appearance') == 'started' then
        local success, result = pcall(function()
            exports['fivem-appearance']:setPedAppearance(cacheped, skinData)
        end)
        if not success then
            print('[ERROR] fivem-appearance export failed:', result)
        end
        return success
    end
    
    if GetResourceState('qb-clothing') == 'started' then
        if type(skinData) ~= 'table' then
            skinData = {}
        end

        local ok, err = pcall(function()
            TriggerEvent('qb-clothing:client:loadPlayerClothing', skinData, cacheped)
        end)

        if not ok then
            print('[ERROR] qb-clothing loadPlayerClothing falló:', err)
        end

        return ok
    end
   
    if GetResourceState('skinchanger') == 'started' then
        local success, result = pcall(function()
            if type(skinData) ~= "table" then
                skinData = {}
            end
            
            if jc and jc.bridge and jc.bridge.GetFramework() == 'ESX' then
                local cleanSkin = {}
                
                if not skinData or type(skinData) ~= "table" or next(skinData) == nil then
                    cleanSkin = {
                        sex = 0, face = 0, skin = 0, age_1 = 0, age_2 = 0,
                        beard_1 = 255, beard_2 = 100, beard_3 = 0, beard_4 = 0,
                        hair_1 = 0, hair_2 = 0, hair_color_1 = 0, hair_color_2 = 0,
                        eyebrows_1 = 0, eyebrows_2 = 100, eyebrows_3 = 0, eyebrows_4 = 0,
                        makeup_1 = 255, makeup_2 = 100, makeup_3 = 0, makeup_4 = 0,
                        lipstick_1 = 255, lipstick_2 = 100, lipstick_3 = 0, lipstick_4 = 0,
                        tshirt_1 = 0, tshirt_2 = 0, torso_1 = 0, torso_2 = 0,
                        decals_1 = 0, decals_2 = 0, arms = 0, arms_2 = 0,
                        pants_1 = 0, pants_2 = 0, shoes_1 = 1, shoes_2 = 0,
                        mask_1 = 0, mask_2 = 0, bproof_1 = 0, bproof_2 = 0,
                        chain_1 = 0, chain_2 = 0, helmet_1 = -1, helmet_2 = 0,
                        glasses_1 = -1, glasses_2 = 0, watches_1 = -1, watches_2 = 0,
                        bracelets_1 = -1, bracelets_2 = 0, bags_1 = 0, bags_2 = 0
                    }
                else
                    for k, v in pairs(skinData) do
                        if type(v) == "number" then
                            cleanSkin[k] = math.floor(v + 0.5)
                        elseif type(v) == "string" then
                            local numValue = tonumber(v)
                            if numValue then
                                cleanSkin[k] = math.floor(numValue + 0.5)
                            else
                                cleanSkin[k] = v
                            end
                        else
                            cleanSkin[k] = v
                        end
                    end
                    
                    cleanSkin.sex = cleanSkin.sex or 0
                    cleanSkin.face = cleanSkin.face or 0
                    cleanSkin.skin = cleanSkin.skin or 0
                end
                
                TriggerEvent('skinchanger:loadSkin', cleanSkin)
            else
                for k, v in pairs(skinData) do
                    if type(v) == "number" and v % 1 ~= 0 then
                        skinData[k] = math.floor(v + 0.5)
                    end
                end
                
                exports['skinchanger']:LoadSkin(skinData)
            end
        end)
        if not success then
            print('[ERROR] skinchanger export failed:', result)
        end
        return success
    end
    
    print('[JC-MultiChar WARNING] No supported clothing system found')
    return false
end

Config.CreateCharacterExports = function(forcedGender)
    local eventBasedAppearance = {'rcore_clothing', 'illenium-appearance', 'qs-appearance', 'qb-clothing', 'bl_appearance'}
    for _, res in ipairs(eventBasedAppearance) do
        if GetResourceState(res) == 'started' then
            local success, result = pcall(function()
                if jc and jc.bridge and jc.bridge.GetFramework() == 'ESX' then
                    TriggerEvent('esx_skin:resetFirstSpawn')
                    TriggerEvent('esx_skin:playerRegistered')
                else
                    TriggerEvent('qb-clothes:client:CreateFirstCharacter')
                end
            end)
            if not success then
                print(('[ERROR] %s character creation failed: %s'):format(res, tostring(result)))
            end
            return success
        end
    end

    if GetResourceState('origen_clothing') == 'started' then
        local success, result = pcall(function()
            local gender = forcedGender or LastCharacterGender or 0
            exports['origen_clothing']:startPedCreation(Config.CharacterSpots.pedCoords, gender, false, false)
        end)
        if not success then print('[ERROR] origen_clothing character creation failed:', result) end
        return success
    end

    if GetResourceState('vms_charcreator') == 'started' then
        local success, result = pcall(function()
            local gender = forcedGender or LastCharacterGender or 0
            TriggerEvent('vms_charcreator:openCreator', gender, false, false)
        end)
        if not success then print('[ERROR] vms_charcreator character creation failed:', result) end
        return success
    end

    if GetResourceState('fivem-appearance') == 'started' then
        local success, result = pcall(function()
            local config = { ped = true, headBlend = true, faceFeatures = true, headOverlays = true, components = true, props = true, allowExit = true, tattoos = true }
            exports['fivem-appearance']:startPlayerCustomization(function() end, config)
        end)
        if not success then print('[ERROR] fivem-appearance character creation failed:', result) end
        return success
    end

    if GetResourceState('crm-appearance') == 'started' then
        local success, result = pcall(function()
            TriggerEvent('crm-appearance:init-new-character', 'crm-male', function() end)
        end)
        if not success then print('[ERROR] crm-appearance character creation failed:', result) end
        return success
    end

    if GetResourceState('skinchanger') == 'started' then
        local success, result = pcall(function()
            TriggerEvent('esx_skin:openSaveableMenu', function() end, function() end)
        end)
        if not success then print('[ERROR] skinchanger character creation failed:', result) end
        return success
    end

    print('[WARNING] No supported character creation system found')
    return false
end

Config.DefaultMaleModel = 'mp_m_freemode_01'
Config.DefaultFemaleModel = 'mp_f_freemode_01'
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

***Put the Database in your server***

{% tabs %}
{% tab title="ESX" %}

```sql
CREATE TABLE IF NOT EXISTS `jotadev_slots` (
    `identifier` VARCHAR(64) NOT NULL PRIMARY KEY,
    `slots` INT NOT NULL DEFAULT 2
);

ALTER TABLE users
ADD COLUMN nationality VARCHAR(50) NOT NULL DEFAULT 'Unknown',
ADD COLUMN static_id INT UNSIGNED UNIQUE;
```

{% endtab %}

{% tab title="Qb-Core" %}

```sql
CREATE TABLE IF NOT EXISTS `jotadev_slots` (
    `identifier` VARCHAR(64) NOT NULL PRIMARY KEY,
    `slots` INT NOT NULL DEFAULT 2
);

ALTER TABLE players
ADD COLUMN static_id INT UNSIGNED UNIQUE;
```

{% endtab %}

{% tab title="QBox" %}

```sql
CREATE TABLE IF NOT EXISTS `jotadev_slots` (
    `identifier` VARCHAR(64) NOT NULL PRIMARY KEY,
    `slots` INT NOT NULL DEFAULT 2
);

ALTER TABLE players
ADD COLUMN static_id INT UNSIGNED UNIQUE;
```

{% endtab %}
{% endtabs %}

***

<details>

<summary>If you have any questions or problems, do not hesitate to contact us:</summary>

Discord: [`https://discord.gg/EDuZKM7Zuv`](https://discord.gg/EDuZKM7Zuv)

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jota-dev-documentation.gitbook.io/jota-dev/scripts/multi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
