diff --git a/client/creator.lua b/client/creator.lua new file mode 100644 index 0000000..018aa70 --- /dev/null +++ b/client/creator.lua @@ -0,0 +1,634 @@ +local newQuestion = { + id = "", + question = "", + options = {} +} + +local newOption = { + id = "", + label = "", + value = false +} + +local function ifThen(condition, ifTrue, ifFalse) + if condition then + return ifTrue + end + return ifFalse +end + +local function OnExit() + lib.callback( + "mri_Qwhitelist:saveConfig", + false, + function(result) + return true + end, + Config + ) +end + +local function ConfirmationDialog(content) + return lib.alertDialog( + { + header = "Confirmação", + content = content, + centered = true, + cancel = true, + labels = { + cancel = "Cancelar", + confirm = "Confirmar" + } + } + ) +end + +local function AddQuestion(args) + local question = table.clone(newQuestion) + if args["questionKey"] then + question = Config.Questions[args.questionKey] + end + local input = + lib.inputDialog( + "Pergunta", + { + { + type = "input", + label = "Pergunta", + default = question.question + } + } + ) + if not input then + args.callback(args) + return + end + question.question = input[1] + if args["questionKey"] then + question.id = string.format("q_%s", args.questionKey) + Config.Questions[args.questionKey] = question + else + question.id = string.format("q_%s", #questions + 1) + Config.Questions[#questions + 1] = question + end + args.callback(args) +end + +local function RemoveQuestion(args) + local question = Config.Questions[args.questionKey] + if ConfirmationDialog(string.format("Remover Pergunta: %s", question.question)) == "confirm" then + table.remove(Config.Questions, args.questionKey) + end + args.callback(args) +end + +local function AddOption(args) + local question = Config.Questions[args.questionKey] + local option = table.clone(newOption) + if args["optionKey"] then + option = question.options[args.optionKey] + end + local input = + lib.inputDialog( + "Opção", + { + { + type = "input", + label = "Opção", + default = option.label + }, + { + type = "checkbox", + label = "Correta", + checked = option.value + } + } + ) + if not input then + args.callback(args) + return + end + option.label = input[1] + option.value = input[2] + if args["optionKey"] then + option.id = string.format("%s_o_%s", question.id, args.optionKey) + question.options[args.optionKey] = option + else + option.id = string.format("%s_o_%s", question.id, #question.options + 1) + question.options[#question.options + 1] = option + end + Config.Questions[args.questionKey] = question + args.callback(args) +end + +local function RemoveOption(args) + local question = Config.Questions[args.questionKey] + local option = question.options[args.optionKey] + if ConfirmationDialog(string.format("Remover Opção: %s", option.label)) == "confirm" then + table.remove(question.options, args.optionKey) + Config.Questions[args.questionKey] = question + end + args.callback(args) +end + +local function ToggleChoice(args) + local question = Config.Questions[args.questionKey] + local option = question.options[args.optionKey] + option.value = not option.value + question.options[args.optionKey] = option + Config.Questions[args.questionKey] = question + args.callback(args) +end + +local function OptionActionMenu(args) + local question = Config.Questions[args.questionKey] + local option = question.options[args.optionKey] + local ctx = { + id = "menu_option", + menu = "menu_options", + title = "Gerenciar Opção", + description = string.format("Opção: %s, Correta: %s", option.label, option.value and "Sim" or "Não"), + onExit = OnExit, + options = { + { + title = "Remover Opção", + description = "Remover essa opção", + icon = "trash", + iconColor = ColorScheme.danger, + iconAnimation = Config.IconAnimation, + onSelect = RemoveOption, + args = { + callback = ListOptions, + questionKey = args.questionKey, + optionKey = args.optionKey + } + }, + { + title = "Alterar Opção", + icon = "tag", + description = "Alterar essa opção", + iconAnimation = Config.IconAnimation, + onSelect = AddOption, + args = { + callback = OptionActionMenu, + questionKey = args.questionKey, + optionKey = args.optionKey + } + }, + { + title = "Correta", + description = string.format("Correta: %s", option.value and "Sim" or "Não"), + icon = ifThen(option.value, "toggle-on", "toggle-off"), + iconColor = ifThen(option.value, ColorScheme.success, ColorScheme.danger), + iconAnimation = Config.IconAnimation, + onSelect = ToggleChoice, + args = { + callback = OptionActionMenu, + questionKey = args.questionKey, + optionKey = args.optionKey + } + } + } + } + + lib.registerContext(ctx) + lib.showContext(ctx.id) +end + +function ListOptions(args) + local question = Config.Questions[args.questionKey] + local ctx = { + id = "menu_options", + menu = "menu_question", + title = "Configurar Pergunta", + description = question.question, + onExit = OnExit, + options = { + { + title = "Adicionar Opção", + icon = "plus", + iconAnimation = Config.IconAnimation, + onSelect = AddOption, + args = { + callback = ListOptions, + questionKey = args.questionKey + } + } + } + } + + for key, option in ipairs(question.options) do + ctx.options[#ctx.options + 1] = { + title = option.label, + description = string.format("Correta: %s", option.value and "Sim" or "Não"), + icon = "clipboard-check", + iconAnimation = Config.IconAnimation, + onSelect = OptionActionMenu, + args = { + callback = ListOptions, + questionKey = args.questionKey, + optionKey = key + } + } + end + lib.registerContext(ctx) + lib.showContext(ctx.id) +end + +local function QuestionActionMenu(args) + local ctx = { + id = "menu_question", + menu = "menu_questions", + title = Config.Questions[args.questionKey].question, + onExit = OnExit, + options = { + { + title = "Alterar Pergunta", + icon = "comment-dots", + iconAnimation = Config.IconAnimation, + onSelect = AddQuestion, + args = { + callback = QuestionActionMenu, + questionKey = args.questionKey + } + }, + { + title = "Remover Pergunta", + icon = "trash", + iconColor = ColorScheme.danger, + iconAnimation = Config.IconAnimation, + onSelect = RemoveQuestion, + args = { + callback = ListQuestions, + questionKey = args.questionKey + } + }, + { + title = "Ver Opções", + icon = "list", + iconAnimation = Config.IconAnimation, + arrow = true, + onSelect = ListOptions, + args = { + callback = QuestionActionMenu, + questionKey = args.questionKey + } + } + } + } + lib.registerContext(ctx) + lib.showContext(ctx.id) +end + +function ListQuestions() + local ctx = { + id = "menu_questions", + menu = "menu_whitelist", + title = "Perguntas da Whitelist", + onExit = OnExit, + options = { + { + title = "Adicionar Pergunta", + description = "Adicionar uma pergunta", + icon = "plus", + iconAnimation = Config.IconAnimation, + onSelect = AddQuestion, + args = { + callback = ListQuestions + } + } + } + } + + for key, question in ipairs(Config.Questions) do + ctx.options[#ctx.options + 1] = { + title = question.question, + description = string.format("Índice: %s, Opções: %s", key, #question.options or 0), + icon = "clipboard-question", + iconAnimation = Config.IconAnimation, + arrow = true, + onSelect = QuestionActionMenu, + args = { + callback = ListQuestions, + questionKey = key + } + } + end + lib.registerContext(ctx) + lib.showContext(ctx.id) +end + +local function ToggleWhitelist(args) + Config.Enabled = not Config.Enabled + args.callback() +end + +local function SetPercent(args) + local input = + lib.inputDialog( + "Percentual de Acertos", + { + { + type = "slider", + label = "Percentual", + default = Config.Percent or 70, + min = 0, + max = 100, + step = 10 + } + } + ) + if input then + Config.Percent = input[1] + end + args.callback() +end + +local function SetPlayerLocation(args) + local result = exports.mri_Qbox:GetRayCoords() + if not result then + args.callback() + return + end + Config.SpawnCoords = result + args.callback() +end + +local function SetExamLocation(args) + local result = exports.mri_Qbox:GetRayCoords() + if not result then + args.callback() + return + end + Config.ExamCoords = result + args.callback() +end + +local function SetZoneLocation(args) + --Provavelmente fazer uma bazuca pra matar formiga + --A ideia é fazer uma função de criação de zonas + --Capaz de permitir o usuário escolher o tipo da zona e suas características + --Se for poly, usar mesmo princípio da rhd_garage + local result = exports.mri_Qbox:GetRayCoords() + if not result then + args.callback() + return + end + Config.ZoneCoords = result + args.callback() +end + +local function ConfigWhitelistLocation() + local ctx = { + id = "menu_whitelist_location", + menu = "menu_whitelist", + title = "Locais da Whitelist", + onExit = OnExit, + options = { + { + title = "Local de Spawn", + description = "Configurar o local de spawn do player", + icon = "street-view", + iconAnimation = Config.IconAnimation, + onSelect = SetPlayerLocation, + args = { + callback = ConfigWhitelistLocation + } + }, + { + title = "Local do Exame", + description = "Configurar o local do exame", + icon = "computer", + iconAnimation = Config.IconAnimation, + onSelect = SetExamLocation, + args = { + callback = ConfigWhitelistLocation + } + }, + { + title = "Zona de Exame", + description = "Configurar a zona de exame", + icon = "vector-square", + iconAnimation = Config.IconAnimation, + disabled = true, + onSelect = SetZoneLocation, + args = { + callback = ConfigWhitelistLocation + } + } + } + } + lib.registerContext(ctx) + lib.showContext(ctx.id) +end + +local function ConfigWhitelist(args) + local input = + lib.inputDialog( + "Configurações das Perguntas", + { + { + type = "number", + label = "Quantidade de Perguntas", + description = "Quantidade de perguntas que serão feitas na Whitelist.", + default = Config.MaxQuestions, + min = 1, + max = 20 + }, + { + type = "number", + label = "Quantidade de Opções", + description = "Quantidade de opções que exibidas por pergunta.", + default = Config.MaxOptions, + min = 2, + max = 5 + } + } + ) + if input then + Config.MaxQuestions = input[1] + Config.MaxOptions = input[2] + end + args.callback() +end + +local function ConfigWhitelist() + local ctx = { + id = "menu_whitelist", + menu = "menu_citizenship", + title = "Configurações da Whitelist", + onExit = OnExit, + options = { + { + title = "Ativar/Desativar", + description = string.format( + "Ativar ou desativar a Whitelist. Ativo: %s", + ifThen(Config.Enabled, "Sim", "Não") + ), + icon = ifThen(Config.Enabled, "toggle-on", "toggle-off"), + iconColor = ifThen(Config.Enabled, ColorScheme.success, ColorScheme.danger), + iconAnimation = Config.IconAnimation, + onSelect = ToggleWhitelist, + args = { + callback = ConfigWhitelist + } + }, + { + title = "Configurar Locais", + description = "Configurar os locais da Whitelist", + icon = "map-location-dot", + arrow = true, + iconAnimation = Config.IconAnimation, + onSelect = ConfigWhitelistLocation, + args = { + callback = ConfigWhitelist + } + }, + { + title = "Percentual de Acertos", + description = string.format("Percentual de acertos para liberar o player: %s", Config.Percent or 70), + icon = "percent", + iconAnimation = Config.IconAnimation, + onSelect = SetPercent, + args = { + callback = ConfigWhitelist + } + }, + -- { + -- title = "Configurar Perguntas", + -- description = "Configurar as perguntas da Whitelist", + -- icon = "list-check", + -- iconAnimation = Config.IconAnimation, + -- onSelect = ConfigQuestions, + -- args = { + -- callback = ConfigWhitelist + -- } + -- }, + { + title = "Ver Perguntas", + description = "Gerenciar as perguntas da Whitelist", + arrow = true, + icon = "list", + iconAnimation = Config.IconAnimation, + onSelect = ListQuestions + } + } + } + lib.registerContext(ctx) + lib.showContext(ctx.id) +end + +local function GetIdentifier(title) + local input = + lib.inputDialog( + title, + { + { + type = "input", + label = "Identificador", + description = "CitizenId ou Source do Jogador", + placeholder = "A0BC1234 ou 1" + } + } + ) + return input +end + +local function AddWhitelist(args) + local input = GetIdentifier("Adicionar Whitelist") + if not input then + return + end + local identifier = input[1] + if tonumber(identifier) then + identifier = tonumber(identifier) + end + if lib.callback.await("mri_Qwhitelist:addCitizenship", false, identifier) then + lib.notify({description = "Whitelist adicionada com sucesso!", type = "success"}) + end + args.callback() +end + +local function RemoveWhitelist(args) + local input = GetIdentifier("Revogar Whitelist") + if not input then + return + end + local identifier = input[1] + if tonumber(identifier) then + identifier = tonumber(identifier) + end + if lib.callback.await("mri_Qwhitelist:removeCitizenship", false, identifier) then + lib.notify({description = "Whitelist revogada com sucesso!", type = "success"}) + end + args.callback() +end + +function manageCitizenship() + local ctx = { + id = "menu_citizenship", + menu = "menu_gerencial", + title = "Gerenciamento da Whitelist", + onExit = OnExit, + options = { + { + title = "Configurar Whitelist", + description = "Configurar a Whitelist", + icon = "cog", + iconAnimation = Config.IconAnimation, + arrow = true, + onSelect = ConfigWhitelist, + args = { + callback = manageCitizenship + } + }, + { + title = "Liberar Player", + description = "Liberar um player da Whitelist", + icon = "user-plus", + disabled = not Config.Enabled, + iconAnimation = Config.IconAnimation, + onSelect = AddWhitelist, + args = { + callback = manageCitizenship + } + }, + { + title = "Revogar Whitelist", + description = "Revogar a Whitelist de um player", + icon = "user-minus", + disabled = not Config.Enabled, + iconColor = ColorScheme.danger, + iconAnimation = Config.IconAnimation, + onSelect = RemoveWhitelist, + args = { + callback = manageCitizenship + } + } + } + } + lib.registerContext(ctx) + lib.showContext(ctx.id) +end + +if GetResourceState("mri_Qbox") == "started" then + exports["mri_Qbox"]:AddManageMenu( + { + title = "Gerenciar Whitelist", + description = "Gerenciar a Whitelist", + icon = "user-lock", + iconAnimation = "fade", + arrow = true, + onSelectFunction = manageCitizenship, + onExit = OnExit + } + ) +else + lib.callback.register( + "mri_Qwhitelist:manageWhitelistMenu", + function() + manageCitizenship() + return true + end + ) +end diff --git a/client/main.lua b/client/main.lua new file mode 100644 index 0000000..3e93995 --- /dev/null +++ b/client/main.lua @@ -0,0 +1,143 @@ +ColorScheme = GlobalState.UIColors or {} +Config = {} +local examCompleted = false + +local function shuffleData(t) + local shuffled = {} + for i = #t, 1, -1 do + local rand = math.random(i) + t[i], t[rand] = t[rand], t[i] + table.insert(shuffled, t[i]) + end + return shuffled +end + +local function showDialog(type, data) + if type == "alert" then + return lib.alertDialog(data) == "confirm" + else + return lib.inputDialog(data) + end +end + +local function showAlertDialog(header, content, cancel, confirmLabel, cancelLabel) + local data = { + header = header, + content = content, + cancel = cancel or false, + centered = true, + labels = { + confirm = confirmLabel or "Confirmar", + cancel = cancelLabel or "Cancelar" + } + } + return showDialog("alert", data) +end + +local function askQuestion(question) + local options = {} + options[#options + 1] = { + type = "select", + options = shuffleData(question.options) + } + local input = lib.inputDialog(question.question, options) + if not input then + for k, v in pairs(options[1].options) do + if v.value then + return false + end + return false + end + else + return input[1] + end +end + +local function teleportPlayer(player, coords) + DoScreenFadeOut(800) + Wait(800) + SetEntityCoords(player, coords.x, coords.y, coords.z) + SetEntityHeading(player, coords.h) + DoScreenFadeIn(1000) +end + +local function beginExam() + local correctAnswers = 0 + local ped = PlayerPedId() + if ped then + if not showAlertDialog(Config.StartExamHeader, Config.StartExamContent, true, "Iniciar") then + return + end + + local score = 0 + local questions = shuffleData(Config.Questions) + for _, question in ipairs(questions) do + correctAnswers = correctAnswers + (askQuestion(question) and 1 or 0) + end + local anwserPercentage = ((100 * correctAnswers) / #Config.Questions) + if anwserPercentage >= Config.Percent then + showAlertDialog(Config.SuccessHeader, Config.SuccessContent, false, "Jogar") + lib.callback.await("mri_Qwhitelist:addCitizenship", false) + examCompleted = true + Zone:Remove() + teleportPlayer(ped, Config.CompletionCoords) + else + showAlertDialog(Config.FailedHeader, Config.FailedContent, false, "Entendi") + end + end +end + +local function escapeCitizenship() + teleportPlayer(cache.ped, Config.SpawnCoords) + lib.notify({description = Config.escapeNotify, type = "error"}) +end + +function loadCitizenship() + teleportPlayer(cache.ped, Config.SpawnCoords) + + if Config.Interaction.Type == "marker" then + Marker:LoadInteractions({callbackFunction = beginExam}) + elseif Config.Interaction.Type == "target" then + Target:LoadInteractions({callbackFunction = beginExam}) + elseif Config.Interaction.Type == "3dtext" then + Text:LoadInteractions({callbackFunction = beginExam}) + end + + local zoneData = { + name = "citizenZone", + coords = Config.citizenZone.coords, + size = Config.citizenZone.size, + rotation = Config.citizenZone.rotation, + debug = true, + onExit = escapeCitizenship + } + + Zone:Load({type = "box", zoneData = zoneData}) + lib.notify({description = Config.loadNotify, type = "info"}) +end + +local function OnPlayerLoaded() + Config = lib.callback.await("mri_Qwhitelist:getConfig", false) + if not lib.callback.await("mri_Qwhitelist:checkCitizenship", false) then + if Config.Enabled then + loadCitizenship() + end + end +end + +AddEventHandler( + "onResourceStart", + function(resourceName) + if (GetCurrentResourceName() ~= resourceName) then + return + end + OnPlayerLoaded() + end +) + +RegisterNetEvent( + "QBCore:Client:OnPlayerLoaded", + function() + OnPlayerLoaded() + end +) diff --git a/fxmanifest.lua b/fxmanifest.lua index 96ccd75..7766e8c 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -1,29 +1,30 @@ fx_version "cerulean" game "gta5" +lua54 "yes" +use_experimental_fxv2_oal "yes" -author "StevoScripts | steve" description "REPO_DESCRIPTION" +author "MRI QBOX Team" version "MRIQBOX_VERSION" +ox_lib "locale" + shared_scripts { - '@ox_lib/init.lua', - 'config.lua', + "@ox_lib/init.lua", } -client_scripts { - 'resource/client.lua', - 'resource/interactions/**.lua', +server_scripts { + "@oxmysql/lib/MySQL.lua", + "server/*.lua" } -server_scripts { - '@oxmysql/lib/MySQL.lua', - 'resource/server.lua', +client_scripts { + "interactions/*.lua", + "client/*.lua" } dependencies { - 'ox_lib', - 'oxmysql', - '/server:4500', + "ox_lib", + "oxmysql", + "/server:4500" } - -lua54 "yes" diff --git a/interactions/marker.lua b/interactions/marker.lua new file mode 100644 index 0000000..1b30bb4 --- /dev/null +++ b/interactions/marker.lua @@ -0,0 +1,62 @@ +Marker = { + LoadInteractions = function(self, data) + if Config.Interaction.Type ~= "marker" then + return + end + + local point = + lib.points.new( + { + coords = Config.ExamCoords, + distance = 8 + } + ) + + function point:nearby() + DrawMarker( + Config.Interaction.MarkerType, + Config.ExamCoords.x, + Config.ExamCoords.y, + Config.ExamCoords.z - (Config.Interaction.MarkerOnFloor and 1.3 or 0.0), + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + Config.Interaction.MarkerSize.x, + Config.Interaction.MarkerSize.y, + Config.Interaction.MarkerSize.z, + Config.Interaction.MarkerColor.r, + Config.Interaction.MarkerColor.g, + Config.Interaction.MarkerColor.b, + 200, + false, + true, + 2.0, + false, + false, + false, + false + ) + + local onScreen, _x, _y = + World3dToScreen2d(Config.ExamCoords.x, Config.ExamCoords.y, Config.ExamCoords.z + 1) + if onScreen then + SetTextScale(0.4, 0.4) + SetTextFont(4) + SetTextProportional(1) + SetTextColour(255, 255, 255, 255) + SetTextOutline() + SetTextEntry("STRING") + SetTextCentre(true) + AddTextComponentString("[~b~E~w~] " .. Config.Interaction.MarkerLabel .. "") + DrawText(_x, _y) + end + + if self.currentDistance < 3 and IsControlJustReleased(0, 38) then + data.callbackFunction() + end + end + end +} diff --git a/interactions/target.lua b/interactions/target.lua new file mode 100644 index 0000000..ed0e91f --- /dev/null +++ b/interactions/target.lua @@ -0,0 +1,35 @@ +Target = { + TargetId = nil, + LoadInteractions = function(self, data) + if Config.Interaction.Type ~= "target" then + return + end + local options = { + { + name = "mri_Qwhitelist:targetExam", + onSelect = data.callbackFunction, + icon = Config.Interaction.TargetIcon, + label = Config.Interaction.TargetLabel, + distance = Config.Interaction.TargetDistance, + }, + } + Target.TargetId = + exports.ox_target:addBoxZone( + { + coords = Config.ExamCoords, + size = Config.Interaction.TargetRadius, + rotation = 45, + options = options + } + ) + end +} + +AddEventHandler( + "onResourceStop", + function(resource) + if resource == GetCurrentResourceName() then + exports.ox_target:removeZone(Target.TargetId) + end + end +) diff --git a/interactions/text.lua b/interactions/text.lua new file mode 100644 index 0000000..b124cc1 --- /dev/null +++ b/interactions/text.lua @@ -0,0 +1,34 @@ +Text = { + LoadInteractions = function(self, data) + if Config.Interaction.Type ~= "3dtext" then + return + end + + local point = + lib.points.new( + { + coords = Config.ExamCoords, + distance = 8 + } + ) + + function point:nearby() + local onScreen, _x, _y = + World3dToScreen2d(Config.ExamCoords.x, Config.ExamCoords.y, Config.ExamCoords.z + 1) + if onScreen then + SetTextScale(0.4, 0.4) + SetTextFont(4) + SetTextProportional(1) + SetTextColour(255, 255, 255, 255) + SetTextOutline() + SetTextEntry("STRING") + SetTextCentre(true) + AddTextComponentString("[~b~E~w~] " .. Config.Interaction.MarkerLabel .. "") + DrawText(_x, _y) + end + if self.currentDistance < 3 and IsControlJustReleased(0, 38) then + data.callbackFunction() + end + end + end +} diff --git a/interactions/zone.lua b/interactions/zone.lua new file mode 100644 index 0000000..dcb32ee --- /dev/null +++ b/interactions/zone.lua @@ -0,0 +1,14 @@ +Zone = { + Zone = nil, + Load = function(self, data) + if data.type == "box" then + Zone.Zone = lib.zones.box(data.zoneData) + end + end, + Remove = function(self) + if Zone.Zone then + Zone.Zone:remove() + Zone.Zone = nil + end + end +} diff --git a/resource/interactions/marker.lua b/resource/interactions/marker.lua deleted file mode 100644 index 520598d..0000000 --- a/resource/interactions/marker.lua +++ /dev/null @@ -1,38 +0,0 @@ -local Config = lib.require('config') - -if Config.interaction.type ~= "marker" then - return -end - -function loadInteractions() - - local point = lib.points.new({ - coords = Config.examCoords, - distance = 8 - }) - - function point:nearby() - - DrawMarker(Config.interaction.markertype, Config.examCoords, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - Config.interaction.markersize.x, Config.interaction.markersize.y, Config.interaction.markersize.z, - Config.interaction.markercolor.r, Config.interaction.markercolor.g, Config.interaction.markercolor.b, 200, - false, true, 2.0, false, false, false, false) - - local onScreen, _x, _y = World3dToScreen2d(Config.examCoords.x, Config.examCoords.y, Config.examCoords.z + 1) - if onScreen then - SetTextScale(0.4, 0.4) - SetTextFont(4) - SetTextProportional(1) - SetTextColour(255, 255, 255, 255) - SetTextOutline() - SetTextEntry("STRING") - SetTextCentre(true) - AddTextComponentString("[~b~E~w~] " .. Config.interaction.markerlabel .. "") - DrawText(_x, _y) - end - - if self.currentDistance < 3 and IsControlJustReleased(0, 38) then - beginExam() - end - end -end diff --git a/resource/interactions/target.lua b/resource/interactions/target.lua deleted file mode 100644 index df1b44a..0000000 --- a/resource/interactions/target.lua +++ /dev/null @@ -1,30 +0,0 @@ -local Config = lib.require('config') - -if Config.interaction.type ~= "target" then - return -end - -local targetId -function loadInteractions() - local options = { - { - name = 'mri_Qwhitelist:targetExam', - onSelect = beginExam, - icon = Config.interaction.targeticon, - label = Config.interaction.targetlabel - }, - distance = Config.interaction.targetdistance, - } - targetId = exports.ox_target:addBoxZone({ - coords = Config.examCoords, - size = Config.interaction.targetradius, - rotation = 45, - options = options - }) -end - -AddEventHandler('onResourceStop', function(resource) - if resource == GetCurrentResourceName() then - exports.ox_target:removeZone(targetId) - end -end) diff --git a/resource/interactions/text.lua b/resource/interactions/text.lua deleted file mode 100644 index bc9679e..0000000 --- a/resource/interactions/text.lua +++ /dev/null @@ -1,31 +0,0 @@ -local Config = lib.require('config') - -if Config.interaction.type ~= "3dtext" then - return -end - -function loadInteractions() - - local point = lib.points.new({ - coords = Config.examCoords, - distance = 8 - }) - - function point:nearby() - local onScreen, _x, _y = World3dToScreen2d(Config.examCoords.x, Config.examCoords.y, Config.examCoords.z + 1) - if onScreen then - SetTextScale(0.4, 0.4) - SetTextFont(4) - SetTextProportional(1) - SetTextColour(255, 255, 255, 255) - SetTextOutline() - SetTextEntry("STRING") - SetTextCentre(true) - AddTextComponentString("[~b~E~w~] " .. Config.interaction.markerlabel .. "") - DrawText(_x, _y) - end - if self.currentDistance < 3 and IsControlJustReleased(0, 38) then - beginExam() - end - end -end diff --git a/resource/server.lua b/resource/server.lua deleted file mode 100644 index 9b0f9db..0000000 --- a/resource/server.lua +++ /dev/null @@ -1,33 +0,0 @@ -local success, result = pcall(MySQL.scalar.await, 'SELECT 1 FROM mri_qwhitelist') - -if not success then - MySQL.query([[CREATE TABLE IF NOT EXISTS `mri_qwhitelist` ( - `id` INT NOT NULL AUTO_INCREMENT, - `citizen` VARCHAR(50) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `citizen` (`citizen`) - )]]) - print('[mri_Qbox] Deployed database table for mri_qwhitelist') -end - -lib.callback.register('mri_Qwhitelist:checkCitizenship', function(source) - local randomBucket = 1000 + source - exports.qbx_core:SetPlayerBucket(source, randomBucket) - local player = exports.qbx_core:GetPlayer(source) - local citizenid = player.PlayerData.citizenid - local isCitizen = false - local row = MySQL.single.await('SELECT * FROM `mri_qwhitelist` WHERE `citizen` = ? LIMIT 1', {citizenid}) - if row then - exports.qbx_core:SetPlayerBucket(source, 0) - isCitizen = true - end - return isCitizen -end) - -lib.callback.register('mri_Qwhitelist:addCitizenship', function(source) - local player = exports.qbx_core:GetPlayer(source) - local citizenid = player.PlayerData.citizenid - local id = MySQL.insert.await('INSERT INTO `mri_qwhitelist` (citizen) VALUES (?)', {citizenid}) - exports.qbx_core:SetPlayerBucket(source, 0) - return id -end) diff --git a/config.lua b/server/config.sample.lua similarity index 59% rename from config.lua rename to server/config.sample.lua index 99226a8..b4fe733 100644 --- a/config.lua +++ b/server/config.sample.lua @@ -1,4 +1,6 @@ -return { +Config = { + Enabled = true, -- Coloque false para desativar a whitelist. + Percent = 70, -- Porcentagem de respostas corretas para liberar o player loadNotify = 'Você deve completar o exame de cidadania para jogar!', -- Notification when player loads in without completing citizenship. escapeNotify = 'Você deve completar o exame de cidadania para jogar!', -- Notification when player tries to leave citizenship office. -- Labels for Exam: @@ -11,24 +13,25 @@ return { FailedContent = 'Por favor, tente novamente.', PassingScore = 4, -- Amount of value questions required to get citizenship. NotifyType = 'ox_lib', -- Support for 'ox_lib', 'qb', 'esx', 'okok' and 'custom' to use a different type. - interaction = { - type = 'target', -- Supports 'marker' and 'target' and '3dtext' + Interaction = { + Type = 'target', -- Supports 'marker' and 'target' and '3dtext' - markerlabel = 'Comece o exame de cidadania', - markertype = 27, -- https://docs.fivem.net/docs/game-references/markers/ - markercolor = { r = 26, g = 115, b = 179}, -- RGB Color picker: https://g.co/kgs/npUqed1 - markersize = { x = 1.0, y = 1.0, z = 1.0}, + MarkerLabel = 'Comece o exame de cidadania', + MarkerType = 27, -- https://docs.fivem.net/docs/game-references/markers/ + MarkerColor = { r = 26, g = 115, b = 179}, -- RGB Color picker: https://g.co/kgs/npUqed1 + MarkerSize = { x = 1.0, y = 1.0, z = 1.0}, + MarkerOnFloor = false, - targeticon = 'fas fa-passport', -- https://fontawesome.com/icons - targetlabel = 'Comece o exame de cidadania', - targetradius = vector3(4, 4, 4), - targerdistance = 2.0, + TargetIcon = 'fas fa-passport', -- https://fontawesome.com/icons + TargetLabel = 'Comece o exame de cidadania', + TargetRadius = vector3(4, 4, 4), + TargetDistance = 2.0, }, -- DO NOT MODIFY UNLESS YOU ARE GOING TO MODIFY citizenZone. - spawnCoords = vec4(-66.24, -822.09, 285.61 -1, 78.8), - examCoords = vec3(-68.24, -814.40, 285.35), -- vec3(-1372.2820, -465.5251, 71.8305) - completionCoords = vec4(-1042.68, -2745.97, 21.36, 323.7), + SpawnCoords = vec4(-66.24, -822.09, 285.61 -1, 78.8), + ExamCoords = vec3(-68.24, -814.40, 285.35), -- vec3(-1372.2820, -465.5251, 71.8305) + CompletionCoords = vec4(-1042.68, -2745.97, 21.36, 323.7), citizenZone = { -- Can be created through /zone box coords = vec3(-73.34, -821.15, 285.0), @@ -38,61 +41,57 @@ return { Questions = { { - title = 'O que é Meta Gaming?', - allowCancel = false, + question = 'O que é Meta Gaming?', options = { {label = 'Metagaming é o uso de qualquer informação que seu personagem não aprendeu dentro do roleplay na cidade.', value = true}, - {label = 'Metagaming é quando você tenta vender pés de galinha para as pessoas e você não tem nenhum pé de galinha.', value = 1}, - {label = 'Eu não sei.', value = 2}, - {label = 'Metagaming é quando você não teme pela sua vida.', value = 3} + {label = 'Metagaming é quando você tenta vender pés de galinha para as pessoas e você não tem nenhum pé de galinha.', value = false}, + {label = 'Eu não sei.', value = false}, + {label = 'Metagaming é quando você não teme pela sua vida.', value = false} } }, { - title = 'O que é Power Gaming?', + question = 'O que é Power Gaming?', options = { {label = 'Powergaming é o uso de formas de roleplay irreais ou a recusa total de fazer roleplay para se dar uma vantagem injusta.', value = true}, - {label = 'Powergaming é o uso do cartão de crédito da sua mãe para comprar Fundador Suporte ;)', value = 1}, - {label = 'Powergaming é quando você invade o clube de alguém usando exploits.', value = 2}, - {label = 'Eu não sei.', value = 3} + {label = 'Powergaming é o uso do cartão de crédito da sua mãe para comprar Fundador Suporte ;)', value = false}, + {label = 'Powergaming é quando você invade o clube de alguém usando exploits.', value = false}, + {label = 'Eu não sei.', value = false} } }, { - title = 'Você pode usar software de trapaça de terceiros?', + question = 'Você pode usar software de trapaça de terceiros?', options = { {label = 'Isso não é permitido sob nenhuma circunstância.', value = true}, - {label = 'Sim, claro, eu adoro eulen!', value = 1}, - {label = 'Somente se você pedir permissão para sua mãe.', value = 2}, - {label = 'Eu não sei.', value = 3} + {label = 'Sim, claro, eu adoro eulen!', value = false}, + {label = 'Somente se você pedir permissão para sua mãe.', value = false}, + {label = 'Eu não sei.', value = false} } }, { - title = 'Qual dos exemplos abaixo é uma Zona Verde?', - allowCancel = false, + question = 'Qual dos exemplos abaixo é uma Zona Verde?', options = { {label = 'Hospitais.', value = true}, - {label = 'Bancos de parque.', value = 1}, - {label = 'Em todos os lugares.', value = 2}, - {label = 'Todos os itens acima', value = 3} + {label = 'Bancos de parque.', value = false}, + {label = 'Em todos os lugares.', value = false}, + {label = 'Todos os itens acima', value = false} } }, { - title = 'O que significa quebrar o personagem?', - allowCancel = false, + question = 'O que significa quebrar o personagem?', options = { {label = 'Quando você fala fora do personagem dentro da cidade.', value = true}, - {label = 'Quando você quebra o personagem de outro jogador.', value = 1}, - {label = 'Quando seu tio não vem te buscar na escola.', value = 2}, - {label = 'Eu não sei.', value = 3} + {label = 'Quando você quebra o personagem de outro jogador.', value = false}, + {label = 'Quando seu tio não vem te buscar na escola.', value = false}, + {label = 'Eu não sei.', value = false} } }, { - title = 'Qual destes exemplos é da Regra de Morte Aleatória?', - allowCancel = false, + question = 'Qual destes exemplos é da Regra de Morte Aleatória?', options = { {label = 'Você não pode atacar outro jogador aleatoriamente sem primeiro se envolver em algum tipo de RP verbal.', value = true}, - {label = 'Você pode matar outros jogadores sem motivo.', value = 1}, - {label = 'Você não pode comprar água a menos que seja um apoiador do servidor.', value = 2}, - {label = 'Eu não sei.', value = 3} + {label = 'Você pode matar outros jogadores sem motivo.', value = false}, + {label = 'Você não pode comprar água a menos que seja um apoiador do servidor.', value = false}, + {label = 'Eu não sei.', value = false} } } }, diff --git a/server/main.lua b/server/main.lua new file mode 100644 index 0000000..fc0154f --- /dev/null +++ b/server/main.lua @@ -0,0 +1,162 @@ +local function GetPlayer(identifier, notifySource) + if tonumber(identifier) then + return exports.qbx_core:GetPlayer(identifier) + else + return exports.qbx_core:GetPlayerByCitizenId(identifier) + end + lib.notify(notifySource, {description = "Jogador nao encontrado.", type = "error", duration = 5000}) + return false +end + +local function AddCitizenship(citizenId, identifier) + local status, err = pcall(MySQL.insert.await, "INSERT INTO `mri_qwhitelist` (citizen) VALUES (?)", {citizenId}) + + if status then + return true + end + print(string.format("identifier: %s", identifier)) + print(err) + return false +end + +local function RemoveCitizenship(citizenId, identifier) + local status, err = pcall(MySQL.update.await, "DELETE FROM `mri_qwhitelist` WHERE `citizen` = ?", {citizenId}) + + if status then + return true + end + print(string.format("identifier: %s", identifier)) + print(err) + return false +end + +local function GetConfig() + local SELECT_DATA = "SELECT * FROM mri_qwhitelistcfg" + local result = MySQL.Sync.fetchAll(SELECT_DATA, {}) + if not result or #result == 0 then + return false + end + Config = json.decode(result[1].config) +end + +local function SetConfig(data) + local INSER_DATA = "INSERT INTO `mri_qwhitelistcfg` (id, config) VALUES (?, ?) ON DUPLICATE KEY UPDATE `config` = ?" + local result = MySQL.Sync.execute(INSER_DATA, {1, data, data}) + print(json.encode(result)) +end + +local function SetPlayerBucket(target, bucket) + exports.qbx_core:SetPlayerBucket(target, bucket) +end + +local function Initialize() + + local success, result = pcall(MySQL.scalar.await, "SELECT 1 FROM mri_qwhitelist") + + if not success then + MySQL.query( + [[CREATE TABLE IF NOT EXISTS `mri_qwhitelist` ( + `id` INT NOT NULL AUTO_INCREMENT, + `citizen` VARCHAR(50) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `citizen` (`citizen`) + )]] + ) + print("[mri_Qbox] Deployed database table for mri_qwhitelist") + end + + success, result = pcall(MySQL.scalar.await, "SELECT 1 FROM mri_qwhitelistcfg") + + if not success then + MySQL.query.await( + [[CREATE TABLE IF NOT EXISTS `mri_qwhitelistcfg` ( + `id` INT NOT NULL AUTO_INCREMENT, + `config` LONGTEXT NOT NULL, + PRIMARY KEY (`id`) + )]] + ) + print("[mri_Qbox] Deployed database table for mri_qwhitelistcfg") + end + + GetConfig() +end + +AddEventHandler('onResourceStart', function(resource) + if resource == GetCurrentResourceName() then + Initialize() + end +end) + +lib.callback.register( + "mri_Qwhitelist:getConfig", + function(source) + return Config + end +) + +lib.callback.register( + "mri_Qwhitelist:saveConfig", + function(source, data) + SetConfig(json.encode(data)) + Config = data + print("[mri_Qwhitelist] Config saved") + return true + end +) + +lib.callback.register( + "mri_Qwhitelist:checkCitizenship", + function(source) + local playerBucket = 1000 + source + exports.qbx_core:SetPlayerBucket(source, playerBucket) + local player = exports.qbx_core:GetPlayer(source) + local citizenid = player.PlayerData.citizenid + local row = MySQL.single.await("SELECT * FROM `mri_qwhitelist` WHERE `citizen` = ? LIMIT 1", {citizenid}) + if row then + exports.qbx_core:SetPlayerBucket(source, 0) + return true + end + return false + end +) + +lib.callback.register( + "mri_Qwhitelist:addCitizenship", + function(source, identifier) + + local player = GetPlayer(identifier or source, identifier and source or nil) + if not player then + return false + end + + local status = AddCitizenship(player.PlayerData.citizenid, identifier or source) + if not status then + if identifier then + lib.notify(source, {description = "Erro ao liberar player, verifique o console para mais informações.", type = "error", duration = 5000}) + end + return status + end + + SetPlayerBucket(player.PlayerData.source, 0) + return status + end +) + +lib.callback.register( + "mri_Qwhitelist:removeCitizenship", + function(source, identifier) + + local player = GetPlayer(identifier) + if not player then + return false + end + print('has player') + + local status = RemoveCitizenship(player.PlayerData.citizenid, identifier) + if not status then + lib.notify(source, {description = "Erro ao revogar whitelist, verifique o console para mais informações.", type = "error", duration = 5000}) + end + + return status + end +)