├── README.md ├── bridge ├── framework │ ├── server │ │ ├── esx.lua │ │ └── qbcore.lua │ └── client │ │ ├── esx.lua │ │ └── qbcore.lua └── inventory │ ├── client │ ├── ox_inventory.lua │ ├── qb-inventory.lua │ └── quasargago.lua │ └── server │ ├── ox_inventory.lua │ ├── qb-inventory.lua │ └── quasargago.lua ├── renzu_clotheshop.sql ├── fxmanifest.lua ├── html ├── index.html ├── styleblue.css ├── stylebluex.css └── script.js ├── server ├── main.lua └── old │ └── server.lua ├── client └── main.lua └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # renzu_clothes 2 | - Fivem Advanced Clotheshop and Wardrobe 3 | 4 | TODO 5 | 6 | -------------------------------------------------------------------------------- /bridge/framework/server/esx.lua: -------------------------------------------------------------------------------- 1 | if not ESX then return end 2 | 3 | GetPlayerFromId = function(src) 4 | if type(src) == 'string' then 5 | return ESX.GetPlayerFromIdentifier(src) 6 | end 7 | return ESX.GetPlayerFromId(src) 8 | end -------------------------------------------------------------------------------- /renzu_clotheshop.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE `users` ADD COLUMN `skin` LONGTEXT NULL DEFAULT NULL; 2 | 3 | CREATE TABLE IF NOT EXISTS `renzu_clothes` ( 4 | `identifier` varchar(64) NOT NULL DEFAULT '', 5 | `wardrobe` longtext NULL, 6 | `inventory` longtext NULL, 7 | PRIMARY KEY (`identifier`) 8 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -------------------------------------------------------------------------------- /bridge/inventory/client/ox_inventory.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('ox_inventory') ~= 'started' then return end 2 | 3 | OpenStash = function(data,identifier) 4 | TriggerEvent('ox_inventory:openInventory', 'stash', {id = 'stash_'..data.motel..'_'..identifier..'_'..data.index, name = 'Storage', slots = 70, weight = 70000, coords = GetEntityCoords(cache.ped)}) 5 | end -------------------------------------------------------------------------------- /bridge/inventory/server/ox_inventory.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('ox_inventory') ~= 'started' then return end 2 | 3 | AddItem = function(src, item, count, metadata) 4 | return exports.ox_inventory:AddItem(src, item, count, metadata) 5 | end 6 | 7 | RemoveItem = function(src, item, count, metadata) 8 | return exports.ox_inventory:RemoveItem(src, item, count, metadata) 9 | end -------------------------------------------------------------------------------- /bridge/inventory/server/qb-inventory.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qb-inventory') ~= 'started' then return end 2 | 3 | AddItem = function(src, item, count, metadata) 4 | return exports['qb-inventory']:AddItem(src, item, count, slot, metadata) 5 | end 6 | 7 | RemoveItem = function(src, item, count, metadata) 8 | return exports['qb-inventory']:RemoveItem(src, item, count, slot, metadata) 9 | end -------------------------------------------------------------------------------- /bridge/inventory/client/qb-inventory.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qb-inventory') ~= 'started' then return end 2 | 3 | OpenStash = function(data,identifier) 4 | TriggerServerEvent('inventory:server:OpenInventory', 'stash', 'stash_'..data.motel..'_'..identifier..'_'..data.index, {}) 5 | TriggerServerEvent("InteractSound_SV:PlayOnSource", "StashOpen", 0.4) 6 | TriggerEvent("inventory:client:SetCurrentStash", 'stash_'..data.motel..'_'..identifier..'_'..data.index) 7 | end -------------------------------------------------------------------------------- /bridge/inventory/client/quasargago.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qs-inventory') ~= 'started' then return end 2 | 3 | OpenStash = function(data,identifier) 4 | TriggerServerEvent('inventory:server:OpenInventory', 'stash', 'stash_'..data.motel..'_'..identifier..'_'..data.index, {}) 5 | TriggerServerEvent("InteractSound_SV:PlayOnSource", "StashOpen", 0.4) 6 | TriggerEvent("inventory:client:SetCurrentStash", 'stash_'..data.motel..'_'..identifier..'_'..data.index) 7 | end -------------------------------------------------------------------------------- /bridge/inventory/server/quasargago.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qs-inventory') ~= 'started' then return end 2 | 3 | AddItem = function(src, item, count, metadata) 4 | if metadata then metadata.showAllDescriptions = true end 5 | return TriggerEvent('inventory:server:addItem', src, item, count, false, metadata) 6 | end 7 | 8 | RemoveItem = function(src, item, count, metadata) 9 | return TriggerEvent('inventory:server:removeItem', src, item, count, false, metadata) 10 | end -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | -- MADE BY Renzuzu 2 | 3 | fx_version 'bodacious' 4 | game 'gta5' 5 | 6 | use_experimental_fxv2_oal 'yes' 7 | lua54 'yes' 8 | ui_page { 9 | 'html/index.html', 10 | } 11 | 12 | shared_script '@renzu_shield/init.lua' 13 | shared_script '@ox_lib/init.lua' 14 | 15 | server_scripts { 16 | 'bridge/framework/server/*.lua', 17 | '@mysql-async/lib/MySQL.lua', 18 | 'server/**.lua', 19 | } 20 | 21 | client_scripts { 22 | 'bridge/framework/client/*.lua', 23 | 'components.lua', 24 | 'client/**.lua', 25 | } 26 | 27 | shared_script 'config.lua' 28 | 29 | files { 30 | 'html/index.html', 31 | 'html/script.js', 32 | 'html/*.css', 33 | 'html/img/**/*.png', 34 | 'gago.json', 35 | } 36 | -------------------------------------------------------------------------------- /bridge/framework/client/esx.lua: -------------------------------------------------------------------------------- 1 | if not ESX then return end 2 | PlayerData = ESX.GetPlayerData() 3 | 4 | RegisterNetEvent('esx:playerLoaded') 5 | AddEventHandler('esx:playerLoaded',function(playerData) 6 | PlayerData = playerData 7 | end) 8 | 9 | RegisterNetEvent('esx:setJob') 10 | AddEventHandler('esx:setJob', function(job) 11 | PlayerData.job = job 12 | end) 13 | 14 | GetInventoryItems = function(name) 15 | local PlayerData = ESX.GetPlayerData() 16 | local data = {} 17 | local itemdata = {} 18 | for _, item in pairs(PlayerData.inventory) do 19 | for k,v in pairs(items) do 20 | if v == item.name then 21 | if not itemdata[item.name] then 22 | itemdata[item.name] = item 23 | else 24 | itemdata[item.name].count += item.count 25 | end 26 | table.insert(data,itemdata) 27 | end 28 | end 29 | end 30 | return data 31 | end -------------------------------------------------------------------------------- /bridge/framework/server/qbcore.lua: -------------------------------------------------------------------------------- 1 | if not QBCORE then return end 2 | 3 | GetPlayerFromId = function(src) 4 | local self = QBCORE.Functions.GetPlayer(src) 5 | if not self then return end 6 | 7 | if self.identifier == nil then 8 | self.identifier = self.PlayerData.citizenid 9 | end 10 | 11 | self.getMoney = function(value) 12 | return self.PlayerData.money['cash'] 13 | end 14 | 15 | self.removeMoney = function(value) 16 | self.Functions.RemoveMoney('cash',tonumber(value)) 17 | return true 18 | end 19 | self.name = self.PlayerData.charinfo.firstname 20 | 21 | self.addMoney = function(value) 22 | return self.Functions.AddMoney('cash',tonumber(value)) 23 | end 24 | 25 | self.removeAccountMoney = function(type,val) 26 | if type == 'money' then type = 'cash' end 27 | self.Functions.RemoveMoney(type,tonumber(val)) 28 | return true 29 | end 30 | 31 | self.getAccount = function(type) 32 | if type == 'money' then type = 'cash' end 33 | return {money = self.PlayerData.money[type]} 34 | end 35 | 36 | return self 37 | end -------------------------------------------------------------------------------- /bridge/framework/client/qbcore.lua: -------------------------------------------------------------------------------- 1 | if not QBCORE then return end 2 | PlayerData = QBCORE.Functions.GetPlayerData() 3 | 4 | if PlayerData.job ~= nil then 5 | PlayerData.job.grade = PlayerData.job.grade.level 6 | end 7 | 8 | if PlayerData.identifier == nil then 9 | PlayerData.identifier = PlayerData.citizenid 10 | end 11 | 12 | RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() 13 | Wait(1500) 14 | PlayerData = QBCORE.Functions.GetPlayerData() 15 | 16 | if PlayerData.job ~= nil then 17 | PlayerData.job.grade = PlayerData.job.grade.level 18 | end 19 | 20 | if PlayerData.identifier == nil then 21 | PlayerData.identifier = PlayerData.citizenid 22 | end 23 | end) 24 | 25 | RegisterNetEvent('QBCore:Client:OnJobUpdate', function(job) 26 | PlayerData.job = job 27 | 28 | PlayerData.job.grade = PlayerData.job.grade.level 29 | end) 30 | 31 | GetInventoryItems = function(name) 32 | local data = {} 33 | local PlayerData = QBCORE.Functions.GetPlayerData() 34 | for _, item in pairs(PlayerData.items) do 35 | if name == item.name then 36 | item.metadata = item.info 37 | table.insert(data,item) 38 | end 39 | end 40 | return data 41 | end -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Renzu Clothes 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | Wardrobe - Clothing Inventory 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | 26 |

