const resName = GetParentResourceName(); let hasDoorsCreator = null; // editing this is useless, don't do it /* Forms stuff */ var forms = document.querySelectorAll('.needs-validation') // Loop over them and prevent submission Array.prototype.slice.call(forms) .forEach(function (form) { form.addEventListener('submit', function (event) { event.preventDefault(); form.classList.add('was-validated') }, false) }) async function getJobLabel(jobName) { return new Promise((resolve, reject) => { $.post(`https://${resName}/getJobLabel`, JSON.stringify({jobName: jobName}), function(jobLabel) { resolve(jobLabel); }) }) } /* Color stuff */ function componentToHex(c) { var hex = c.toString(16); return hex.length == 1 ? "0" + hex : hex; } function rgbToHex(r, g, b) { return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); } function hexToRgb(hex) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; } /* Language stuff */ let TRANSLATIONS = {}; let ENGLISH_TRANSLATIONS = {}; function translateEverything() { $("body").find("[data-translation-id], [data-bs-toggle='tooltip']").each(function() { let translationId = $(this).data("translationId") if( $(this).data("bsToggle") == "tooltip" ) { $(this).prop("title", getLocalizedText(translationId)); $(this).tooltip(); } else { $(this).prop("innerHTML", getLocalizedText(translationId)); } }) } async function refreshTranslations(locale) { let rawEnglishTranslations = await $.get("menu_translations/en.json"); ENGLISH_TRANSLATIONS = typeof rawEnglishTranslations == "object" ? rawEnglishTranslations : JSON.parse(rawEnglishTranslations); let rawTranslations = await $.get(`menu_translations/${locale}.json`); TRANSLATIONS = typeof rawTranslations == "object" ? rawTranslations : JSON.parse(rawTranslations); translateEverything(); } async function loadTranslations() { const locale = await $.post(`https://${resName}/getLocale`); refreshTranslations(locale); } loadTranslations(); function getLocalizedText(text) { return TRANSLATIONS[text] || ENGLISH_TRANSLATIONS[text] || text; } /* Utils */ function getFramework() { return new Promise((resolve) => { $.post(`https://${resName}/getFramework`, {}, (framework) => { resolve(framework) }) }) } async function getCurrentCoords() { return new Promise((resolve, reject) => { $.post(`https://${resName}/getCurrentCoords`, {}, function(coords) { resolve(coords); }) }); } async function getCurrentCoordsAndHeading() { return new Promise((resolve, reject) => { $.post(`https://${resName}/getCurrentCoordsAndHeading`, {}, function(data) { resolve(data); }) }); } // Open/Close menu function openMenu(version, fullConfig) { $("#races-creator-version").text(version); loadRaces(); loadSettings(fullConfig); $("#races-creator").show() } function closeMenu() { // Resets current active tab $("#races-creator").find(".nav-link, .tab-pane").each(function() { if($(this).data("isDefault") == "1") { $(this).addClass(["active", "show"]) } else { $(this).removeClass(["active", "show"]) } }) $("#races-creator").hide(); $.post(`https://${resName}/close`, {}) } $("#close-main-btn").click(closeMenu); // Messages received by client window.addEventListener('message', (event) => { let data = event.data; let action = data.action; switch(action) { case "openMenu": { openMenu(data.version, data.fullConfig); break; } case "showRaceHUD": { $("#race-hud").show(); $("#app").show(); break; } case "hideRaceHUD": { $("#race-hud").hide(); $("#app").hide(); break; } case "updateRacePositionHUD": { $("#race-hud-position").text(data.position); break; } case "updateRaceTimeHUD": { $("#race-hud-time").text(data.time); break; } case "updateRaceCheckpointHUD": { $("#race-hud-checkpoint").text(data.checkpoint); break; } case "updateRaceLapHUD": { $("#race-hud-lap").text(data.lap); break; } } }) /* ███████ ███████ ████████ ████████ ██ ███ ██ ██████ ███████ ██ ██ ██ ██ ██ ████ ██ ██ ██ ███████ █████ ██ ██ ██ ██ ██ ██ ██ ███ ███████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███████ ███████ ██ ██ ██ ██ ████ ██████ ███████ */ /* Discord logs */ function toggleDiscordLogsInSettings(enable) { $("#settings-mainDiscordWebhook").prop("disabled", !enable); $("#settings-mainDiscordWebhook").prop("required", enable); $("#settings-specific-webooks-div").find(`.form-control`).prop("disabled", !enable); } $("#settings-areDiscordLogsActive").change(function() { let enabled = $(this).prop("checked"); toggleDiscordLogsInSettings(enabled); }) function getSeparatedDiscordWebhooks() { let webhooks = {}; $("#settings-specific-webooks-div").find(".form-control").each(function(index, element) { let logType = $(element).data("logType"); let webhook = $(element).val(); if(webhook) { webhooks[logType] = webhook; } }); return webhooks; } /* Discord logs END */ $("#settings-customize-checkpoint-blip-btn").click(async function() { const oldBlipData = $(this).data("blipData"); const newBlipData = await blipDialog(oldBlipData); $(this).data("blipData", newBlipData); }) async function getRaceLabel(raceId) { return new Promise((resolve, reject) => { $.post(`https://${resName}/getRaceLabel`, JSON.stringify({raceId: raceId}), (label) => { resolve(label); }); }); } async function addRaceLeaderboard(raceId, webhook) { let div = $(`
${getLocalizedText("menu:race")} ${getLocalizedText("menu:webhook")}
`); div.find(".choose-race-btn").click(async function() { const raceId = await racesDialog(); if(raceId) { div.data("raceId", raceId); const raceLabel = await getRaceLabel(raceId); div.find(".race-label").val(raceLabel); } }); div.find(".delete-element-btn").click(function() { div.remove(); }); div.find(".webhook-input").tooltip(); if(raceId) { div.data("raceId", raceId); const raceLabel = await getRaceLabel(raceId); if(!raceLabel) return; // The race was deleted div.find(".race-label").val(raceLabel); div.find(".webhook-input").val(webhook); } $("#settings-leaderboard-list").append(div) } $("#settings-add-race-leaderboard-btn").click(function() { addRaceLeaderboard(); }) function getAllDiscordLeaderboards() { let leaderboards = {}; $("#settings-leaderboard-list").find(".leaderboard-element").each(function(index, element) { let raceId = $(element).data("raceId"); let webhook = $(element).find(".webhook-input").val(); if(raceId && webhook) { leaderboards[raceId] = webhook; } }); return leaderboards; } $("#settings-enable-players-race-command").change(function() { const enabled = $(this).prop("checked"); $("#settings-players-race-command").prop("disabled", !enabled); }) $("#settings-players-race-allowed-jobs-btn").click(async function() { const oldAllowedJobs = $(this).data("allowedJobs"); const newAllowedJobs = await jobsDialog(oldAllowedJobs); $(this).data("allowedJobs", newAllowedJobs); }); function loadSettings(fullConfig) { // Language $("#settings-locale").val(fullConfig.locale); // Generic $("#settings-ace-permission").val(fullConfig.acePermission); $("#settings-enable-players-race-command").prop("checked", fullConfig.enablePlayersRaceCommand).change(); $("#settings-players-race-command").val(fullConfig.playersRaceCommand); $("#settings-players-race-allowed-jobs-btn").data("allowedJobs", fullConfig.playersRaceAllowedJobs); $("#settings-enable-boost-on-start").prop("checked", fullConfig.enableBoostOnStart); $("#settings-customize-checkpoint-blip-btn").data("blipData", fullConfig.checkpointBlipCustomization); $("#settings-enable-checkpoint-visual-effect").prop("checked", fullConfig.checkpointEffects.visual); $("#settings-enable-checkpoint-sound-effect").prop("checked", fullConfig.checkpointEffects.sound); $("#settings-seconds-before-kick-from-race").val(fullConfig.secondsBeforeKickFromRace); $("#settings-use-character-name-instead-of-nickname").prop("checked", fullConfig.useCharacterNameInsteadOfNickname); $("#settings-can-restart-from-checkpoint").prop("checked", fullConfig.canRestartFromCheckpoint); // Leaderboards $("#settings-leaderboard-list").empty(); for(const [raceId, webhook] of Object.entries(fullConfig.discordLeaderboards || {})) { addRaceLeaderboard(raceId, webhook); } // Discord logs $("#settings-areDiscordLogsActive").prop("checked", fullConfig.areDiscordLogsActive); $("#settings-mainDiscordWebhook").val(fullConfig.mainDiscordWebhook); toggleDiscordLogsInSettings(fullConfig.areDiscordLogsActive); for(const[logType, webhook] of Object.entries(fullConfig.specificWebhooks)) { $("#settings-specific-webooks-div").find(`[data-log-type="${logType}"]`).val(webhook); } // Discord logs - END } $("#settings").submit(function(event) { if (!this.checkValidity()) { event.preventDefault(); event.stopPropagation(); return; } else { $(this).removeClass("was-validated"); } let clientSettings = { // Generic enablePlayersRaceCommand: $("#settings-enable-players-race-command").prop("checked"), playersRaceCommand: $("#settings-players-race-command").val(), playersRaceAllowedJobs: $("#settings-players-race-allowed-jobs-btn").data("allowedJobs"), enableBoostOnStart: $("#settings-enable-boost-on-start").prop("checked"), checkpointBlipCustomization: $("#settings-customize-checkpoint-blip-btn").data("blipData"), checkpointEffects: { visual: $("#settings-enable-checkpoint-visual-effect").prop("checked"), sound: $("#settings-enable-checkpoint-sound-effect").prop("checked"), }, secondsBeforeKickFromRace: $("#settings-seconds-before-kick-from-race").val(), canRestartFromCheckpoint: $("#settings-can-restart-from-checkpoint").prop("checked"), } let sharedSettings = { locale: $("#settings-locale").val(), } let serverSettings = { // Generic acePermission: $("#settings-ace-permission").val(), useCharacterNameInsteadOfNickname: $("#settings-use-character-name-instead-of-nickname").prop("checked"), // Discord logs areDiscordLogsActive: $("#settings-areDiscordLogsActive").prop("checked"), mainDiscordWebhook: $("#settings-mainDiscordWebhook").val(), specificWebhooks: getSeparatedDiscordWebhooks(), // Leaderboards discordLeaderboards: getAllDiscordLeaderboards() } $.post(`https://${resName}/saveSettings`, JSON.stringify({ clientSettings: clientSettings, sharedSettings: sharedSettings, serverSettings: serverSettings, })); refreshTranslations(sharedSettings.locale); }); /* ██████ █████ ██████ ███████ ███████ ██ ██ ██ ██ ██ ██ ██ ██████ ███████ ██ █████ ███████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██████ ███████ ███████ */ let racesDatatable = $("#races-container").DataTable( { "lengthMenu": [10, 15, 20], "createdRow": function ( row, data, index ) { $(row).addClass("clickable"); $(row).click(function() { let id = parseInt( data[0] ); editRace(id); }) }, }); let races = {}; function loadRaces() { $.post(`https://${resName}/getAllRaces`, {}, async function(rawRaces) { // Manually create the table to avoid incompatibilities due table indexing races = {}; for(const[k, raceData] of Object.entries(rawRaces)) { races[raceData.id] = raceData; } racesDatatable.clear(); for(const[id, raceData] of Object.entries(races)) { if(raceData.identifier != "admin") continue; racesDatatable.row.add([ id, raceData.label ]); } racesDatatable.draw(); }) } function setDefaultDataOfRace() { let raceModal = $("#race-modal"); // Generic $("#race-label").val("Default"); $("#race-minimum-police").val(0); $("#race-minimum-players").val(2); $("#race-time-limit").val(60); $("#race-laps").val(1); $("#race-show-map-path").prop("checked", true); $("#race-alert-police-on-start").prop("checked", false); $("#race-is-arcade").prop("checked", false).change(); setArcadeEffects(); // Set all to 0 // Starting zone $("#race-starting-zone-x").val(""); $("#race-starting-zone-y").val(""); $("#race-starting-zone-z").val(""); $("#race-starting-zone-size").val(""); // Prize $("#race-entrance-fee").val(100); raceModal.find("input:radio[name='prize-distribution'][value='winner-takes-all']").prop("checked", true).change(); $("#race-entrance-fee-account").val("bank"); // Checkpoints $("#race-checkpoints-list").empty(); // Other ? raceModal.data("blipData", getDefaultBlipCustomization()); raceModal.data("markerData", getDefaultMarkerCustomization()); raceModal.data("allowedJobs", null); raceModal.data("allowedVehicleClasses", null); } $("#new-race-btn").click(function() { let raceModal = $("#race-modal"); // Converts from edit modal to create modal raceModal.data("action", "create"); $("#delete-race-btn").hide(); $("#save-race-btn").text( getLocalizedText("menu:create") ); setDefaultDataOfRace(); raceModal.modal("show"); }); $("#race-customize-blip-btn").click(async function() { let raceModal = $("#race-modal"); const oldBlipData = raceModal.data("blipData"); const newBlipData = await blipDialog(oldBlipData); raceModal.data("blipData", newBlipData); }); $("#race-customize-marker-btn").click(async function() { let raceModal = $("#race-modal"); const oldMarkerData = raceModal.data("markerData"); const newMarkerData = await markerDialog(oldMarkerData); raceModal.data("markerData", newMarkerData); }); $("#race-allowed-jobs-btn").click(async function() { let raceModal = $("#race-modal"); const oldAllowedJobs = raceModal.data("allowedJobs"); const newAllowedJobs = await jobsDialog(oldAllowedJobs); raceModal.data("allowedJobs", newAllowedJobs); }); $("#race-allowed-vehicle-classes-btn").click(async function() { let raceModal = $("#race-modal"); const oldAllowedVehicleClasses = raceModal.data("allowedVehicleClasses"); const newAllowedVehicleClasses = await vehicleClassesDialog(oldAllowedVehicleClasses); raceModal.data("allowedVehicleClasses", newAllowedVehicleClasses); }); $("#race-choose-entrance-fee-account-btn").click(async function() { const accountName = await accountsDialog(); if(accountName) { $("#race-entrance-fee-account").val(accountName); } }) function editRace(id) { let raceModal = $("#race-modal"); // Converts from create modal to edit modal raceModal.data("action", "edit"); raceModal.data("raceId", id); $("#delete-race-btn").show(); $("#save-race-btn").text( getLocalizedText("menu:save") ); const raceInfo = races[id]; const raceData = raceInfo.data; // Generic $("#race-label").val(raceInfo.label); $("#race-minimum-police").val(raceData.minimumPolice); $("#race-minimum-players").val(raceData.minimumPlayers); $("#race-time-limit").val(raceData.timeLimit); $("#race-laps").val(raceData.laps); $("#race-alert-police-on-start").prop("checked", raceData.alertPoliceOnStart); $("#race-show-map-path").prop("checked", raceData.showMapPath); $("#race-is-arcade").prop("checked", raceData.isArcade).change(); setArcadeEffects(raceData.arcadeEffects); raceModal.data("blipData", raceData.blipData); raceModal.data("markerData", raceData.markerData); raceModal.data("allowedJobs", raceData.allowedJobs); raceModal.data("allowedVehicleClasses", raceData.allowedVehicleClasses); // Starting zone $("#race-starting-zone-x").val(raceData.startingZone.coords.x); $("#race-starting-zone-y").val(raceData.startingZone.coords.y); $("#race-starting-zone-z").val(raceData.startingZone.coords.z); $("#race-starting-zone-size").val(raceData.startingZone.size); // Prize $("#race-entrance-fee").val(raceData.entranceFee); raceModal.find("input:radio[name='prize-distribution'][value='" + raceData.prizeDistribution + "']").prop("checked", true).change(); $("#race-entrance-fee-account").val(raceData.entranceFeeAccount); // Checkpoints $("#race-checkpoints-list").empty(); for(const checkpoint of raceData.checkpoints) { addCheckpointInRace(checkpoint); } raceModal.modal("show"); } $("#race-choose-starting-zone-btn").click(function() { $("html").hide(); $.post(`https://${resName}/chooseStartingZone`, {}, function(startingZone) { if(startingZone) { $("#race-starting-zone-x").val(startingZone.coords.x); $("#race-starting-zone-y").val(startingZone.coords.y); $("#race-starting-zone-z").val(startingZone.coords.z); $("#race-starting-zone-size").val(startingZone.size); } $("html").show(); }) }) function addCheckpointInRace(checkpoint) { const checkpointIndex = $("#race-checkpoints-list").children().length + 1; let checkpointDiv = $(`

