816 lines
		
	
	
		
			No EOL
		
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			816 lines
		
	
	
		
			No EOL
		
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 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 = $(`
 | |
| 		<div class="input-group leaderboard-element my-3">
 | |
| 			<span class="input-group-text">${getLocalizedText("menu:race")}</span>
 | |
| 			<input type="text" class="form-control race-label" disabled>
 | |
| 			<button type="button" class="btn btn-secondary choose-race-btn"><i class="bi bi-list-ul"></i></button>	
 | |
| 			<span class="input-group-text ms-2">${getLocalizedText("menu:webhook")}</span>
 | |
| 			<input type="text" required class="form-control webhook-input" data-bs-toggle="tooltip" data-bs-placement="top" title="${getLocalizedText("menu:webhook:description")}" placeholder="https://discord.com/api/webhooks/USE_YOUR_WEBHOOK/YOUR_WEBHOOK">
 | |
| 			<button type="button" class="btn-close btn-close-white ms-2 my-auto delete-element-btn"></button>	
 | |
| 		</div>
 | |
| 	`);
 | |
| 
 | |
| 	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 = $(`
 | |
| 		<div class="row g-2 row-cols-auto align-items-center justify-content-center mb-3">
 | |
| 			<p class="fs-3 my-auto me-2 checkpoint-index">${checkpointIndex}</p>
 | |
| 			
 | |
| 			<div class="form-floating text-body col">
 | |
| 				<input type="number" step="0.01" class="form-control checkpoint-x" placeholder="X" required>
 | |
| 				<label>${getLocalizedText("menu:x")}</label>
 | |
| 			</div>
 | |
| 
 | |
| 			<div class="form-floating text-body col">
 | |
| 				<input type="number" step="0.01" class="form-control checkpoint-y" placeholder="Y" required>
 | |
| 				<label>${getLocalizedText("menu:y")}</label>
 | |
| 			</div>
 | |
| 
 | |
| 			<div class="form-floating text-body col">
 | |
| 				<input type="number" step="0.01" class="form-control checkpoint-z" placeholder="Z" required>
 | |
| 				<label>${getLocalizedText("menu:z")}</label>
 | |
| 			</div>
 | |
| 
 | |
| 			<button type="button" class="btn btn-secondary col-auto checkpoint-current-coords-btn" data-translation-id="menu:choose_starting_zone" data-bs-toggle="tooltip" data-bs-placement="top"><i class="bi bi-arrow-down-square"></i></button>
 | |
| 
 | |
| 			<div class="form-floating text-body col-1 ms-2">
 | |
| 				<input type="number" step="0.01" class="form-control checkpoint-size" placeholder="Size" val="10.0" required>
 | |
| 				<label>${getLocalizedText("menu:size")}</label>
 | |
| 			</div>
 | |
| 			
 | |
| 			<button type="button" class="btn-close delete-checkpoint-btn"></button>	
 | |
| 		</div>
 | |
| 	`);
 | |
| 
 | |
| 	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();
 | |
| 		}
 | |
| 	});
 | |
| }); | 