27 | 28 | 29 |

30 | 31 | 32 | 33 |
34 |
35 |
36 |
37 | 38 |
39 | 40 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /server/main.lua: -------------------------------------------------------------------------------- 1 | if Config.old then return end 2 | 3 | if not lib then warn('ox_lib is required in this version') return end 4 | 5 | lib.callback.register('renzu_clothes:Pay', function(src,total,appearance) 6 | local xPlayer = GetPlayerFromId(src) 7 | local money = xPlayer.getAccount('money').money 8 | if money >= total then 9 | xPlayer.removeMoney(total) 10 | SaveAppearance(xPlayer,appearance) 11 | return true 12 | end 13 | return false 14 | end) 15 | 16 | SaveOutfit = function(xPlayer,name,appearance) 17 | local str = 'SELECT %s FROM %s WHERE %s = ?' 18 | local query = MySQL.query.await(str:format('outfits','renzu_clothes_outfits','identifier'),{xPlayer.identifier}) 19 | if query[1] then 20 | local outfits = json.decode(query[1].outfits) 21 | outfits[name] = appearance 22 | return MySQL.update.await('UPDATE renzu_clothes_outfits SET outfits = ? WHERE identifier = ?',{json.encode(outfits), xPlayer.identifier}) 23 | else 24 | outfits = {[name] = appearance} 25 | return MySQL.insert.await('INSERT INTO renzu_clothes_outfits (identifier, outfits) VALUES(?, ?)',{xPlayer.identifier,json.encode(outfits)}) 26 | end 27 | return false 28 | end 29 | 30 | lib.callback.register('renzu_clothes:SaveOutfit', function(src,data) 31 | local xPlayer = GetPlayerFromId(src) 32 | local appearance = data.appearance 33 | local name = data.name 34 | return SaveOutfit(xPlayer,name,appearance) 35 | end) 36 | 37 | lib.callback.register('renzu_clothes:RemoveOutfit', function(src,name) 38 | local xPlayer = GetPlayerFromId(src) 39 | return SaveOutfit(xPlayer,name,nil) 40 | end) 41 | 42 | local charset = {} do -- [0-9a-zA-Z] 43 | for c = 48, 57 do table.insert(charset, string.char(c)) end 44 | for c = 65, 90 do table.insert(charset, string.char(c)) end 45 | for c = 97, 122 do table.insert(charset, string.char(c)) end 46 | end 47 | 48 | local function randomString(length) 49 | if not length or length <= 0 then return '' end 50 | return randomString(length - 1) .. charset[math.random(1, #charset)] 51 | end 52 | 53 | local sharedcode_cache = {} 54 | lib.callback.register('renzu_clothes:GenerateCode', function(src,appearance) 55 | local generatedcode = randomString(8):upper() 56 | sharedcode_cache[generatedcode] = appearance 57 | return generatedcode 58 | end) 59 | 60 | lib.callback.register('renzu_clothes:AddCode', function(src, data) 61 | if not data then return end 62 | if not sharedcode_cache[data[1]] then return end 63 | local xPlayer = GetPlayerFromId(src) 64 | return SaveOutfit(xPlayer,data[2],sharedcode_cache[data[1]]) 65 | end) 66 | 67 | 68 | lib.callback.register('renzu_clothes:getOutfits', function(src,data) 69 | local xPlayer = GetPlayerFromId(src) 70 | local outfits = {} 71 | local str = 'SELECT %s FROM %s WHERE %s = ?' 72 | local query = MySQL.query.await(str:format('outfits','renzu_clothes_outfits','identifier'),{xPlayer.identifier}) 73 | local outfits = query[1] and json.decode(query[1].outfits) 74 | return outfits or {} 75 | end) 76 | 77 | SaveAppearance = function(xPlayer,skin) 78 | if ESX then 79 | MySQL.query.await('UPDATE users SET skin = ? WHERE identifier = ?', {json.encode(skin), xPlayer.identifier}) 80 | else 81 | if skin.model ~= nil and skin ~= nil then 82 | -- TODO: Update primary key to be citizenid so this can be an insert on duplicate update query 83 | MySQL.query('DELETE FROM playerskins WHERE citizenid = ?', { xPlayer.PlayerData.citizenid }, function() 84 | MySQL.insert('INSERT INTO playerskins (citizenid, model, skin, active) VALUES (?, ?, ?, ?)', { 85 | xPlayer.PlayerData.citizenid, 86 | skin.model, 87 | json.encode(skin), 88 | 1 89 | }) 90 | end) 91 | end 92 | end 93 | return true 94 | end 95 | 96 | Citizen.CreateThreadNow(function() 97 | local success, result = pcall(MySQL.scalar.await, 'SELECT 1 FROM renzu_clothes_outfits') 98 | if not success then 99 | MySQL.query.await([[CREATE TABLE `renzu_clothes_outfits` ( 100 | `id` int NOT NULL AUTO_INCREMENT KEY, 101 | `identifier` varchar(64) DEFAULT NULL, 102 | `outfits` longtext DEFAULT NULL 103 | )]]) 104 | print("^2SQL INSTALL SUCCESSFULLY ^0") 105 | end 106 | end) -------------------------------------------------------------------------------- /server/old/server.lua: -------------------------------------------------------------------------------- 1 | if not Config.old then return end 2 | ESX = exports['es_extended']:getSharedObject() 3 | 4 | function AddClothestoInventory(current,data) 5 | for k,v in pairs(data) do 6 | 7 | --if current['inventory'] == nil then current['inventory'] = {} 8 | if current[v.name] == nil then 9 | --current[v.NameHash] = {} 10 | current[v.name] = {compo = k, draw = v.draw, texture = v.texture, count = 1} 11 | 12 | end 13 | end 14 | return current 15 | end 16 | 17 | function SaveClothes(clothename,clothe,xPlayer,data) 18 | local result = SqlFunc(Config.Mysql,'fetchAll','SELECT * FROM renzu_clothes WHERE identifier = @identifier', {['@identifier'] = xPlayer.identifier}) 19 | if result[1] == nil then -- save new wardrobe to db 20 | local wardrobe = {} 21 | wardrobe[clothename] = clothe 22 | SqlFunc(Config.Mysql,'execute','INSERT INTO renzu_clothes (identifier, wardrobe, inventory) VALUES (@identifier, @wardrobe, @inventory)', { 23 | ['@identifier'] = xPlayer.identifier, 24 | ['@wardrobe'] = json.encode(wardrobe), 25 | ['@inventory'] = json.encode(AddClothestoInventory({},data)) 26 | }) 27 | elseif result[1] then -- replace existing or save new 28 | 29 | local wardrobe = json.decode(result[1].wardrobe) or {} 30 | local inventory = json.decode(result[1].inventory) or {} 31 | local clothes = {} 32 | if data then 33 | clothes = AddClothestoInventory(inventory,data) 34 | 35 | end 36 | 37 | wardrobe[clothename] = clothe 38 | SqlFunc(Config.Mysql,'execute','UPDATE renzu_clothes SET wardrobe = @wardrobe, inventory = @inventory WHERE identifier = @identifier', { 39 | ['@identifier'] = xPlayer.identifier, 40 | ['@wardrobe'] = json.encode(wardrobe), 41 | ['@inventory'] = json.encode(clothes) 42 | }) 43 | end 44 | -- save skin 45 | SqlFunc(Config.Mysql,'execute','UPDATE users SET skin = @skin WHERE identifier = @identifier', { 46 | ['@skin'] = json.encode(clothe), 47 | ['@identifier'] = xPlayer.identifier 48 | }) 49 | end 50 | 51 | ESX.RegisterServerCallback('renzu_clothes:save',function(source, cb, clothe) 52 | local source = tonumber(source) 53 | local xPlayer = ESX.GetPlayerFromId(source) 54 | SqlFunc(Config.Mysql,'execute','UPDATE users SET skin = @skin WHERE identifier = @identifier', { 55 | ['@skin'] = json.encode(clothe), 56 | ['@identifier'] = xPlayer.identifier 57 | }) 58 | 59 | end) 60 | 61 | ESX.RegisterServerCallback('renzu_clothes:delclothe',function(source, cb, name) 62 | local source = tonumber(source) 63 | local xPlayer = ESX.GetPlayerFromId(source) 64 | local result = SqlFunc(Config.Mysql,'fetchAll','SELECT * FROM renzu_clothes WHERE identifier = @identifier', {['@identifier'] = xPlayer.identifier}) 65 | if result[1] ~= nil then 66 | local data = result[1] 67 | local wardrobe = json.decode(data['wardrobe']) 68 | wardrobe[name] = nil 69 | SqlFunc(Config.Mysql,'execute','UPDATE renzu_clothes SET wardrobe = @wardrobe WHERE identifier = @identifier', { 70 | ['@identifier'] = xPlayer.identifier, 71 | ['@wardrobe'] = json.encode(wardrobe) 72 | }) 73 | cb(true) 74 | end 75 | cb(false) 76 | end) 77 | 78 | ESX.RegisterServerCallback('renzu_clothes:buyclothe',function(source, cb, compo, draw, texture, dlc, bill) 79 | local source = tonumber(source) 80 | local xPlayer = ESX.GetPlayerFromId(source) 81 | 82 | if xPlayer.getMoney() >= bill then 83 | xPlayer.removeMoney(bill) 84 | local result = SqlFunc(Config.Mysql,'fetchAll','SELECT * FROM renzu_clothes WHERE identifier = @identifier', {['@identifier'] = xPlayer.identifier}) 85 | if result[1] == nil then -- save new wardrobe to db 86 | local data = {} 87 | data['inventory'] = {} 88 | data['inventory'][dlc] = {compo = compo, draw = draw, texture = texture, count = 1} 89 | SqlFunc(Config.Mysql,'execute','INSERT INTO renzu_clothes (identifier, wardrobe) VALUES (@identifier, @wardrobe)', { 90 | ['@identifier'] = xPlayer.identifier, 91 | ['@wardrobe'] = '[]', 92 | ['@inventory'] = json.encode(data['inventory']), 93 | }) 94 | elseif result[1] then -- replace existing or save new 95 | local data = {} 96 | if result[1]['inventory'] == nil then 97 | result[1]['inventory'] = {} 98 | end 99 | -- if result[1]['wardrobe'] == nil then 100 | -- result[1]['wardrobe'] = {} 101 | -- end 102 | 103 | data['inventory'] = json.decode(result[1]['inventory']) 104 | 105 | if data['inventory'][dlc] ~= nil then 106 | 107 | data['inventory'][dlc].count = tonumber(data['inventory'][dlc].count) + 1 108 | else 109 | 110 | 111 | data['inventory'][dlc] = {compo = compo, draw = draw, texture = texture, count = 1} 112 | end 113 | SqlFunc(Config.Mysql,'execute','UPDATE renzu_clothes SET inventory = @inventory WHERE identifier = @identifier', { 114 | ['@identifier'] = xPlayer.identifier, 115 | ['@inventory'] = json.encode(data['inventory']) 116 | }) 117 | end 118 | Config.Notify('success','Job', dlc..' has been added to your clothing inventory',xPlayer.source) 119 | else 120 | Config.Notify('success','Job', dlc..' cannot not afford',xPlayer.source) 121 | cb(false) 122 | end 123 | end) 124 | 125 | ESX.RegisterServerCallback('renzu_clothes:selectclothe',function(source, cb, skin) 126 | local source = tonumber(source) 127 | local xPlayer = ESX.GetPlayerFromId(source) 128 | SqlFunc(Config.Mysql,'execute','UPDATE users SET skin = @skin WHERE identifier = @identifier', { 129 | ['@skin'] = json.encode(skin), 130 | ['@identifier'] = xPlayer.identifier 131 | }) 132 | end) 133 | 134 | ESX.RegisterServerCallback('renzu_clothes:saveclothes', function(source, cb, name, data, inwardrobe, bill, clothedata, payment, currentshop) 135 | local xPlayer = ESX.GetPlayerFromId(source) 136 | if payment then -- Payment exists when opening cashier. 137 | if bill == 0 then 138 | Config.Notify('error', 'Clothing', 'You do not have a bill to settle with the cashier.', source) 139 | cb("ignore") 140 | elseif name == '' then 141 | Config.Notify('error', 'Clothing', 'You will need to give a name for your outfit.', source) 142 | cb("nosave") 143 | elseif payment == 'cash' then 144 | if xPlayer.getMoney() >= bill then 145 | xPlayer.removeMoney(bill) 146 | if Config.renzujobs then 147 | exports.renzu_jobs:addMoney(tonumber(bill),Config.Shop[currentshop].job,source,'money',true) 148 | end 149 | Config.Notify('success', 'Clothing', 'Purchased clothes successfully.', source) 150 | SaveClothes(name,data,xPlayer,clothedata) 151 | cb("save") 152 | else 153 | Config.Notify('error', 'Clothing', 'Not enough money on hand to make purchase.', source) 154 | cb("nosave") 155 | end 156 | elseif payment == 'bank' then 157 | if xPlayer.getAccount('bank').money >= bill then 158 | xPlayer.removeAccountMoney('bank', bill) 159 | if Config.renzujobs then 160 | exports.renzu_jobs:addMoney(tonumber(bill),Config.Shop[currentshop].job,source,'money',true) 161 | end 162 | Config.Notify('success', 'Clothing', 'Purchased clothes successfully.', source) 163 | SaveClothes(name,data,xPlayer,clothedata) 164 | cb("save") 165 | else 166 | Config.Notify('error', 'Clothing', 'Not enough money in bank to make purchase.', source) 167 | cb("nosave") 168 | end 169 | end 170 | else -- Payment is nil when opening the wardrobe. 171 | if name == '' then 172 | Config.Notify('error', 'Clothing', 'You will need to give a name for your outfit.', source) 173 | cb("nosave") 174 | else 175 | Config.Notify('success','Clothing', 'Outfit successfully saved.', source) 176 | SaveClothes(name,data,xPlayer,clothedata) 177 | cb("save") 178 | end 179 | end 180 | end) 181 | 182 | ESX.RegisterServerCallback('renzu_clothes:getPlayerWardrobe', function(source, cb, inventory, data) 183 | 184 | local xPlayer = ESX.GetPlayerFromId(source) 185 | local result = SqlFunc(Config.Mysql,'fetchAll','SELECT * FROM renzu_clothes WHERE identifier = @identifier', {['@identifier'] = xPlayer.identifier}) 186 | local wardrobe = {} 187 | local inventory = {} 188 | 189 | if result[1] then 190 | wardrobe = json.decode(result[1].wardrobe) or {} 191 | inventory = json.decode(result[1].inventory) or {} 192 | else 193 | SqlFunc(Config.Mysql,'execute','INSERT INTO renzu_clothes (identifier, wardrobe, inventory) VALUES (@identifier, @wardrobe, @inventory)', { 194 | ['@identifier'] = xPlayer.identifier, 195 | ['@wardrobe'] = '[]', 196 | ['@inventory'] = json.encode(AddClothestoInventory({},data)) 197 | }) 198 | wardrobe = {} 199 | inventory = AddClothestoInventory({},data) or {} 200 | end 201 | cb({inventory = inventory, wardrobe = wardrobe}) 202 | end) 203 | 204 | function SqlFunc(plugin,type,query,var) 205 | local wait = promise.new() 206 | if type == 'fetchAll' and plugin == 'mysql-async' then 207 | MySQL.Async.fetchAll(query, var, function(result) 208 | wait:resolve(result) 209 | end) 210 | end 211 | if type == 'execute' and plugin == 'mysql-async' then 212 | MySQL.Async.execute(query, var, function(result) 213 | wait:resolve(result) 214 | end) 215 | end 216 | if type == 'execute' and plugin == 'ghmattisql' then 217 | exports['ghmattimysql']:execute(query, var, function(result) 218 | wait:resolve(result) 219 | end) 220 | end 221 | if type == 'fetchAll' and plugin == 'ghmattisql' then 222 | exports.ghmattimysql:execute(query, var, function(result) 223 | wait:resolve(result) 224 | end) 225 | end 226 | if type == 'execute' and plugin == 'oxmysql' then 227 | exports.oxmysql:execute(query, var, function(result) 228 | wait:resolve(result) 229 | end) 230 | end 231 | if type == 'fetchAll' and plugin == 'oxmysql' then 232 | exports['oxmysql']:fetch(query, var, function(result) 233 | wait:resolve(result) 234 | end) 235 | end 236 | return Citizen.Await(wait) 237 | end 238 | 239 | AddEventHandler('onResourceStop', function(resourceName) 240 | if (GetCurrentResourceName() ~= resourceName) then 241 | return 242 | end 243 | 244 | end) 245 | -------------------------------------------------------------------------------- /client/main.lua: -------------------------------------------------------------------------------- 1 | if Config.old then return end 2 | 3 | if not lib then warn('ox_lib is required in this version') return end 4 | 5 | local oldskin = nil 6 | local oldcomponent = {} 7 | local carts = {} 8 | 9 | SetPedComponent = function(type, component, drawable, index) 10 | if type ~= 'Props' then 11 | return SetPedComponentVariation(cache.ped, component, drawable, index, 2) 12 | else 13 | return SetPedPropIndex(cache.ped, component, drawable, index, 2) 14 | end 15 | end 16 | 17 | ClothingMenu = function(data) 18 | local lists = {} 19 | local clothesdata = deepcopy(Config.Data) 20 | if not oldskin then 21 | oldskin = Config.GetSkin() 22 | end 23 | local nums = {} 24 | local texture = data.type ~= 'Props' and GetPedTextureVariation(cache.ped,data.id) or GetPedPropTextureIndex(cache.ped,data.id) 25 | local drawable = data.type ~= 'Props' and GetPedDrawableVariation(cache.ped,data.id) or GetPedPropIndex(cache.ped,data.id) 26 | oldcomponent = { 27 | type = data.type, 28 | texture = texture, 29 | drawable = drawable, 30 | component = data.id 31 | } 32 | for drawable = 1, data.num do 33 | local texturenum = data.type ~= 'Props' and GetNumberOfPedTextureVariations(cache.ped, data.id, drawable) or GetNumberOfPedPropTextureVariations(cache.ped, data.id, drawable, - 1) 34 | local clothes_meta = {} 35 | local textureid = 1 36 | local drawable = drawable 37 | for texture = 1, texturenum do 38 | clothes_meta = GetClotheData(data.id,drawable,texture,data.type == 'Props',data.name) or GetClotheData(data.id,drawable,1,data.type == 'Props',data.name) or clothes_meta 39 | textureid = texture 40 | table.insert(nums,clothes_meta.label and clothes_meta.label ~= clothes_meta.NameHash and clothes_meta.label or data.name:gsub('_1',''):upper() ..' - '..texture) 41 | end 42 | local label = clothes_meta.label or '' 43 | local hash = clothes_meta.NameHash or '' 44 | local icon = clothes_meta.label and clothes_meta.label ~= clothes_meta.NameHash and 'https://raw.githubusercontent.com/renzuzu/carmap/main/carmap/clothes/'..hash..'.png' or data.icon 45 | local price = clothes_meta.Price or 100 46 | table.insert(lists,{close = false, description = 'Price: '..price, icon = icon, label = 'T Shirt '..label, values = nums , args = {price = price, drawable = drawable, texture = textureid, component = data.id}}) 47 | nums = {} 48 | end 49 | 50 | if #lists == 0 then 51 | return Notify('No Clothing Available') 52 | end 53 | 54 | lib.registerMenu({ 55 | id = 'clothe_lists', 56 | title = data.name:gsub('_1',''):upper(), 57 | position = 'top-right', 58 | onSideScroll = function(selected, scrollIndex, args) 59 | SetPedComponent(data.type,args.component, args.drawable, scrollIndex) 60 | end, 61 | onSelected = function(selected, secondary, args) 62 | SetPedComponent(data.type,args.component, args.drawable, secondary) 63 | end, 64 | onClose = function(keyPressed) 65 | SetPedComponent(oldcomponent.type,oldcomponent.component, oldcomponent.drawable, oldcomponent.texture) 66 | end, 67 | options = lists 68 | }, function(selected, scrollIndex, args) 69 | 70 | if not carts[args.component] then 71 | carts[args.component] = {} 72 | end 73 | 74 | carts[args.component] = { 75 | texture = scrollIndex, 76 | drawable = args.drawable, 77 | component = args.component, 78 | price = args.price 79 | } 80 | 81 | oldcomponent = { 82 | type = data.type, 83 | texture = scrollIndex, 84 | drawable = args.drawable, 85 | component = args.component 86 | } 87 | 88 | lib.notify({ 89 | description = 'Added to Cart', 90 | type = 'success' 91 | }) 92 | end) 93 | 94 | lib.showMenu('clothe_lists') 95 | end 96 | 97 | GetNumOfPedVariations = function(type,id) 98 | if type == 'ComponentVariations' then 99 | return GetNumberOfPedDrawableVariations(cache.ped, id) 100 | elseif type == 'Props' then 101 | return GetNumberOfPedPropDrawableVariations(cache.ped, id) 102 | end 103 | end 104 | 105 | ClothingIndex = function(data) 106 | local lists = {} 107 | local clothesdata = deepcopy(Config.Data) 108 | 109 | local hasarms = false 110 | local indexes = data.indexes 111 | for k,v in pairs(indexes) do 112 | if string.find(v,'arms') then 113 | hasarms = true 114 | end 115 | if clothesdata[v] and clothesdata[v].type and not string.find(v,'_2') then 116 | local num = GetNumOfPedVariations(clothesdata[v].type, clothesdata[v].componentid) 117 | table.insert(lists,{icon = clothesdata[v].icon,label = clothesdata[v].label, description = clothesdata[v].label, args = {icon = clothesdata[v].icon, type = clothesdata[v].type, num = num, id = clothesdata[v].componentid, name = v}}) 118 | end 119 | end 120 | 121 | if #lists == 0 then 122 | Notify('No Clothing Available') 123 | return 124 | end 125 | if not hasarms then 126 | local num = GetNumOfPedVariations(clothesdata[indexes[1]].type, clothesdata[indexes[1]].componentid) 127 | return ClothingMenu({icon = clothesdata[indexes[1]].icon, type = clothesdata[indexes[1]].type, num = num, id = clothesdata[indexes[1]].componentid, name = indexes[1]}) 128 | end 129 | 130 | lib.registerMenu({ 131 | id = 'menu__index', 132 | title = data.label, 133 | position = 'top-right', 134 | options = lists 135 | }, function(selected, scrollIndex, args) 136 | ClothingMenu(args) 137 | end) 138 | 139 | lib.showMenu('menu__index') 140 | end 141 | 142 | Notify = function(msg,icon) 143 | local label = type(msg) ~= 'string' and '[E] - '..msg.label or msg 144 | lib.showTextUI(label, { 145 | position = "top-right", 146 | icon = icon or 'hand', 147 | style = { 148 | --borderRadius = '10px', 149 | backgroundColor = 'rgb(66 67 68 / 20%)', 150 | color = '#ccd1d5', 151 | textShadow = '1px 1px #8d959d', 152 | boxShadow = 'rgb(239 239 239 / 80%) 2px 2px 3px', 153 | borderBottomLeftRadius = '0 ', 154 | borderTopLeftRadius = '30px', 155 | marginBottom = '20vh' 156 | } 157 | }) 158 | end 159 | 160 | Cashier = function(data) 161 | local total = 0 162 | local incart = 0 163 | for k,v in pairs(carts) do 164 | total += v.price 165 | incart += 1 166 | end 167 | lib.registerContext({ 168 | id = 'clothe_cashier', 169 | title = 'Shop Cashier', 170 | options = { 171 | { 172 | title = 'Pay Bills', 173 | description = '***Total:*** $ '..total..' \n ***inCart:*** '..incart, 174 | icon = 'check', 175 | onSelect = function() 176 | Config.GetSkin() 177 | local success = lib.callback.await('renzu_clothes:Pay', false, total,Config.GetSkin()) 178 | if success then 179 | Notify('Clothes Successfully paid', 'check') 180 | carts = {} 181 | oldskin = nil 182 | oldcomponent = {} 183 | else 184 | Notify('You dont have enough money', 'cross') 185 | end 186 | end, 187 | arrow = true, 188 | args = { 189 | someValue = 500 190 | } 191 | } 192 | } 193 | }) 194 | lib.showContext('clothe_cashier') 195 | end 196 | 197 | Outfits = function() 198 | local options = {} 199 | local outfits = lib.callback.await('renzu_clothes:getOutfits',false) 200 | for name,appearance in pairs(outfits) do 201 | table.insert(options,{ 202 | title = name, 203 | description = 'use outfit '..name, 204 | icon = 'tshirt', 205 | onSelect = function() 206 | lib.registerContext({ 207 | id = 'outfit_option', 208 | title = 'Outfit', 209 | menu = 'outfits', 210 | options = { 211 | { 212 | title = 'Use', 213 | description = 'Set as New Outfit', 214 | icon = 'user-check', 215 | iconColor = 'green', 216 | onSelect = function() 217 | lib.showContext('outfits') 218 | Config.SetSkin(appearance) 219 | Notify('Outfit '..name..' is used', 'check') 220 | end 221 | }, 222 | { 223 | title = 'Delete', 224 | description = 'Remove outfit from wardrobe', 225 | icon = 'user-times', 226 | iconColor = 'red', 227 | onSelect = function() 228 | local success = lib.callback.await('renzu_clothes:RemoveOutfit',false,name) 229 | Notify('Outfit '..name..' has been removed') 230 | end 231 | } 232 | } 233 | }) 234 | lib.showContext('outfit_option') 235 | end, 236 | arrow = true, 237 | }) 238 | end 239 | if #options == 0 then 240 | return Notify('Outfits is empty', 'check') 241 | end 242 | lib.registerContext({ 243 | id = 'outfits', 244 | title = 'Outfit Lists', 245 | menu = 'wardrobe', 246 | options = options 247 | }) 248 | lib.showContext('outfits') 249 | end 250 | 251 | Wardrobe = function(data) 252 | lib.registerContext({ 253 | id = 'wardrobe', 254 | title = 'Wardrobe', 255 | options = { 256 | { 257 | title = 'Save Current', 258 | description = 'Save current outfit', 259 | icon = 'save', 260 | onSelect = function() 261 | local input = lib.inputDialog('New Outfit', { 262 | {type = 'input', label = 'Outfit Name', description = 'Name of the new outfit'}, 263 | }) 264 | 265 | if not input then return end 266 | local appearance = Config.GetSkin() 267 | local success = lib.callback.await('renzu_clothes:SaveOutfit',false, {name = input[1], appearance = appearance}) 268 | 269 | if success then 270 | Notify('Outfit '..input[1]..' Saved', 'check') 271 | else 272 | Notify('Outfit '..input[1]..' is not saved', 'check') 273 | end 274 | 275 | end, 276 | arrow = true, 277 | }, 278 | { 279 | title = 'View Outfits', 280 | description = 'See all saved outfits', 281 | icon = 'list', 282 | onSelect = Outfits, 283 | arrow = true, 284 | }, 285 | { 286 | title = 'Add Outfit Code', 287 | description = 'Save outfit from generated code', 288 | icon = 'code', 289 | onSelect = function() 290 | local input = lib.inputDialog('Add Outfit Code', { 291 | {type = 'input', label = 'Outfit code', description = 'Shared outfit code'}, 292 | {type = 'input', label = 'New Outfit Name', description = 'name your outfit'}, 293 | }) 294 | 295 | if not input then return end 296 | 297 | local add = lib.callback.await('renzu_clothes:AddCode',false,input) 298 | 299 | if add then 300 | Notify('Outfit has beed saved to wardrobe') 301 | else 302 | Notify('Outfit code does not exist') 303 | end 304 | end, 305 | arrow = true, 306 | }, 307 | { 308 | title = 'Generate Code', 309 | description = 'Generate sharable code for current outfit', 310 | icon = 'qrcode', 311 | onSelect = function() 312 | local code = lib.callback.await('renzu_clothes:GenerateCode',false, Config.GetSkin()) 313 | 314 | if code then 315 | local input = lib.inputDialog('Generated Outfit Code', { 316 | {type = 'input', label = 'Outfit code', description = 'Shareable outfit code', disabled = true, default = code}, 317 | }) 318 | 319 | if not input then return end 320 | lib.setClipboard(code) 321 | Notify("Code has been Copy to Clipboard") 322 | end 323 | end, 324 | arrow = true, 325 | } 326 | } 327 | }) 328 | lib.showContext('wardrobe') 329 | end 330 | 331 | ClothingPoints = function(data) 332 | local point = lib.points.new(data.coord, 1.5, { 333 | data = data, 334 | }) 335 | 336 | function point:onEnter() 337 | Notify(self.data,data.icon or Config.Data[self.data.indexes[1]].icon) 338 | end 339 | 340 | function point:onExit() 341 | lib.hideTextUI() 342 | end 343 | 344 | function point:nearby() 345 | DrawMarker(2, self.coords.x, self.coords.y, self.coords.z, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 0.4, 0.4, 0.4, 200, 255, 241, 50, false, true, 2, nil, nil, false) 346 | 347 | if self.currentDistance < 1 and IsControlJustReleased(0, 38) then 348 | if data.type == 'cashier' then 349 | Cashier(data) 350 | elseif data.type == 'wardrobe' then 351 | Wardrobe(data) 352 | else 353 | ClothingIndex(self.data) 354 | end 355 | end 356 | end 357 | return point 358 | end 359 | 360 | local points = {} 361 | local ped = nil 362 | ClothingZones = function(data) -- check if player entered the zones and trigger all points for shop 363 | function onEnter(self) 364 | for k,point in pairs(data.clothesdisplay) do 365 | table.insert(points,ClothingPoints(point)) 366 | end 367 | table.insert(points,ClothingPoints({label = 'Cashier', coord = data.cashier, type = 'cashier', icon = 'cash-register'})) 368 | table.insert(points,ClothingPoints({label = 'Wardrobe', coord = data.wardrobe, type = 'wardrobe', icon = 'tshirt'})) 369 | 370 | local model = `s_f_y_shop_mid` 371 | lib.requestModel(model) 372 | ped = CreatePed(4, model, data.cashier_ped.x,data.cashier_ped.y,data.cashier_ped.z, false, false) 373 | TaskTurnPedToFaceEntity(ped,cache.ped,-1) 374 | SetEntityAsMissionEntity(ped, true, true) 375 | SetBlockingOfNonTemporaryEvents(ped, true) 376 | SetEntityInvincible(ped, true) 377 | 378 | Notify('Welcome to Clothing Shop','fas fa-tshirt') 379 | Wait(3000) 380 | lib.hideTextUI() 381 | end 382 | 383 | function onExit(self) 384 | for i = 1 , #points do 385 | points[i]:remove() 386 | end 387 | if DoesEntityExist(ped) then 388 | DeleteEntity(ped) 389 | end 390 | 391 | if oldskin then 392 | Config.SetSkin(oldskin) 393 | oldskin = nil 394 | carts = {} 395 | oldcomponent = {} 396 | lib.hideMenu(true) 397 | end 398 | end 399 | 400 | 401 | local box = lib.zones.box({ 402 | coords = data.coord, 403 | size = vec3(30, 30, 20), 404 | rotation = 45, 405 | debug = false, 406 | inside = inside, 407 | onEnter = onEnter, 408 | onExit = onExit 409 | }) 410 | end 411 | 412 | for k,v in pairs(Config.Shop) do 413 | 414 | ClothingZones(v) 415 | 416 | local vec = v.coord 417 | local name = v.name 418 | local blip = AddBlipForCoord(v.coord.x, v.coord.y, v.coord.z) 419 | SetBlipSprite (blip, v.blips.sprite) 420 | SetBlipDisplay(blip, 4) 421 | SetBlipScale (blip, 0.8) 422 | SetBlipColour (blip, v.blips.color) 423 | SetBlipAsShortRange(blip, true) 424 | BeginTextCommandSetBlipName('STRING') 425 | AddTextComponentSubstringPlayerName(""..v.name.."") 426 | EndTextCommandSetBlipName(blip) 427 | end 428 | 429 | function GetClotheData(componentid,drawableid,textureid,prop,name) 430 | local data = {} 431 | if prop then 432 | 433 | data = Components.Props[GetEntityModel(PlayerPedId())][tostring(componentid)] ~= nil and Components.Props[GetEntityModel(PlayerPedId())][tostring(componentid)][tostring(drawableid)] ~= nil and Components.Props[GetEntityModel(PlayerPedId())][tostring(componentid)][tostring(drawableid)][tostring(textureid)] ~= nil and Components.Props[GetEntityModel(PlayerPedId())][tostring(componentid)][tostring(drawableid)][tostring(textureid)] 434 | if name and data then 435 | data.Price = data.Price * Config.Data[name].multiplier 436 | end 437 | return data 438 | 439 | elseif not prop then 440 | 441 | data = Components.ComponentVariations[GetEntityModel(PlayerPedId())][tostring(componentid)] ~= nil and Components.ComponentVariations[GetEntityModel(PlayerPedId())][tostring(componentid)][tostring(drawableid)] ~= nil and Components.ComponentVariations[GetEntityModel(PlayerPedId())][tostring(componentid)][tostring(drawableid)][tostring(textureid)] ~= nil and Components.ComponentVariations[GetEntityModel(PlayerPedId())][tostring(componentid)][tostring(drawableid)][tostring(textureid)] 442 | if name and data then 443 | data.Price = data.Price * Config.Data[name].multiplier 444 | end 445 | 446 | end 447 | return data 448 | end 449 | 450 | function deepcopy(orig) 451 | local orig_type = type(orig) 452 | local copy 453 | if orig_type == 'table' then 454 | copy = {} 455 | for orig_key, orig_value in next, orig, nil do 456 | copy[deepcopy(orig_key)] = deepcopy(orig_value) 457 | end 458 | setmetatable(copy, deepcopy(getmetatable(orig))) 459 | else -- number, string, boolean, etc 460 | copy = orig 461 | end 462 | return copy 463 | end -------------------------------------------------------------------------------- /html/styleblue.css: -------------------------------------------------------------------------------- 1 | .leaderboard { 2 | max-width: 40vw; 3 | width: 40vw; 4 | border-radius: 12px; 5 | max-height: 70vh; 6 | overflow-x: hidden; 7 | } 8 | header { 9 | height: 11vh !important; 10 | position: absolute !important; 11 | width: 93% !important; 12 | } 13 | article { 14 | width: 99% !important; 15 | } 16 | .leaderboard header { 17 | /* --start: 20%; 18 | height: 130px; */ 19 | --start: 25%; 20 | height: 100px; 21 | background-image: repeating-radial-gradient(circle at var(--start), #613a3a0a 0%, #00000091 10%, rgb(0 0 0 / 53%) 10%, rgb(43 30 30 / 33%) 17%), linear-gradient(to right, #090b15b8, #1d1d1fbd); 22 | background: #050d23; 23 | color: #fff; 24 | position: fixed; 25 | border-radius: 12px 12px 0 0; 26 | overflow: hidden; 27 | background: rgb(93 98 103); 28 | /* background: linear-gradient(90deg, rgb(143 146 148) 0%, rgb(136 138 140) 35%, rgb(156 159 162) 100%); */ 29 | width: 40vw; 30 | z-index: 999; 31 | border-bottom-left-radius: 10px; 32 | border-bottom-right-radius: 10px; 33 | left: 2%; 34 | } 35 | .leaderboard header .leaderboard__title { 36 | position: absolute; 37 | z-index: 2; 38 | top: 50%; 39 | 40 | transform: translateY(-50%); 41 | text-transform: uppercase; 42 | margin: 0; 43 | } 44 | .leaderboard header .leaderboard__title span { 45 | display: block; 46 | margin-top: 2px; 47 | margin-right: 21px; 48 | /* position: absolute; */ 49 | } 50 | .leaderboard header .leaderboard__title--top { 51 | font-size: 24px; 52 | font-weight: 700; 53 | letter-spacing: 6.5px; 54 | } 55 | .leaderboard header .leaderboard__title--bottom { 56 | font-size: 10px; 57 | font-weight: 500; 58 | letter-spacing: 3.55px; 59 | opacity: 0.65; 60 | transform: translateY(-2px); 61 | } 62 | .leaderboard header .leaderboard__icon { 63 | fill: #fff; 64 | opacity: 0.85; 65 | width: 70px; 66 | position: absolute; 67 | top: 10%; 68 | left: 5%; 69 | /* transform: translate(-140%, -50%); */ 70 | } 71 | .leaderboard__profiles { 72 | /* background-color: #00000085; */ 73 | /* border-radius: 0 0 12px 12px; */ 74 | padding: 4px 11px 20px; 75 | display: flow-root; 76 | row-gap: 8px; 77 | /* width: 48%; */ 78 | /* float: right; */ 79 | height: 50%; 80 | margin-top: 16vh; 81 | 82 | } 83 | .leaderboard__profile { 84 | display: grid; 85 | grid-template-columns: 0.1fr 3fr 0.2fr; 86 | align-items: center; 87 | padding: 5px 9px 15px 9px; 88 | overflow: hidden; 89 | border-radius: 5px; 90 | box-shadow: 0 1px 1px 1px rgb(134 134 134 / 48%); 91 | cursor: pointer; 92 | transition: transform 0.25s cubic-bezier(0.7, 0.98, 0.86, 0.98), box-shadow 0.25s cubic-bezier(0.7, 0.98, 0.86, 0.98); 93 | background-color: #2e3033; 94 | width: 92% !important; 95 | float: left; 96 | position: relative; 97 | margin: 4px; 98 | height: 65px; 99 | direction: ltr; 100 | } 101 | .leaderboard__profile:hover { 102 | transform: scale(1.05); 103 | box-shadow: 0 9px 47px 11px rgba(51, 51, 51, 0.18); 104 | } 105 | .leaderboard__picture { 106 | /* max-width: 150%; */ 107 | width: 40px; 108 | border-radius: 50%; 109 | box-shadow: 0 0 0 8px #79797938, 0 0 0 4px #ecececa8; 110 | } 111 | .leaderboard__name { 112 | color: #d0d0d0; 113 | font-weight: 500; 114 | font-size: 1.5vh; 115 | letter-spacing: 0.64px; 116 | margin-left: 32px; 117 | } 118 | .leaderboard__value { 119 | color: #35d8ac; 120 | font-weight: 700; 121 | font-size: 15px; 122 | text-align: right; 123 | } 124 | .leaderboard__value > span { 125 | opacity: 0.8; 126 | font-weight: 600; 127 | font-size: 13px; 128 | margin-left: 3px; 129 | } 130 | 131 | body { 132 | margin: 0; 133 | background-color: #ffffff00; 134 | /* display: grid; */ 135 | /* height: 100vh; */ 136 | place-items: center; 137 | font-family: "Source Sans Pro", sans-serif; 138 | position: absolute; 139 | left: 5%; 140 | right: 0; 141 | width: 30vw; 142 | top: 15%; 143 | /* height: 100%; */ 144 | overflow: hidden; 145 | } 146 | 147 | /* The Modal (background) */ 148 | .modal { 149 | overflow: revert; 150 | display: none; 151 | position: fixed; 152 | z-index: 1; 153 | padding-top: 5vh; 154 | left: 32%; 155 | /* left: 0; */ 156 | top: 0; 157 | width: 70%; 158 | height: 100%; 159 | border-radius: 10px; 160 | } 161 | 162 | /* Modal Content */ 163 | .modal-content { 164 | position: relative; 165 | background-color: rgb(81 82 84); 166 | margin: auto; 167 | padding: 0; 168 | border: 1px solid #888; 169 | width: 55%; 170 | box-shadow: 0 4px 8px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%); 171 | -webkit-animation-name: animatetop; 172 | -webkit-animation-duration: 0.4s; 173 | animation-name: animatetop; 174 | animation-duration: 0.4s; 175 | color: #2097ff; 176 | border-radius: 20px; 177 | } 178 | 179 | /* Add Animation */ 180 | @-webkit-keyframes animatetop { 181 | from {top:-300px; opacity:0} 182 | to {top:0; opacity:1} 183 | } 184 | 185 | @keyframes animatetop { 186 | from {top:-300px; opacity:0} 187 | to {top:0; opacity:1} 188 | } 189 | 190 | /* The Close Button */ 191 | .close { 192 | color: white; 193 | float: right; 194 | font-size: 28px; 195 | font-weight: bold; 196 | } 197 | 198 | .close:hover, 199 | .close:focus { 200 | color: #000; 201 | text-decoration: none; 202 | cursor: pointer; 203 | } 204 | 205 | h2 { 206 | margin: 5px; 207 | font-size: 15px !important; 208 | } 209 | .modal-header { 210 | padding: 5px 5px; 211 | color: white; 212 | background: linear-gradient( 213 | 90deg 214 | , rgb(28 28 29) 0%, rgb(28 32 37 / 92%) 35%, rgb(66 69 72) 100%); 215 | border-radius: 20px; 216 | border-bottom-right-radius: 0; 217 | border-bottom-left-radius: 0; 218 | } 219 | 220 | .modal-body {padding: 2px 16px;} 221 | 222 | form {max-width: 99%;padding: 10px 10px;background: #27262673;border-radius: 8px;} 223 | 224 | h1 { 225 | width: 100%; 226 | font-size:20px; 227 | margin: 0 0 30px 0; 228 | text-align: center; 229 | } 230 | 231 | input[type="text"], input[type="password"], input[type="date"], input[type="datetime"], input[type="email"], input[type="number"], input[type="search"], input[type="tel"], input[type="time"], input[type="url"], textarea, select {background: rgba(255,255,255,0.1); 232 | border: none; 233 | font-size: 16px; 234 | height: 30px; 235 | margin: 0; 236 | outline: 0; 237 | padding: 8px; 238 | width: 45%; 239 | background-color: #0000003d; 240 | color: #8a97a0; 241 | box-shadow: 0 1px 0 rgb(0 0 0 / 3%) inset; 242 | margin-bottom: 30px; 243 | } 244 | button {padding: 0.6vh;color: #FFF;background-color: #505050;font-size: 11px;text-align: center;font-style: normal;border-radius: 5px;width: 100px;border: 1px solid #333333;border-width: 1px 1px 3px;box-shadow: 0 -1px 0 rgba(255,255,255,0.1) inset;margin-bottom: 1px;position: absolute;top: 122%;left: 15%;/* height: 50px; */margin-left: 2%;} 245 | 246 | 247 | body::-webkit-scrollbar { 248 | width: 2em; 249 | } 250 | 251 | body::-webkit-scrollbar-track { 252 | box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 253 | } 254 | 255 | body::-webkit-scrollbar-thumb { 256 | background-color: darkgrey; 257 | outline: 1px solid slategrey; 258 | } 259 | ::-webkit-scrollbar { 260 | width: 4px; 261 | } 262 | ::-webkit-scrollbar-thumb { 263 | border-radius: 30px; 264 | background: -webkit-gradient( 265 | linear, 266 | left top, 267 | left bottom, 268 | from(#686d71), 269 | to(#040c02) 270 | ); 271 | } 272 | 273 | .leaderboard { 274 | /* box-shadow: 0 0 40px -10px rgba(0, 0, 0, 0.4); */ 275 | } 276 | .jobname { 277 | float: left !important; 278 | position: absolute !important; 279 | left: 2.9vw !important; 280 | font-weight: 700 !important; 281 | font-size: 12px !important; 282 | top: 0.3vh !important; 283 | } 284 | .admin { 285 | float: left !important; 286 | position: absolute !important; 287 | left: 0.3vw !important; 288 | font-weight: 700 !important; 289 | font-size: 15px !important; 290 | top: 15% !important; 291 | color: rgb(10, 206, 75) !important; 292 | } 293 | 294 | .discordname { 295 | float: left; 296 | position: absolute; 297 | left: 3vw; 298 | font-weight: 700; 299 | font-size: 1vh; 300 | bottom: 0.5vh; 301 | } 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | .card-container { 318 | display: grid; 319 | grid-auto-rows: 2.7fr; 320 | grid-template-columns: repeat(auto-fill, minmax(312px, 1fr)); 321 | max-width: 1248px; 322 | margin: auto; 323 | } 324 | 325 | @media screen and (max-width: 1264px) { 326 | .card-container { 327 | max-width: 936px; 328 | } 329 | } 330 | 331 | @media screen and (max-width: 952px) { 332 | .card-container { 333 | max-width: 624px; 334 | } 335 | } 336 | 337 | .card { 338 | position: relative; 339 | color: white; 340 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, 341 | Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; 342 | border-radius: 14px; 343 | text-align: center; 344 | background: rgb(11 11 16 / 68%); 345 | display: grid; 346 | grid-template-columns: 280px; 347 | grid-template-rows: 2fr 0.7fr; 348 | width: 95%; 349 | box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.9); 350 | transition: 0.5s ease; 351 | margin: 16px; 352 | min-width: 250px; 353 | height: 500px; 354 | } 355 | 356 | @media screen and (max-width: 640px) { 357 | .card { 358 | width: 100%; 359 | grid-template-columns: repeat(auto-fill, minmax(100%, 1fr)); 360 | } 361 | 362 | .card-container { 363 | grid-template-columns: calc(100% - 32px); 364 | width: calc(100% - 32px); 365 | } 366 | } 367 | 368 | .card.light:nth-last-of-type(odd), 369 | .card.dark:nth-last-of-type(even) { 370 | --fore: white; 371 | --back: rgba(18, 18, 49, 0.987); 372 | } 373 | 374 | .card.light:nth-last-of-type(even), 375 | .card.dark:nth-last-of-type(odd) { 376 | --fore: black; 377 | --back: white; 378 | } 379 | 380 | .card .card-header { 381 | position: absolute; 382 | border-radius: 14px; 383 | top: 0; 384 | left: 0; 385 | right: 0; 386 | display: flex; 387 | justify-content: space-between; 388 | padding: 20px 16px; 389 | } 390 | 391 | .card-header i { 392 | color: var(--fore); 393 | } 394 | 395 | .card-header .options > i { 396 | margin-left: 10px; 397 | } 398 | 399 | .card .card-text { 400 | color: var(--fore); 401 | height: fit-content; 402 | width: 25vw; 403 | margin: auto; 404 | margin-top: 32px; 405 | } 406 | 407 | .card-text .card-img { 408 | position: relative; 409 | background: url("https://cultivatedculture.com/wp-content/uploads/2019/12/LinkedIn-Profile-Picture-Example-Madeline-Mann.jpeg"); 410 | height: 150px; 411 | width: 150px; 412 | background-size: cover; 413 | border-radius: 50%; 414 | margin: auto; 415 | box-shadow: 2px 5px 15px rgb(0 0 0 / 90%); 416 | } 417 | 418 | .card-img i { 419 | position: absolute; 420 | bottom: 6px; 421 | right: 4px; 422 | background: linear-gradient(90deg, #d400ff 0%, #d108a5 36%, #ff006a 100%); 423 | border-radius: 15px; 424 | } 425 | 426 | .card-text h2 { 427 | margin: 8px; 428 | margin-bottom: 2px; 429 | font-size: 18px; 430 | } 431 | 432 | .card-text h5 { 433 | margin: 0; 434 | color: grey; 435 | font-size: 8px; 436 | text-transform: uppercase; 437 | letter-spacing: 2px; 438 | } 439 | 440 | .card-text p { 441 | font-weight: 400; 442 | font-size: 11px; 443 | color: var(--fore); 444 | margin: 12px auto; 445 | width: 100%; 446 | } 447 | 448 | .card-text button { 449 | border-radius: 20px; 450 | text-transform: uppercase; 451 | border: none; 452 | padding: 10px; 453 | width: 180px; 454 | font-weight: 700; 455 | color: white; 456 | margin-top: 20px; 457 | background-image: linear-gradient( 458 | 90deg, 459 | #36a4f3 0%, 460 | #0d559e 36%, 461 | #1a5fb5 100% 462 | ); 463 | position: unset; 464 | } 465 | 466 | .card-stats { 467 | display: flex; 468 | justify-content: space-around; 469 | border-bottom-left-radius: 15px; 470 | border-bottom-right-radius: 15px; 471 | position: absolute; 472 | left: 0; 473 | right: 0; 474 | bottom: 30px; 475 | padding: 4px 16px; 476 | } 477 | 478 | .card-stats .stats { 479 | display: flex; 480 | align-items: center; 481 | justify-content: center; 482 | flex-direction: column; 483 | padding: 10px 0; 484 | color: var(--fore); 485 | } 486 | 487 | .card-stats .type { 488 | font-size: 9px; 489 | font-weight: 400; 490 | color: grey; 491 | letter-spacing: 2px; 492 | text-transform: uppercase; 493 | } 494 | 495 | .card-stats .value { 496 | font-size: 16px; 497 | font-weight: 500; 498 | } 499 | 500 | .socials i { 501 | margin-left: 10px; 502 | font-size: 24px; 503 | background-clip: text; 504 | background: -webkit-linear-gradient(0deg, #d400ff 0%, #ff006a 100%); 505 | -webkit-background-clip: text; 506 | -webkit-text-fill-color: transparent; 507 | } 508 | 509 | .card:hover { 510 | transform: scale(1.1); 511 | } 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | .container { 520 | display: flex; 521 | flex-direction: column; 522 | align-items: center; 523 | justify-content: center; 524 | width: 100%; 525 | height: 100%; 526 | } 527 | 528 | [type="radio"] { 529 | z-index: -1; 530 | position: absolute; 531 | opacity: 0; 532 | } 533 | [type="radio"]:checked ~ label { 534 | border-color: #0e80b7; 535 | background-color: rgba(97, 154, 234, 0.16); 536 | color: #FFF; 537 | border-style: unset; 538 | } 539 | [type="radio"]:checked ~ label:before { 540 | will-change: transform, border-width, border-color; 541 | -webkit-animation: bubble 1s ease-in; 542 | animation: bubble 1s ease-in; 543 | } 544 | [type="radio"]:checked ~ label:after { 545 | will-change: opacity, box-shadow; 546 | -webkit-animation: sparkles 700ms ease-in-out; 547 | animation: sparkles 700ms ease-in-out; 548 | } 549 | [type="radio"]:checked ~ label > span { 550 | will-change: transform; 551 | border: 0; 552 | background-image: linear-gradient(to top right, #6E89FF, #4363EE); 553 | -webkit-animation: radio 400ms cubic-bezier(0.17, 0.89, 0.32, 1.49); 554 | animation: radio 400ms cubic-bezier(0.17, 0.89, 0.32, 1.49); 555 | } 556 | [type="radio"]:checked ~ label > span:after { 557 | content: ""; 558 | position: absolute; 559 | top: 50%; 560 | left: 50%; 561 | transform: translate(-50%, -50%); 562 | width: 0; 563 | height: 0; 564 | border-radius: 10px; 565 | background-color: #fff; 566 | } 567 | [type="radio"]:checked ~ label .job { 568 | will-change: box-shadow; 569 | -webkit-animation: job 500ms ease-in-out forwards; 570 | animation: job 500ms ease-in-out forwards; 571 | } 572 | [type="radio"]:checked ~ label .job:after { 573 | will-change: transform; 574 | -webkit-animation: shine 500ms ease-in forwards; 575 | animation: shine 500ms ease-in forwards; 576 | -webkit-animation-delay: 100ms; 577 | animation-delay: 100ms; 578 | } 579 | 580 | label { 581 | /* background: #222; */ 582 | position: relative; 583 | display: grid; 584 | align-items: center; 585 | grid-template-columns: 20px auto 100px; 586 | grid-gap: 20px; 587 | width: 320px; 588 | height: 62px; 589 | padding: 0 20px; 590 | border-radius: 6px; 591 | border: 2px solid transparent; 592 | background-color: transparent; 593 | transition: all 300ms ease-in; 594 | font-size: 20px; 595 | text-transform: capitalize; 596 | color: #fff; 597 | } 598 | label:hover { 599 | border-color: #4062F6; 600 | background-color: rgba(97, 154, 234, 0.16); 601 | } 602 | label:before, label:after { 603 | position: absolute; 604 | left: 29px; 605 | border-radius: 50%; 606 | content: ''; 607 | } 608 | label:before { 609 | margin: -2rem; 610 | border: solid 2rem #545461; 611 | width: 4rem; 612 | height: 4rem; 613 | transform: scale(0); 614 | } 615 | label:after { 616 | margin: -0.1875rem; 617 | width: 0.375rem; 618 | height: 0.375rem; 619 | box-shadow: 0.32476rem -2.6875rem 0 -0.1875rem #ff8080, -0.32476rem -2.3125rem 0 -0.1875rem #ffed80, 2.30366rem -1.42172rem 0 -0.1875rem #ffed80, 1.6055rem -1.69573rem 0 -0.1875rem #a4ff80, 2.54785rem 0.91464rem 0 -0.1875rem #a4ff80, 2.32679rem 0.19796rem 0 -0.1875rem #80ffc8, 0.87346rem 2.56226rem 0 -0.1875rem #80ffc8, 1.29595rem 1.94258rem 0 -0.1875rem #80c8ff, -1.45866rem 2.28045rem 0 -0.1875rem #80c8ff, -0.71076rem 2.2244rem 0 -0.1875rem #a480ff, -2.69238rem 0.28141rem 0 -0.1875rem #a480ff, -2.18226rem 0.8312rem 0 -0.1875rem #ff80ed, -1.89869rem -1.92954rem 0 -0.1875rem #ff80ed, -2.01047rem -1.18791rem 0 -0.1875rem #ff8080; 620 | } 621 | label > span { 622 | position: relative; 623 | display: inline-flex; 624 | width: 20px; 625 | height: 20px; 626 | border-radius: 20px; 627 | border: 2px solid #1045a5; 628 | background: #121517; 629 | border-color: #1045a5; 630 | } 631 | 632 | .job { 633 | position: relative; 634 | width: 243px; 635 | height: 152px; 636 | padding: 22px 24px; 637 | border-radius: 16px; 638 | box-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.25); 639 | background-image: linear-gradient(45deg, #FFF, #CDCDCD); 640 | overflow: hidden; 641 | } 642 | .job:before { 643 | content: ""; 644 | position: absolute; 645 | top: 0; 646 | bottom: 0; 647 | left: 0; 648 | right: 0; 649 | background: url("") no-repeat; 650 | } 651 | .job:after { 652 | content: ""; 653 | position: absolute; 654 | top: 0; 655 | bottom: 0; 656 | width: 40px; 657 | transform: translateX(-70px); 658 | background-image: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0)); 659 | } 660 | .job--blue { 661 | background-image: linear-gradient(45deg, #748DFB, #3859E8); 662 | } 663 | .job--dark { 664 | background-image: linear-gradient(45deg, #616161, #484848); 665 | } 666 | .job--sm { 667 | position: absolute; 668 | right: -76px; 669 | transform: scale(0.24); 670 | } 671 | 672 | .text__row { 673 | display: grid; 674 | grid-template-columns: 54px 64px; 675 | grid-gap: 6px; 676 | } 677 | .text__row:last-of-type { 678 | grid-template-columns: 45px 54px; 679 | margin-top: 7px; 680 | } 681 | .text__loader { 682 | height: 13px; 683 | border-radius: 2px; 684 | background-color: rgba(0, 0, 0, 0.4); 685 | } 686 | 687 | .option:not(:last-child) { 688 | margin-bottom: 4px; 689 | } 690 | 691 | @-webkit-keyframes radio { 692 | 0%, 17.5% { 693 | transform: scale(0); 694 | } 695 | } 696 | 697 | @keyframes radio { 698 | 0%, 17.5% { 699 | transform: scale(0); 700 | } 701 | } 702 | @-webkit-keyframes job { 703 | 0% { 704 | box-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.25); 705 | transform: scale(0.24); 706 | } 707 | 45% { 708 | box-shadow: 0 12px 32px 0 rgba(0, 0, 0, 0.5); 709 | transform: scale(0.25); 710 | } 711 | 100% { 712 | box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.4); 713 | transform: scale(0.24); 714 | } 715 | } 716 | @keyframes job { 717 | 0% { 718 | box-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.25); 719 | transform: scale(0.24); 720 | } 721 | 45% { 722 | box-shadow: 0 12px 32px 0 rgba(0, 0, 0, 0.5); 723 | transform: scale(0.25); 724 | } 725 | 100% { 726 | box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.4); 727 | transform: scale(0.24); 728 | } 729 | } 730 | @-webkit-keyframes shine { 731 | from { 732 | transform: translateX(-70px) rotate(10deg); 733 | } 734 | to { 735 | transform: translateX(300px) rotate(10deg); 736 | } 737 | } 738 | @keyframes shine { 739 | from { 740 | transform: translateX(-70px) rotate(10deg); 741 | } 742 | to { 743 | transform: translateX(300px) rotate(10deg); 744 | } 745 | } 746 | @-webkit-keyframes bubble { 747 | 15% { 748 | transform: scale(1); 749 | border-color: #545461; 750 | border-width: 2rem; 751 | } 752 | 30%, 100% { 753 | transform: scale(1); 754 | border-color: #545461; 755 | border-width: 0; 756 | } 757 | } 758 | @keyframes bubble { 759 | 15% { 760 | transform: scale(1); 761 | border-color: #545461; 762 | border-width: 2rem; 763 | } 764 | 30%, 100% { 765 | transform: scale(1); 766 | border-color: #545461; 767 | border-width: 0; 768 | } 769 | } 770 | @-webkit-keyframes sparkles { 771 | 0%, 10% { 772 | opacity: 0; 773 | transform: scale(0); 774 | } 775 | 15% { 776 | opacity: 1; 777 | transform: scale(1.2) rotate(-20deg); 778 | box-shadow: 0.32476rem -2.1875rem 0 0rem #ff8080, -0.32476rem -1.8125rem 0 0rem #ffed80, 1.91274rem -1.10998rem 0 0rem #ffed80, 1.21459rem -1.38398rem 0 0rem #a4ff80, 2.06039rem 0.80338rem 0 0rem #a4ff80, 1.83932rem 0.0867rem 0 0rem #80ffc8, 0.65652rem 2.11178rem 0 0rem #80ffc8, 1.07901rem 1.4921rem 0 0rem #80c8ff, -1.24172rem 1.82996rem 0 0rem #80c8ff, -0.49382rem 1.77391rem 0 0rem #a480ff, -2.20492rem 0.17015rem 0 0rem #a480ff, -1.69479rem 0.71994rem 0 0rem #ff80ed, -1.50777rem -1.61779rem 0 0rem #ff80ed, -1.61955rem -0.87617rem 0 0rem #ff8080; 779 | } 780 | } 781 | @keyframes sparkles { 782 | 0%, 10% { 783 | opacity: 0; 784 | transform: scale(0); 785 | } 786 | 15% { 787 | opacity: 1; 788 | transform: scale(1.2) rotate(-20deg); 789 | box-shadow: 0.32476rem -2.1875rem 0 0rem #ff8080, -0.32476rem -1.8125rem 0 0rem #ffed80, 1.91274rem -1.10998rem 0 0rem #ffed80, 1.21459rem -1.38398rem 0 0rem #a4ff80, 2.06039rem 0.80338rem 0 0rem #a4ff80, 1.83932rem 0.0867rem 0 0rem #80ffc8, 0.65652rem 2.11178rem 0 0rem #80ffc8, 1.07901rem 1.4921rem 0 0rem #80c8ff, -1.24172rem 1.82996rem 0 0rem #80c8ff, -0.49382rem 1.77391rem 0 0rem #a480ff, -2.20492rem 0.17015rem 0 0rem #a480ff, -1.69479rem 0.71994rem 0 0rem #ff80ed, -1.50777rem -1.61779rem 0 0rem #ff80ed, -1.61955rem -0.87617rem 0 0rem #ff8080; 790 | } 791 | } 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | .season_tabs { 800 | position: relative; 801 | min-height: 360px; 802 | clear: both; 803 | margin: 5px 0; 804 | height: 600px; 805 | } 806 | .season_tab { 807 | float: left; 808 | clear: both; 809 | width: 8vw; 810 | /* background: #222; */ 811 | /* height: 50%; */ 812 | padding-bottom: 10px; 813 | } 814 | .season_tab label { 815 | background: #0e0e0e73; 816 | padding: 10px; 817 | border: 1px solid #ccc; 818 | margin-left: -1px; 819 | font-size: 15px; 820 | vertical-align: middle; 821 | position: relative; 822 | left: 1px; 823 | color: #fff; 824 | width: 264px; 825 | height: 22vh; 826 | display: table-cell; 827 | border-style: unset; 828 | } 829 | .season_tab [type=radio] { 830 | display: none; 831 | } 832 | .season_content { 833 | position: absolute; 834 | top: 0; 835 | left: 29%; 836 | background: #08080814; 837 | right: 0; 838 | bottom: 0; 839 | display: none; 840 | padding: 10px; 841 | /* border: 1px solid #ccc; */ 842 | height: 550px; 843 | overflow: scroll; 844 | } 845 | .season_content span { 846 | animation: 0.5s ease-out 0s 1 slideInFromTop; 847 | } 848 | [type=radio]:checked ~ label { 849 | background: #141415db; 850 | border-bottom: 2px solid #8bc34a; 851 | z-index: 2; 852 | } 853 | [type=radio]:checked ~ label ~ .season_content { 854 | z-index: 1; 855 | display: block; 856 | } 857 | 858 | /* styles */ 861 | select { 862 | /* Reset */ 863 | -webkit-appearance: none; 864 | -moz-appearance: none; 865 | appearance: none; 866 | border: 0; 867 | outline: 0; 868 | font: inherit; 869 | /* Personalize */ 870 | width: 100%; 871 | height: 3em; 872 | padding: 0 4em 0 1em; 873 | background: url(https://upload.wikimedia.org/wikipedia/commons/9/9d/Caret_down_font_awesome_whitevariation.svg) no-repeat right 0.8em center/1.4em, linear-gradient(to left, rgba(6, 79, 161, 0.747) 3em, rgba(5, 49, 131, 0.884) 3em); 874 | color: white; 875 | border-radius: 0.25em; 876 | box-shadow: 0 0 1em 0 rgba(0, 0, 0, 0.2); 877 | cursor: pointer; 878 | /*