${checkpointIndex}

`); checkpointDiv.find(".checkpoint-current-coords-btn").click(async function() { const coords = await getCurrentCoords(); checkpointDiv.find(".checkpoint-x").val(coords.x); checkpointDiv.find(".checkpoint-y").val(coords.y); checkpointDiv.find(".checkpoint-z").val(coords.z); }) checkpointDiv.find(".delete-checkpoint-btn").click(function() { checkpointDiv.remove(); // Update checkpoint indexes $("#race-checkpoints-list").children().each(function(index) { $(this).find(".checkpoint-index").text(index + 1); }); }); if(checkpoint) { checkpointDiv.find(".checkpoint-x").val(checkpoint.coords.x); checkpointDiv.find(".checkpoint-y").val(checkpoint.coords.y); checkpointDiv.find(".checkpoint-z").val(checkpoint.coords.z); checkpointDiv.find(".checkpoint-size").val(checkpoint.size); } $("#race-checkpoints-list").append(checkpointDiv); } $("#race-add-checkpoint").click(function() { // If there are less than 99 checkpoints, add a new one if($("#race-checkpoints-list").children().length < 99) { addCheckpointInRace(); } }); function getRaceCheckpoints() { let checkpoints = []; $("#race-checkpoints-list").children().each(function() { checkpoints.push({ coords: { x: parseFloat( $(this).find(".checkpoint-x").val() ), y: parseFloat( $(this).find(".checkpoint-y").val() ), z: parseFloat( $(this).find(".checkpoint-z").val() ) }, size: parseFloat( $(this).find(".checkpoint-size").val() ) }) }); return checkpoints; } $("#race-setup-all-checkpoints").click(function() { $("html").hide(); $.post(`https://${resName}/setupAllCheckpoints`, JSON.stringify({oldCheckpoints: getRaceCheckpoints()}), function(checkpoints) { if(checkpoints) { $("#race-checkpoints-list").empty(); for(const checkpoint of checkpoints) { addCheckpointInRace(checkpoint); } } $("html").show(); }) }) $("#race-is-arcade").change(function() { const enabled = $(this).prop("checked"); $("#race-arcade-probabilities-div").toggle(enabled); }) function getArcadeEffects() { let effects = []; $("#race-arcade-probabilities-div").find("input").each(function() { effects.push({ name: $(this).data("effectName"), probability: parseInt( $(this).val() ) || 0 }) }); return effects; } function setArcadeEffects(effects) { if(effects) { for(const effectData of effects) { $("#race-arcade-probabilities-div").find(`input[data-effect-name="${effectData.name}"]`).val(effectData.probability); } } else { $("#race-arcade-probabilities-div").find("input").val(0); } } $("#race-form").submit(function(event) { if (!this.checkValidity()) { event.preventDefault(); event.stopPropagation(); return; } else { $(this).removeClass("was-validated"); } let raceModal = $("#race-modal"); let action = raceModal.data("action"); let raceData = { label: $("#race-label").val(), data: { allowedJobs: raceModal.data("allowedJobs"), blipData: raceModal.data("blipData"), markerData: raceModal.data("markerData"), allowedVehicleClasses: raceModal.data("allowedVehicleClasses"), minimumPolice: parseInt( $("#race-minimum-police").val() ), minimumPlayers: parseInt( $("#race-minimum-players").val() ), timeLimit: parseInt( $("#race-time-limit").val() ), laps: parseInt( $("#race-laps").val() ), alertPoliceOnStart: $("#race-alert-police-on-start").prop("checked"), showMapPath: $("#race-show-map-path").prop("checked"), isArcade: $("#race-is-arcade").prop("checked"), arcadeEffects: getArcadeEffects(), entranceFee: parseInt( $("#race-entrance-fee").val() ), entranceFeeAccount: $("#race-entrance-fee-account").val(), prizeDistribution: $("input:radio[name='prize-distribution']:checked").val(), startingZone: { coords: { x: parseFloat( $("#race-starting-zone-x").val() ), y: parseFloat( $("#race-starting-zone-y").val() ), z: parseFloat( $("#race-starting-zone-z").val() ) }, size: parseFloat( $("#race-starting-zone-size").val() ) }, checkpoints: getRaceCheckpoints() } } switch(action) { case "create": { $.post(`https://${resName}/createRace`, JSON.stringify(raceData), function(isSuccessful) { if(isSuccessful) { raceModal.modal("hide"); loadRaces(); } }); break; } case "edit": { $.post(`https://${resName}/updateRace`, JSON.stringify({raceId: raceModal.data("raceId"), raceData: raceData}), function(isSuccessful) { if(isSuccessful) { raceModal.modal("hide"); loadRaces(); } }); break; } } }) $("#delete-race-btn").click(function() { let raceModal = $("#race-modal"); let raceId = raceModal.data("raceId"); $.post(`https://${resName}/deleteRace`, JSON.stringify({raceId: raceId}), function(isSuccessful) { if(isSuccessful) { raceModal.modal("hide"); loadRaces(); } }); });