2715 lines
		
	
	
	
		
			92 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			2715 lines
		
	
	
	
		
			92 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const resName = GetParentResourceName();
 | 
						|
let hasDoorsCreator = null; // editing this is useless, don't do it
 | 
						|
 | 
						|
// Open/Close menu
 | 
						|
function openMenu(version, fullConfig) {
 | 
						|
	$("#farming-creator-version").text(version);
 | 
						|
 | 
						|
	loadSeeds();
 | 
						|
	loadFields();
 | 
						|
	loadFarms();
 | 
						|
	loadWorkbenches();
 | 
						|
	loadFoundries();
 | 
						|
	loadFormulas();
 | 
						|
	loadSettings(fullConfig);
 | 
						|
 | 
						|
    $("#farming-creator").show()
 | 
						|
}
 | 
						|
 | 
						|
function closeMenu() {
 | 
						|
	// Resets current active tab
 | 
						|
	$("#farming-creator").find(".nav-link, .tab-pane").each(function() {
 | 
						|
		if($(this).data("isDefault") == "1") {
 | 
						|
			$(this).addClass(["active", "show"])
 | 
						|
		} else {
 | 
						|
			$(this).removeClass(["active", "show"])
 | 
						|
		}
 | 
						|
	})
 | 
						|
	
 | 
						|
    $("#farming-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;
 | 
						|
		}
 | 
						|
	}
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
███████ ███████ ████████ ████████ ██ ███    ██  ██████  ███████ 
 | 
						|
██      ██         ██       ██    ██ ████   ██ ██       ██      
 | 
						|
███████ █████      ██       ██    ██ ██ ██  ██ ██   ███ ███████ 
 | 
						|
     ██ ██         ██       ██    ██ ██  ██ ██ ██    ██      ██ 
 | 
						|
███████ ███████    ██       ██    ██ ██   ████  ██████  ███████ 
 | 
						|
*/
 | 
						|
 | 
						|
/* 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-item-to-burn-plants-choose-item").click(async function() {
 | 
						|
	const itemName = await itemsDialog();
 | 
						|
 | 
						|
	$("#settings-item-to-burn-plants-item-name").val(itemName);
 | 
						|
})
 | 
						|
 | 
						|
$("#settings-burn-plants-animations-btn").click(async function() {
 | 
						|
	const oldAnimations = $("#settings-burn-plants-animations-btn").data("animationsData");
 | 
						|
	const newAnimations = await animationsDialog(oldAnimations || []);
 | 
						|
 | 
						|
	$("#settings-burn-plants-animations-btn").data("animationsData", newAnimations);
 | 
						|
})
 | 
						|
 | 
						|
function loadSettings(fullConfig) {
 | 
						|
 | 
						|
	// Language
 | 
						|
	setTomSelectValue("#settings-locale", fullConfig.locale)
 | 
						|
	setTomSelectValue("#settings-targeting-script", fullConfig.targetingScript)
 | 
						|
	setTomSelectValue("#settings-help-notification-script", fullConfig.helpNotification)
 | 
						|
 | 
						|
	// Generic
 | 
						|
	$("#settings-ace-permission").val(fullConfig.acePermission);
 | 
						|
	$("#settings-can-always-carry").prop("checked", fullConfig.canAlwaysCarryItem);
 | 
						|
	$("#settings-can-receive-multiple-same-item").prop("checked", fullConfig.canReceiveMultipleTimesTheSameItem);
 | 
						|
	$("#settings-menu-position").val(fullConfig.menuPosition);
 | 
						|
	$("#settings-targeting-script").val(fullConfig.targetingScript);
 | 
						|
	setSelectiveTargetingSettings(fullConfig.selectiveTargeting);
 | 
						|
 | 
						|
	// Seeds
 | 
						|
	$("#settings-time-to-burn-plants").val(fullConfig.timeToBurnPlants);
 | 
						|
	$("#settings-minimum-distance-between-plants").val(fullConfig.minimumDistanceBetweenPlants);
 | 
						|
	$("#settings-burn-plants-animations-btn").data("animationsData", fullConfig.burnPlantsAnimations || []);
 | 
						|
	$("#settings-item-to-burn-plants-is-required").prop("checked", fullConfig.itemToBurnPlants.isRequired);
 | 
						|
	$("#settings-item-to-burn-plants-item-name").val(fullConfig.itemToBurnPlants.name);
 | 
						|
	$("#settings-item-to-burn-plants-minimum-quantity").val(fullConfig.itemToBurnPlants.minQuantity);
 | 
						|
	$("#settings-item-to-burn-plants-lose-on-use").prop("checked", fullConfig.itemToBurnPlants.loseOnUse);
 | 
						|
 | 
						|
	// Farms
 | 
						|
	$("#settings-allow-afk-farming").prop("checked", fullConfig.allowAfkFarming);
 | 
						|
 | 
						|
	// Foundries
 | 
						|
	$("#settings-allow-to-save-formulas").prop("checked", fullConfig.allowToSaveFormulas);
 | 
						|
	$("#settings-allow-afk-foundrying").prop("checked", fullConfig.allowAfkFoundrying);
 | 
						|
 | 
						|
	// 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
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
function toggleSelectiveTargeting() {
 | 
						|
	const enabled = $("#settings-targeting-script").val() != "none";
 | 
						|
	$("#selective-targeting-container").find(".form-check-input").prop("disabled", !enabled);
 | 
						|
}
 | 
						|
 | 
						|
function getSelectiveTargetingSettings() {
 | 
						|
	let selectiveTargeting = {};
 | 
						|
 | 
						|
	$("#selective-targeting-container").find(".form-check-input").each(function(index, element) {
 | 
						|
		element = $(element);
 | 
						|
 | 
						|
		let featureName = element.data("featureName");
 | 
						|
		let enabled = element.prop("checked");
 | 
						|
 | 
						|
		selectiveTargeting[featureName] = enabled;
 | 
						|
	});
 | 
						|
 | 
						|
	return selectiveTargeting;
 | 
						|
}
 | 
						|
 | 
						|
function setSelectiveTargetingSettings(selectiveTargeting) {
 | 
						|
	$("#selective-targeting-container").find(".form-check-input").each(function(index, element) {
 | 
						|
		element = $(element);
 | 
						|
 | 
						|
		let featureName = element.data("featureName");
 | 
						|
		let enabled = selectiveTargeting[featureName];
 | 
						|
 | 
						|
		element.prop("checked", enabled);
 | 
						|
	});
 | 
						|
 | 
						|
	toggleSelectiveTargeting();
 | 
						|
}
 | 
						|
 | 
						|
$("#settings-targeting-script").change(function() {
 | 
						|
	toggleSelectiveTargeting();
 | 
						|
})
 | 
						|
 | 
						|
$("#settings").submit(async function(event) {
 | 
						|
	if(isThereAnyErrorInForm(event)) return;
 | 
						|
 | 
						|
	let clientSettings = {
 | 
						|
		// Generic
 | 
						|
		menuPosition: $("#settings-menu-position").val(),
 | 
						|
		targetingScript: $("#settings-targeting-script").val(),
 | 
						|
		helpNotification: $("#settings-help-notification-script").val(),
 | 
						|
 | 
						|
		// Seeds
 | 
						|
		minimumDistanceBetweenPlants: parseFloat( $("#settings-minimum-distance-between-plants").val() ),
 | 
						|
		burnPlantsAnimations: $("#settings-burn-plants-animations-btn").data("animationsData"),
 | 
						|
 | 
						|
		// Farms
 | 
						|
		allowAfkFarming: $("#settings-allow-afk-farming").prop("checked"),
 | 
						|
 | 
						|
		selectiveTargeting: getSelectiveTargetingSettings()
 | 
						|
	}
 | 
						|
 | 
						|
	let sharedSettings = {
 | 
						|
		locale: $("#settings-locale").val(),
 | 
						|
		timeToBurnPlants: parseInt( $("#settings-time-to-burn-plants").val() ),
 | 
						|
		allowToSaveFormulas: $("#settings-allow-to-save-formulas").prop("checked"),
 | 
						|
		allowAfkFoundrying: $("#settings-allow-afk-foundrying").prop("checked"),
 | 
						|
	}
 | 
						|
 | 
						|
	let serverSettings = {
 | 
						|
		// Generic
 | 
						|
		acePermission: $("#settings-ace-permission").val(),
 | 
						|
		canAlwaysCarryItem: $("#settings-can-always-carry").prop("checked"),
 | 
						|
		canReceiveMultipleTimesTheSameItem: $("#settings-can-receive-multiple-same-item").prop("checked"),
 | 
						|
 | 
						|
		// Seeds
 | 
						|
		itemToBurnPlants: {
 | 
						|
			isRequired: $("#settings-item-to-burn-plants-is-required").prop("checked"),
 | 
						|
			name: $("#settings-item-to-burn-plants-item-name").val(),
 | 
						|
			minQuantity: parseInt( $("#settings-item-to-burn-plants-minimum-quantity").val() ),
 | 
						|
			loseOnUse: $("#settings-item-to-burn-plants-lose-on-use").prop("checked"),
 | 
						|
		},
 | 
						|
 | 
						|
		// Discord logs
 | 
						|
		areDiscordLogsActive: $("#settings-areDiscordLogsActive").prop("checked"),
 | 
						|
		mainDiscordWebhook: $("#settings-mainDiscordWebhook").val(),
 | 
						|
		specificWebhooks: getSeparatedDiscordWebhooks(),
 | 
						|
	}
 | 
						|
 | 
						|
	const response = await $.post(`https://${resName}/saveSettings`, JSON.stringify({
 | 
						|
		clientSettings: clientSettings,
 | 
						|
		serverSettings: serverSettings,
 | 
						|
		sharedSettings: sharedSettings
 | 
						|
	}));
 | 
						|
	showServerResponse(response);
 | 
						|
 | 
						|
	refreshTranslations(sharedSettings.locale);
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
███████ ███████ ███████ ██████  ███████ 
 | 
						|
██      ██      ██      ██   ██ ██      
 | 
						|
███████ █████   █████   ██   ██ ███████ 
 | 
						|
     ██ ██      ██      ██   ██      ██ 
 | 
						|
███████ ███████ ███████ ██████  ███████ 
 | 
						|
*/
 | 
						|
let seedsDatatable = $("#seeds-container").DataTable( {
 | 
						|
	"lengthMenu": [10, 15, 20],
 | 
						|
	"createdRow": function ( row, data, index ) {
 | 
						|
		$(row).addClass("clickable");
 | 
						|
 | 
						|
		$(row).click(function() {
 | 
						|
			let id = parseInt( data[0] );
 | 
						|
 | 
						|
			editSeed(id);
 | 
						|
		})
 | 
						|
	},
 | 
						|
});
 | 
						|
 | 
						|
let seeds = {};
 | 
						|
 | 
						|
function loadSeeds() {
 | 
						|
	$.post(`https://${resName}/getAllSeeds`, {}, async function(rawSeeds) {
 | 
						|
 | 
						|
		// Manually create the table to avoid incompatibilities due table indexing
 | 
						|
		seeds = {};
 | 
						|
 | 
						|
		for(const[k, seedData] of Object.entries(rawSeeds)) {
 | 
						|
			seeds[seedData.id] = seedData;
 | 
						|
		}
 | 
						|
 | 
						|
		seedsDatatable.clear();
 | 
						|
 | 
						|
		for(const[id, seedData] of Object.entries(seeds)) {
 | 
						|
			seedsDatatable.row.add([
 | 
						|
				id,
 | 
						|
				seedData.label,
 | 
						|
				seedData.data.stages.length
 | 
						|
			]);
 | 
						|
		}
 | 
						|
 | 
						|
		seedsDatatable.draw();
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
function setDefaultDataOfSeed() {
 | 
						|
	$("#seed-label").val("Default");
 | 
						|
	$("#seed-maximum-steepness").val(55);
 | 
						|
	$("#seed-minimum-free-space-above").val(3.0);
 | 
						|
	$("#seed-item-name").val("");
 | 
						|
	$("#seed-item-minimum-quantity").val(1);
 | 
						|
	$("#seed-item-lose-on-use-checkbox").prop("checked", true);
 | 
						|
	$("#seed-minimum-police").val(0);
 | 
						|
 | 
						|
	let seedModal = $("#seed-modal");
 | 
						|
	seedModal.data("materialsOptions", getDefaultMaterialsOptions());
 | 
						|
	seedModal.data("plantingAnimations", [defaultPlantingAnimData]);
 | 
						|
	seedModal.data("markerData", getDefaultMarkerCustomization());
 | 
						|
 | 
						|
	$("#seed-stages").empty();
 | 
						|
}
 | 
						|
 | 
						|
$("#new-seed-btn").click(function() {
 | 
						|
	let seedModal = $("#seed-modal");
 | 
						|
 | 
						|
	// Converts from edit modal to create modal
 | 
						|
	seedModal.data("action", "create");
 | 
						|
	
 | 
						|
	$("#delete-seed-btn").hide();
 | 
						|
	$("#save-seed-btn").text( getLocalizedText("menu:create") );
 | 
						|
	
 | 
						|
	setDefaultDataOfSeed();
 | 
						|
 | 
						|
	seedModal.modal("show");
 | 
						|
})
 | 
						|
 | 
						|
$("#materials-options-btn").click(async function() {
 | 
						|
	let seedModal = $("#seed-modal");
 | 
						|
 | 
						|
	const oldMaterials = seedModal.data("materialsOptions");
 | 
						|
	const newMaterials = await groundMaterialsDialog(oldMaterials);
 | 
						|
	
 | 
						|
	seedModal.data("materialsOptions", newMaterials);
 | 
						|
})
 | 
						|
 | 
						|
$("#seed-planting-animation-btn").click(async function() {
 | 
						|
	let seedModal = $("#seed-modal");
 | 
						|
 | 
						|
	const oldAnimations = seedModal.data("plantingAnimations");
 | 
						|
	const newAnimations = await animationsDialog(oldAnimations);
 | 
						|
	
 | 
						|
	seedModal.data("plantingAnimations", newAnimations);
 | 
						|
});
 | 
						|
 | 
						|
$("#seed-customize-marker-btn").click(async function() {
 | 
						|
	let seedModal = $("#seed-modal");
 | 
						|
 | 
						|
	const oldMarkerData = seedModal.data("markerData");
 | 
						|
	const newMarkerData = await markerDialog(oldMarkerData);
 | 
						|
 | 
						|
	seedModal.data("markerData", newMarkerData);
 | 
						|
});
 | 
						|
 | 
						|
$("#choose-seed-item-name-btn").click(async function() {
 | 
						|
	const itemName = await itemsDialog();
 | 
						|
 | 
						|
	$("#seed-item-name").val(itemName);
 | 
						|
})
 | 
						|
 | 
						|
function renameAllStagesByTheirOrder() {
 | 
						|
	$("#seed-stages").find(".stage-title").each(function(index, element) {
 | 
						|
		let stageNumber = index + 1;
 | 
						|
 | 
						|
		$(this).prop("innerHTML", `${ getLocalizedText("menu:stage") }  ${stageNumber}`)
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
async function addRequiredItemToStage(stageDiv, requiredItem) {
 | 
						|
	let itemDiv = $(`
 | 
						|
		<div class="row g-2 row-cols-auto align-items-center text-body my-2 required-item justify-content-center">
 | 
						|
			<button type="button" class="btn-close delete-required-item-btn me-3" ></button>	
 | 
						|
 | 
						|
			<select class="form-select required-item-type" style="width: auto;">
 | 
						|
				<option selected value="item">${getLocalizedText("menu:item")}</option>
 | 
						|
				<option value="account">${getLocalizedText("menu:account")}</option>
 | 
						|
				${await getFramework() == "ESX" ? `<option value="weapon">${getLocalizedText("menu:weapon")}</option>` : ""}
 | 
						|
			</select>
 | 
						|
			
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="text" class="form-control required-item-name" placeholder="Name" required>
 | 
						|
				<label>${ getLocalizedText("menu:object_name") }</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button type="button" class="btn btn-secondary col-auto choose-item-btn" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose") }"><i class="bi bi-list-ul"></i></button>	
 | 
						|
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="number" min=0 class="form-control required-item-min-quantity" placeholder="${getLocalizedText("menu:min_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:min_quantity")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div class="form-check my-auto fs-4 ms-1">
 | 
						|
				<input class="form-check-input required-item-lose-on-use-checkbox" type="checkbox" value="">
 | 
						|
				<label class="form-check-label">${getLocalizedText("menu:lose_on_use")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
	
 | 
						|
	itemDiv.find(".delete-required-item-btn").click(function() {
 | 
						|
		itemDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	itemDiv.find(".choose-item-btn").click(async function() {
 | 
						|
		let objectType = itemDiv.find(".required-item-type").val();
 | 
						|
 | 
						|
		let objectName = await objectDialog(objectType);
 | 
						|
 | 
						|
		itemDiv.find(".required-item-name").val(objectName);
 | 
						|
	}).tooltip();
 | 
						|
 | 
						|
	
 | 
						|
	if(requiredItem) {
 | 
						|
		itemDiv.find(".required-item-type").val(requiredItem.type);
 | 
						|
		itemDiv.find(".required-item-name").val(requiredItem.name);
 | 
						|
		itemDiv.find(".required-item-min-quantity").val(requiredItem.minQuantity);
 | 
						|
		itemDiv.find(".required-item-lose-on-use-checkbox").prop("checked", requiredItem.loseOnUse);
 | 
						|
	}
 | 
						|
 | 
						|
	stageDiv.find(".stage-required-items-list").append(itemDiv);
 | 
						|
}
 | 
						|
 
 | 
						|
async function addRewardItemToStage(stageDiv, rewardItem) {
 | 
						|
	let itemDiv = $(`
 | 
						|
		<div class="row g-2 row-cols-auto align-items-center text-body my-2 reward-item justify-content-center">
 | 
						|
			<button type="button" class="btn-close delete-reward-item-btn me-3" ></button>	
 | 
						|
 | 
						|
			<select class="form-select reward-item-type" style="width: auto;">
 | 
						|
				<option selected value="item">${getLocalizedText("menu:item")}</option>
 | 
						|
				<option value="account">${getLocalizedText("menu:account")}</option>
 | 
						|
				${await getFramework() == "ESX" ? `<option value="weapon">${getLocalizedText("menu:weapon")}</option>` : ""}
 | 
						|
			</select>
 | 
						|
			
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="text" class="form-control reward-item-name" placeholder="Name" required>
 | 
						|
				<label>${ getLocalizedText("menu:object_name") }</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button type="button" class="btn btn-secondary col-auto choose-item-btn" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose") }"><i class="bi bi-list-ul"></i></button>	
 | 
						|
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number" min=0 class="form-control reward-item-min-quantity" placeholder="${getLocalizedText("menu:min_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:min_quantity")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number" min=0 class="form-control reward-item-max-quantity" placeholder="${getLocalizedText("menu:max_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:max_quantity")}</label>
 | 
						|
			</div>
 | 
						|
			
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number"  class="form-control reward-item-chances" placeholder="${getLocalizedText("menu:probability")}" required>
 | 
						|
				<label>${getLocalizedText("menu:probability")}</label>
 | 
						|
			</div>
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
	
 | 
						|
	itemDiv.find(".delete-reward-item-btn").click(function() {
 | 
						|
		itemDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	itemDiv.find(".choose-item-btn").click(async function() {
 | 
						|
		let objectType = itemDiv.find(".reward-item-type").val();
 | 
						|
 | 
						|
		let objectName = await objectDialog(objectType);
 | 
						|
 | 
						|
		itemDiv.find(".reward-item-name").val(objectName);
 | 
						|
	}).tooltip();
 | 
						|
 | 
						|
	
 | 
						|
	if(rewardItem) {
 | 
						|
		itemDiv.find(".reward-item-type").val(rewardItem.type);
 | 
						|
		itemDiv.find(".reward-item-name").val(rewardItem.name);
 | 
						|
		itemDiv.find(".reward-item-min-quantity").val(rewardItem.minQuantity);
 | 
						|
		itemDiv.find(".reward-item-max-quantity").val(rewardItem.maxQuantity);
 | 
						|
		itemDiv.find(".reward-item-chances").val(rewardItem.chances);
 | 
						|
	}
 | 
						|
 | 
						|
	stageDiv.find(".stage-reward-items-list").append(itemDiv);
 | 
						|
}
 | 
						|
 | 
						|
function addSeedStage(stageData) {
 | 
						|
	const stageIndex = $("#seed-stages").children(".stage").length + 1;
 | 
						|
 | 
						|
	let stageDiv = $(`
 | 
						|
		<div class="stage">
 | 
						|
			<h3 class="text-center stage-title">${getLocalizedText("menu:stage")} ${stageIndex}</h3>
 | 
						|
 | 
						|
			<div class="d-flex gap-2 align-items-center justify-content-center mt-3">
 | 
						|
				<div class="form-floating text-body col-3">
 | 
						|
					<input type="text" class="form-control plant-model" placeholder="Plant model" required>
 | 
						|
					<label>${getLocalizedText("menu:plant_model")}</label>
 | 
						|
				</div>
 | 
						|
 | 
						|
				<a class="btn btn-secondary clickable open-models-btn" target="_blank" onclick='window.invokeNative("openUrl", "https://forge.plebmasters.de/objects/")' data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:open_models_list") }"><i class="bi bi-images"></i></a>
 | 
						|
			
 | 
						|
				<div class="form-floating text-body col-2 ms-3">
 | 
						|
					<input type="text" class="form-control stage-label" placeholder="Label" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:player_can_see_this") }" required>
 | 
						|
					<label>${getLocalizedText("menu:label")}</label>
 | 
						|
				</div>
 | 
						|
 | 
						|
				<div class="form-floating text-body col-2">
 | 
						|
					<input type="number" class="form-control stage-duration" placeholder="Duration" min="1" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:stage_duration_description") }" required>
 | 
						|
					<label>${getLocalizedText("menu:duration_minutes")}</label>
 | 
						|
				</div>
 | 
						|
 | 
						|
				<div class="form-floating text-body col-2">
 | 
						|
					<input type="number" class="form-control stage-minutes-before-death" placeholder="Duration" min="1" value="60" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:minutes_before_death_description") }" required>
 | 
						|
					<label>${getLocalizedText("menu:minutes_before_death")}</label>
 | 
						|
				</div>
 | 
						|
 | 
						|
				<button type="button" class="btn btn-secondary mx-3 seed-stage-end-animation-btn" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:stage_end_animation_description") }">${getLocalizedText("menu:stage_end_animation")}</button>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<h3 class="text-center mt-5">${getLocalizedText("menu:on_stage_end")}</h3>
 | 
						|
 | 
						|
			<div>
 | 
						|
				<p class="text-center fs-4">${getLocalizedText("menu:required_items")}</p>
 | 
						|
 | 
						|
				<div class="stage-required-items-list">
 | 
						|
 | 
						|
				</div>
 | 
						|
 | 
						|
				<button type="button" class="btn btn-secondary stage-add-required-item-btn">${getLocalizedText("menu:add_item")}</button>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div>
 | 
						|
				<p class="text-center fs-4">${getLocalizedText("menu:items_to_give")}</p>
 | 
						|
 | 
						|
				<div class="my-4 row g-2 row-cols-auto align-items-center justify-content-center">
 | 
						|
					<p class="text-center fs-4 my-auto me-3">${ getLocalizedText("menu:amount_of_objects_as_reward") }</p>
 | 
						|
 | 
						|
					<div class="form-floating text-body col-3">
 | 
						|
						<input type="number" class="form-control min-objects-amount" placeholder="Minimum" required>
 | 
						|
						<label>${ getLocalizedText("menu:min_quantity") }</label>
 | 
						|
					</div>
 | 
						|
 | 
						|
					<div class="form-floating text-body col-3">
 | 
						|
						<input type="number" class="form-control max-objects-amount" placeholder="Maximum" required>
 | 
						|
						<label>${ getLocalizedText("menu:max_quantity") }</label>
 | 
						|
					</div>
 | 
						|
				</div>
 | 
						|
					
 | 
						|
				<div class="stage-reward-items-list">
 | 
						|
 | 
						|
				</div>
 | 
						|
 | 
						|
				<button type="button" class="btn btn-secondary stage-add-reward-item-btn">${getLocalizedText("menu:add_item")}</button>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div class="d-inline-block col-12 mt-1">
 | 
						|
				<button type="button" class="btn btn-warning btn-sm float-end delete-stage-btn">${getLocalizedText("menu:delete_stage")}</button>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<hr class="thick-hr">
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
 | 
						|
	stageDiv.find("[data-bs-toggle='tooltip']").tooltip();
 | 
						|
 | 
						|
	stageDiv.find(".delete-stage-btn").click(function() {
 | 
						|
		stageDiv.remove();
 | 
						|
		renameAllStagesByTheirOrder();
 | 
						|
	});
 | 
						|
 | 
						|
	stageDiv.find(".stage-add-required-item-btn").click(function() {
 | 
						|
		addRequiredItemToStage(stageDiv);
 | 
						|
	});
 | 
						|
 | 
						|
	stageDiv.find(".stage-add-reward-item-btn").click(function() {
 | 
						|
		addRewardItemToStage(stageDiv);
 | 
						|
	});
 | 
						|
 | 
						|
	// Default interaction animation in case there isn't any
 | 
						|
	stageDiv.data("stageEndAnimations", [defaultPlantInteractionAnimData]);
 | 
						|
 | 
						|
	stageDiv.find(".seed-stage-end-animation-btn").click(async function() {
 | 
						|
		const oldAnimations = stageDiv.data("stageEndAnimations");
 | 
						|
		const newAnimations = await animationsDialog(oldAnimations);
 | 
						|
		
 | 
						|
		stageDiv.data("stageEndAnimations", newAnimations);
 | 
						|
	});
 | 
						|
 | 
						|
	if(stageData) {
 | 
						|
		stageDiv.find(".plant-model").val(stageData.plantModel);
 | 
						|
		stageDiv.find(".stage-label").val(stageData.label);
 | 
						|
		stageDiv.find(".stage-duration").val(stageData.duration);
 | 
						|
		stageDiv.find(".stage-minutes-before-death").val(stageData.minutesBeforeDeath);
 | 
						|
		
 | 
						|
		stageDiv.find(".min-objects-amount").val(stageData.minObjectsAmount);
 | 
						|
		stageDiv.find(".max-objects-amount").val(stageData.maxObjectsAmount);
 | 
						|
 | 
						|
		stageDiv.data("stageEndAnimations", stageData.stageEndAnimations);
 | 
						|
 | 
						|
		for(let requiredItem of stageData.requiredItems) {
 | 
						|
			addRequiredItemToStage(stageDiv, requiredItem);
 | 
						|
		}
 | 
						|
 | 
						|
		for(let rewardItem of stageData.rewardItems) {
 | 
						|
			addRewardItemToStage(stageDiv, rewardItem);
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		addRequiredItemToStage(stageDiv);
 | 
						|
		addRewardItemToStage(stageDiv);
 | 
						|
 | 
						|
		// If it's not the first stage, the model will be copied from the stage before
 | 
						|
		if(stageIndex > 1) {
 | 
						|
			const model = $("#seed-stages").children(".stage").last().find(".plant-model").val();
 | 
						|
 | 
						|
			stageDiv.find(".plant-model").val(model);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	$("#seed-stages").append(stageDiv);
 | 
						|
}
 | 
						|
 | 
						|
$("#add-seed-stage-btn").click(function() {
 | 
						|
	addSeedStage();
 | 
						|
})
 | 
						|
 | 
						|
function editSeed(id) {
 | 
						|
	let seedModal = $("#seed-modal");
 | 
						|
 | 
						|
	// Converts from create modal to edit modal
 | 
						|
	seedModal.data("action", "edit");
 | 
						|
	seedModal.data("seedId", id);
 | 
						|
 | 
						|
	$("#delete-seed-btn").show();
 | 
						|
	$("#save-seed-btn").text( getLocalizedText("menu:save") );
 | 
						|
 | 
						|
	const seedInfo = seeds[id];
 | 
						|
	const seedData = seedInfo.data;
 | 
						|
 | 
						|
	$("#seed-label").val(seedInfo.label);
 | 
						|
	$("#seed-maximum-steepness").val(seedData.maximumSteepness);
 | 
						|
	$("#seed-minimum-free-space-above").val(seedData.minimumFreeSpaceAbove);
 | 
						|
	$("#seed-item-name").val(seedData.seedItemName);
 | 
						|
	$("#seed-item-minimum-quantity").val(seedData.seedItemMinimumQuantity);
 | 
						|
	$("#seed-item-lose-on-use-checkbox").prop("checked", seedData.seedItemLoseOnUse);
 | 
						|
	$("#seed-minimum-police").val(seedData.minimumPolice);
 | 
						|
 | 
						|
	seedModal.data("materialsOptions", seedData.materialsOptions);
 | 
						|
	seedModal.data("plantingAnimations", seedData.plantingAnimations || [defaultPlantingAnimData]);
 | 
						|
	seedModal.data("markerData", seedData.markerData || getDefaultMarkerCustomization());
 | 
						|
 | 
						|
	$("#seed-stages").empty();
 | 
						|
 | 
						|
	if(seedData.stages) {
 | 
						|
		for(const[stage, stageData] of Object.entries(seedData.stages)) {
 | 
						|
			addSeedStage(stageData);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	seedModal.modal("show");
 | 
						|
}
 | 
						|
 | 
						|
function getRequiredItemFromStageDiv(stageDiv) {
 | 
						|
	let requiredItems = [];
 | 
						|
 | 
						|
	stageDiv.find(".stage-required-items-list").find(".required-item").each(function() {
 | 
						|
		const itemData = {
 | 
						|
			type: $(this).find(".required-item-type").val(),
 | 
						|
			name: $(this).find(".required-item-name").val(),
 | 
						|
			minQuantity: parseInt( $(this).find(".required-item-min-quantity").val() ),
 | 
						|
			loseOnUse: $(this).find(".required-item-lose-on-use-checkbox").prop("checked")
 | 
						|
		}
 | 
						|
 | 
						|
		requiredItems.push(itemData);
 | 
						|
	});
 | 
						|
 | 
						|
	return requiredItems;
 | 
						|
}
 | 
						|
 | 
						|
function getRewardItemFromStageDiv(stageDiv) {
 | 
						|
	let rewardItems = [];
 | 
						|
 | 
						|
	stageDiv.find(".stage-reward-items-list").find(".reward-item").each(function() {
 | 
						|
		const itemData = {
 | 
						|
			type: $(this).find(".reward-item-type").val(),
 | 
						|
			name: $(this).find(".reward-item-name").val(),
 | 
						|
			minQuantity: parseInt( $(this).find(".reward-item-min-quantity").val() ),
 | 
						|
			maxQuantity: parseInt( $(this).find(".reward-item-max-quantity").val() ),
 | 
						|
			chances: parseInt( $(this).find(".reward-item-chances").val() ),
 | 
						|
		}
 | 
						|
 | 
						|
		rewardItems.push(itemData);
 | 
						|
	});
 | 
						|
 | 
						|
	return rewardItems;
 | 
						|
}
 | 
						|
 | 
						|
function getSeedStages() {
 | 
						|
	let stages = [];
 | 
						|
 | 
						|
	$("#seed-stages").find(".stage").each(function() {
 | 
						|
		let stage = {
 | 
						|
			plantModel: $(this).find(".plant-model").val(),
 | 
						|
			label: $(this).find(".stage-label").val(),
 | 
						|
			duration: parseInt( $(this).find(".stage-duration").val() ),
 | 
						|
			minutesBeforeDeath: parseInt( $(this).find(".stage-minutes-before-death").val() ),
 | 
						|
			stageEndAnimations: $(this).data("stageEndAnimations"),
 | 
						|
			requiredItems: getRequiredItemFromStageDiv( $(this) ),
 | 
						|
			minObjectsAmount: parseInt( $(this).find(".min-objects-amount").val() ),
 | 
						|
			maxObjectsAmount: parseInt( $(this).find(".max-objects-amount").val() ),
 | 
						|
			rewardItems: getRewardItemFromStageDiv( $(this) ),
 | 
						|
		};
 | 
						|
 | 
						|
		stages.push(stage);
 | 
						|
	});
 | 
						|
 | 
						|
	return stages;
 | 
						|
}
 | 
						|
 | 
						|
$("#seed-form").submit(function(event) {
 | 
						|
	if(isThereAnyErrorInForm(event)) return;
 | 
						|
 | 
						|
	let seedModal = $("#seed-modal");
 | 
						|
	let action = seedModal.data("action");
 | 
						|
 | 
						|
	let seedData = {
 | 
						|
		label: $("#seed-label").val(),
 | 
						|
		data: {
 | 
						|
			maximumSteepness: parseInt( $("#seed-maximum-steepness").val() ),
 | 
						|
			minimumFreeSpaceAbove: parseFloat( $("#seed-minimum-free-space-above").val() ), 
 | 
						|
			materialsOptions: seedModal.data("materialsOptions"),
 | 
						|
			plantingAnimations: seedModal.data("plantingAnimations") || [defaultPlantingAnimData],
 | 
						|
			markerData: seedModal.data("markerData"),
 | 
						|
			seedItemName: $("#seed-item-name").val(),
 | 
						|
			seedItemMinimumQuantity: parseInt( $("#seed-item-minimum-quantity").val() ),
 | 
						|
			seedItemLoseOnUse: $("#seed-item-lose-on-use-checkbox").prop("checked"),
 | 
						|
			minimumPolice: parseInt( $("#seed-minimum-police").val() ),
 | 
						|
			stages: getSeedStages()
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	switch(action) {
 | 
						|
		case "create": {
 | 
						|
			$.post(`https://${resName}/createSeed`, JSON.stringify(seedData), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					seedModal.modal("hide");
 | 
						|
					loadSeeds();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case "edit": {
 | 
						|
			$.post(`https://${resName}/updateSeed`, JSON.stringify({seedId: seedModal.data("seedId"), seedData: seedData}), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					seedModal.modal("hide");
 | 
						|
					loadSeeds();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
})
 | 
						|
 | 
						|
$("#delete-seed-btn").click(function() {
 | 
						|
	let seedModal = $("#seed-modal");
 | 
						|
	let seedId = seedModal.data("seedId");
 | 
						|
 | 
						|
	$.post(`https://${resName}/deleteSeed`, JSON.stringify({seedId: seedId}), function(isSuccessful) {
 | 
						|
		if(isSuccessful) {
 | 
						|
			seedModal.modal("hide");
 | 
						|
			loadSeeds();
 | 
						|
		}
 | 
						|
	});
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
███████ ██ ███████ ██      ██████  ███████
 | 
						|
██      ██ ██      ██      ██   ██ ██     
 | 
						|
█████   ██ █████   ██      ██   ██ ███████
 | 
						|
██      ██ ██      ██      ██   ██      ██
 | 
						|
██      ██ ███████ ███████ ██████  ███████
 | 
						|
*/
 | 
						|
let fieldsDatatable = $("#fields-container").DataTable( {
 | 
						|
	"lengthMenu": [10, 15, 20],
 | 
						|
	"createdRow": function ( row, data, index ) {
 | 
						|
		$(row).addClass("clickable");
 | 
						|
 | 
						|
		$(row).click(function() {
 | 
						|
			let id = parseInt( data[0] );
 | 
						|
 | 
						|
			editField(id);
 | 
						|
		})
 | 
						|
	},
 | 
						|
});
 | 
						|
 | 
						|
let fields = {};
 | 
						|
 | 
						|
async function getCountOfObjectsForFieldId(id) {
 | 
						|
	return new Promise(function(resolve) {
 | 
						|
		$.post(`https://${resName}/getCountOfObjectsForFieldId`, JSON.stringify({fieldId: parseInt(id)}), function(count) {
 | 
						|
			resolve(count);
 | 
						|
		});
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
function loadFields() {
 | 
						|
	$.post(`https://${resName}/getAllFields`, {}, async function(rawFields) {
 | 
						|
 | 
						|
		// Manually create the table to avoid incompatibilities due table indexing
 | 
						|
		fields = {};
 | 
						|
 | 
						|
		for(const[k, fieldData] of Object.entries(rawFields)) {
 | 
						|
			fields[fieldData.id] = fieldData;
 | 
						|
		}
 | 
						|
 | 
						|
		fieldsDatatable.clear();
 | 
						|
 | 
						|
		for(const[id, fieldData] of Object.entries(fields)) {
 | 
						|
			fieldsDatatable.row.add([
 | 
						|
				id,
 | 
						|
				fieldData.label,
 | 
						|
				fieldData.data.radius,
 | 
						|
				await getCountOfObjectsForFieldId(id),
 | 
						|
			]);
 | 
						|
		}
 | 
						|
 | 
						|
		fieldsDatatable.draw();
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
function setDefaultDataOfField() {
 | 
						|
	$("#field-label").val("Default");
 | 
						|
	$("#field-object-model").val("");
 | 
						|
	$("#field-radius").val(30.0);
 | 
						|
	$("#field-minimum-police").val(0);
 | 
						|
	$("#field-max-objects").val(30);
 | 
						|
	$("#field-respawn-timer").val(15);
 | 
						|
	
 | 
						|
	$("#field-coords-x").val("");
 | 
						|
	$("#field-coords-y").val("");
 | 
						|
	$("#field-coords-z").val("");
 | 
						|
 | 
						|
	$("#field-reward-min-objects-amount").val(1);
 | 
						|
	$("#field-reward-max-objects-amount").val(1);
 | 
						|
 | 
						|
	$("#field-required-items-list").empty();
 | 
						|
	$("#field-reward-items-list").empty();
 | 
						|
 | 
						|
	$("#field-modal").find("input:radio[name='field-spawn-coords-type'][value='automatic']").prop("checked", true).change();
 | 
						|
 | 
						|
	let fieldModal = $("#field-modal");
 | 
						|
	fieldModal.data("animations", [defaultPlantInteractionAnimData]);
 | 
						|
	fieldModal.data("blipData", getDefaultBlipCustomization());
 | 
						|
	fieldModal.data("allowedJobs", null);
 | 
						|
	fieldModal.data("availableSpawnPoints", null);
 | 
						|
}
 | 
						|
 | 
						|
$("#new-field-btn").click(function() {
 | 
						|
	let fieldModal = $("#field-modal");
 | 
						|
 | 
						|
	// Converts from edit modal to create modal
 | 
						|
	fieldModal.data("action", "create");
 | 
						|
	
 | 
						|
	$("#delete-field-btn").hide();
 | 
						|
	$("#save-field-btn").text( getLocalizedText("menu:create") );
 | 
						|
	
 | 
						|
	setDefaultDataOfField();
 | 
						|
 | 
						|
	fieldModal.modal("show");
 | 
						|
})
 | 
						|
 | 
						|
$("#field-animations-btn").click(async function() {
 | 
						|
	let fieldModal = $("#field-modal");
 | 
						|
 | 
						|
	let oldAnimations = fieldModal.data("animations");
 | 
						|
	let newAnimations = await animationsDialog(oldAnimations);
 | 
						|
 | 
						|
	fieldModal.data("animations", newAnimations);
 | 
						|
})
 | 
						|
 | 
						|
$("#field-customize-blip-btn").click(async function() {
 | 
						|
	let fieldModal = $("#field-modal");
 | 
						|
 | 
						|
	let oldBlipData = fieldModal.data("blipData");
 | 
						|
	let newBlipData = await blipDialog(oldBlipData);
 | 
						|
 | 
						|
	fieldModal.data("blipData", newBlipData);
 | 
						|
})
 | 
						|
 | 
						|
$("#field-allowed-jobs-btn").click(async function() {
 | 
						|
	let fieldModal = $("#field-modal");
 | 
						|
 | 
						|
	let oldAllowedJobs = fieldModal.data("allowedJobs");
 | 
						|
	let newAllowedJobs = await jobsDialog(oldAllowedJobs);
 | 
						|
 | 
						|
	fieldModal.data("allowedJobs", newAllowedJobs);
 | 
						|
})
 | 
						|
 | 
						|
$("#field-current-coords-btn").click(async function() {
 | 
						|
	const coords = await getCurrentCoords();
 | 
						|
	
 | 
						|
	$("#field-coords-x").val(coords.x);
 | 
						|
	$("#field-coords-y").val(coords.y);
 | 
						|
	$("#field-coords-z").val(coords.z);
 | 
						|
})
 | 
						|
 | 
						|
$("input:radio[name='field-spawn-coords-type']").change(function() {
 | 
						|
	const spawnType = $(this).val();
 | 
						|
 | 
						|
	$("#field-modal").data("availableSpawnPoints", null);
 | 
						|
 | 
						|
	$("#field-choose-allowed-spawn-coordinates-btn").toggle(spawnType == "manual");
 | 
						|
})
 | 
						|
 | 
						|
$("#field-choose-allowed-spawn-coordinates-btn").click(async function() {
 | 
						|
	let fieldModal = $("#field-modal");
 | 
						|
 | 
						|
	fieldModal.modal("hide");
 | 
						|
	$("#farming-creator").hide();
 | 
						|
 | 
						|
	$.post(`https://${resName}/chooseAvailableSpawnpoints`, JSON.stringify({
 | 
						|
		coords: {
 | 
						|
			x: parseFloat( $("#field-coords-x").val() ),
 | 
						|
			y: parseFloat( $("#field-coords-y").val() ),
 | 
						|
			z: parseFloat( $("#field-coords-z").val() ),
 | 
						|
		},
 | 
						|
		radius: parseFloat( $("#field-radius").val() ),
 | 
						|
	}), async function(availableSpawnPoints) {
 | 
						|
		if(availableSpawnPoints) {
 | 
						|
			$("#field-modal").data("availableSpawnPoints", availableSpawnPoints);
 | 
						|
		}
 | 
						|
 | 
						|
		$("#farming-creator").show();
 | 
						|
		fieldModal.modal("show");
 | 
						|
	});
 | 
						|
});
 | 
						|
 | 
						|
async function addRequiredItemToField(requiredItem) {
 | 
						|
	let itemDiv = $(`
 | 
						|
		<div class="row g-2 row-cols-auto align-items-center text-body my-2 required-item justify-content-center">
 | 
						|
			<button type="button" class="btn-close delete-required-item-btn me-3" ></button>	
 | 
						|
 | 
						|
			<select class="form-select required-item-type" style="width: auto;">
 | 
						|
				<option selected value="item">${getLocalizedText("menu:item")}</option>
 | 
						|
				<option value="account">${getLocalizedText("menu:account")}</option>
 | 
						|
				${await getFramework() == "ESX" ? `<option value="weapon">${getLocalizedText("menu:weapon")}</option>` : ""}
 | 
						|
			</select>
 | 
						|
			
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="text" class="form-control required-item-name" placeholder="Name" required>
 | 
						|
				<label>${ getLocalizedText("menu:object_name") }</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button type="button" class="btn btn-secondary col-auto choose-item-btn" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose") }"><i class="bi bi-list-ul"></i></button>	
 | 
						|
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="number" min=0 class="form-control required-item-min-quantity" placeholder="${getLocalizedText("menu:min_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:min_quantity")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div class="form-check my-auto fs-4 ms-1">
 | 
						|
				<input class="form-check-input required-item-lose-on-use-checkbox" type="checkbox" value="">
 | 
						|
				<label class="form-check-label">${getLocalizedText("menu:lose_on_use")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
	
 | 
						|
	itemDiv.find(".delete-required-item-btn").click(function() {
 | 
						|
		itemDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	itemDiv.find(".choose-item-btn").click(async function() {
 | 
						|
		let objectType = itemDiv.find(".required-item-type").val();
 | 
						|
 | 
						|
		let objectName = await objectDialog(objectType);
 | 
						|
 | 
						|
		itemDiv.find(".required-item-name").val(objectName);
 | 
						|
	}).tooltip();
 | 
						|
 | 
						|
	
 | 
						|
	if(requiredItem) {
 | 
						|
		itemDiv.find(".required-item-type").val(requiredItem.type);
 | 
						|
		itemDiv.find(".required-item-name").val(requiredItem.name);
 | 
						|
		itemDiv.find(".required-item-min-quantity").val(requiredItem.minQuantity);
 | 
						|
		itemDiv.find(".required-item-lose-on-use-checkbox").prop("checked", requiredItem.loseOnUse);
 | 
						|
	}
 | 
						|
 | 
						|
	$("#field-required-items-list").append(itemDiv);
 | 
						|
}
 | 
						|
$("#field-add-required-item-btn").click(function() {
 | 
						|
	addRequiredItemToField();
 | 
						|
});
 | 
						|
 | 
						|
async function addRewardItemToField(rewardItem) {
 | 
						|
	let itemDiv = $(`
 | 
						|
		<div class="row g-2 row-cols-auto align-items-center text-body my-2 reward-item justify-content-center">
 | 
						|
			<button type="button" class="btn-close delete-reward-item-btn me-3" ></button>	
 | 
						|
 | 
						|
			<select class="form-select reward-item-type" style="width: auto;">
 | 
						|
				<option selected value="item">${getLocalizedText("menu:item")}</option>
 | 
						|
				<option value="account">${getLocalizedText("menu:account")}</option>
 | 
						|
				${await getFramework() == "ESX" ? `<option value="weapon">${getLocalizedText("menu:weapon")}</option>` : ""}
 | 
						|
			</select>
 | 
						|
			
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="text" class="form-control reward-item-name" placeholder="Name" required>
 | 
						|
				<label>${ getLocalizedText("menu:object_name") }</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button type="button" class="btn btn-secondary col-auto choose-item-btn" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose") }"><i class="bi bi-list-ul"></i></button>	
 | 
						|
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number" min=0 class="form-control reward-item-min-quantity" placeholder="${getLocalizedText("menu:min_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:min_quantity")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number" min=0 class="form-control reward-item-max-quantity" placeholder="${getLocalizedText("menu:max_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:max_quantity")}</label>
 | 
						|
			</div>
 | 
						|
			
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number"  class="form-control reward-item-chances" placeholder="${getLocalizedText("menu:probability")}" required>
 | 
						|
				<label>${getLocalizedText("menu:probability")}</label>
 | 
						|
			</div>
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
	
 | 
						|
	itemDiv.find(".delete-reward-item-btn").click(function() {
 | 
						|
		itemDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	itemDiv.find(".choose-item-btn").click(async function() {
 | 
						|
		let objectType = itemDiv.find(".reward-item-type").val();
 | 
						|
 | 
						|
		let objectName = await objectDialog(objectType);
 | 
						|
 | 
						|
		itemDiv.find(".reward-item-name").val(objectName);
 | 
						|
	}).tooltip();
 | 
						|
 | 
						|
	
 | 
						|
	if(rewardItem) {
 | 
						|
		itemDiv.find(".reward-item-type").val(rewardItem.type);
 | 
						|
		itemDiv.find(".reward-item-name").val(rewardItem.name);
 | 
						|
		itemDiv.find(".reward-item-min-quantity").val(rewardItem.minQuantity);
 | 
						|
		itemDiv.find(".reward-item-max-quantity").val(rewardItem.maxQuantity);
 | 
						|
		itemDiv.find(".reward-item-chances").val(rewardItem.chances);
 | 
						|
	}
 | 
						|
 | 
						|
	$("#field-reward-items-list").append(itemDiv);
 | 
						|
}
 | 
						|
$("#field-add-reward-item-btn").click(function() {
 | 
						|
	addRewardItemToField();
 | 
						|
})
 | 
						|
 | 
						|
function editField(id) {
 | 
						|
	let fieldModal = $("#field-modal");
 | 
						|
 | 
						|
	// Converts from create modal to edit modal
 | 
						|
	fieldModal.data("action", "edit");
 | 
						|
	fieldModal.data("fieldId", id);
 | 
						|
 | 
						|
	$("#delete-field-btn").show();
 | 
						|
	$("#save-field-btn").text( getLocalizedText("menu:save") );
 | 
						|
 | 
						|
	let fieldData = fields[id];
 | 
						|
 | 
						|
	$("#field-label").val(fieldData.label);
 | 
						|
	$("#field-object-model").val(fieldData.data.objectModel);
 | 
						|
	$("#field-radius").val(fieldData.data.radius);
 | 
						|
	$("#field-max-objects").val(fieldData.data.maxObjects);
 | 
						|
	$("#field-respawn-timer").val(fieldData.data.respawnTimer);
 | 
						|
	$("#field-minimum-police").val(fieldData.data.minimumPolice);
 | 
						|
 | 
						|
	$("#field-modal").find("input:radio[name='field-spawn-coords-type'][value='" + fieldData.data.spawnType + "']").prop("checked", true).change();
 | 
						|
 | 
						|
	$("#field-coords-x").val(fieldData.data.coords.x);
 | 
						|
	$("#field-coords-y").val(fieldData.data.coords.y);
 | 
						|
	$("#field-coords-z").val(fieldData.data.coords.z);
 | 
						|
 | 
						|
	$("#field-reward-min-objects-amount").val(fieldData.data.minObjectsAmount);
 | 
						|
	$("#field-reward-max-objects-amount").val(fieldData.data.maxObjectsAmount);
 | 
						|
 | 
						|
	$("#field-required-items-list").empty();
 | 
						|
	if(fieldData.data.requiredItems) {
 | 
						|
		for(let requiredItem of fieldData.data.requiredItems) {
 | 
						|
			addRequiredItemToField(requiredItem);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	$("#field-reward-items-list").empty();
 | 
						|
	if(fieldData.data.rewardItems) {
 | 
						|
		for(let rewardItem of fieldData.data.rewardItems) {
 | 
						|
			addRewardItemToField(rewardItem);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	fieldModal.data("animations", fieldData.data.animations);
 | 
						|
	fieldModal.data("blipData", fieldData.data.blipData);
 | 
						|
	fieldModal.data("allowedJobs", fieldData.data.allowedJobs || null);
 | 
						|
	fieldModal.data("availableSpawnPoints", fieldData.data.availableSpawnPoints);
 | 
						|
 | 
						|
	fieldModal.modal("show");
 | 
						|
}
 | 
						|
 | 
						|
function getRewardItemsFromField() {
 | 
						|
	let rewardItems = [];
 | 
						|
 | 
						|
	$("#field-reward-items-list").find(".reward-item").each(function() {
 | 
						|
		let rewardItem = {
 | 
						|
			type: $(this).find(".reward-item-type").val(),
 | 
						|
			name: $(this).find(".reward-item-name").val(),
 | 
						|
			minQuantity: parseInt( $(this).find(".reward-item-min-quantity").val() ),
 | 
						|
			maxQuantity: parseInt( $(this).find(".reward-item-max-quantity").val() ),
 | 
						|
			chances: parseInt( $(this).find(".reward-item-chances").val() )
 | 
						|
		}
 | 
						|
 | 
						|
		rewardItems.push(rewardItem);
 | 
						|
	});
 | 
						|
 | 
						|
	return rewardItems;
 | 
						|
}
 | 
						|
 | 
						|
function getRequiredItemsFromField() {
 | 
						|
	let requiredItems = [];
 | 
						|
 | 
						|
	$("#field-required-items-list").find(".required-item").each(function() {
 | 
						|
		let requiredItem = {
 | 
						|
			type: $(this).find(".required-item-type").val(),
 | 
						|
			name: $(this).find(".required-item-name").val(),
 | 
						|
			minQuantity: parseInt( $(this).find(".required-item-min-quantity").val() ),
 | 
						|
			loseOnUse: $(this).find(".required-item-lose-on-use-checkbox").prop("checked")
 | 
						|
		}
 | 
						|
 | 
						|
		requiredItems.push(requiredItem);
 | 
						|
	});
 | 
						|
 | 
						|
	return requiredItems;
 | 
						|
}
 | 
						|
 | 
						|
$("#field-form").submit(function(event) {
 | 
						|
	if(isThereAnyErrorInForm(event)) return;
 | 
						|
 | 
						|
	let fieldModal = $("#field-modal");
 | 
						|
	let action = fieldModal.data("action");
 | 
						|
 | 
						|
	let fieldData = {
 | 
						|
		label: $("#field-label").val(),
 | 
						|
		data: {
 | 
						|
			objectModel: $("#field-object-model").val(),
 | 
						|
			radius: parseFloat( $("#field-radius").val() ), 
 | 
						|
			maxObjects: parseInt( $("#field-max-objects").val() ), // Prop objects that can be spawned
 | 
						|
			respawnTimer: parseInt( $("#field-respawn-timer").val() ),
 | 
						|
			minimumPolice: parseInt( $("#field-minimum-police").val() ),
 | 
						|
			animations: fieldModal.data("animations"),
 | 
						|
			blipData: fieldModal.data("blipData") || [getDefaultBlipCustomization()],
 | 
						|
			allowedJobs: fieldModal.data("allowedJobs" || null),
 | 
						|
			coords: {
 | 
						|
				x: parseFloat( $("#field-coords-x").val() ),
 | 
						|
				y: parseFloat( $("#field-coords-y").val() ),
 | 
						|
				z: parseFloat( $("#field-coords-z").val() ),
 | 
						|
			},
 | 
						|
			spawnType: $("input:radio[name='field-spawn-coords-type']:checked").val(),
 | 
						|
			availableSpawnPoints: fieldModal.data("availableSpawnPoints"),
 | 
						|
			minObjectsAmount: parseInt( $("#field-reward-min-objects-amount").val() ),
 | 
						|
			maxObjectsAmount: parseInt( $("#field-reward-max-objects-amount").val() ),
 | 
						|
			rewardItems: getRewardItemsFromField(),
 | 
						|
			requiredItems: getRequiredItemsFromField(),
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	switch(action) {
 | 
						|
		case "create": {
 | 
						|
			$.post(`https://${resName}/createField`, JSON.stringify(fieldData), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					fieldModal.modal("hide");
 | 
						|
					loadFields();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case "edit": {
 | 
						|
			$.post(`https://${resName}/updateField`, JSON.stringify({fieldId: fieldModal.data("fieldId"), fieldData: fieldData}), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					fieldModal.modal("hide");
 | 
						|
					loadFields();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
})
 | 
						|
 | 
						|
$("#delete-field-btn").click(function() {
 | 
						|
	let fieldModal = $("#field-modal");
 | 
						|
	let fieldId = fieldModal.data("fieldId");
 | 
						|
 | 
						|
	$.post(`https://${resName}/deleteField`, JSON.stringify({fieldId: fieldId}), function(isSuccessful) {
 | 
						|
		if(isSuccessful) {
 | 
						|
			fieldModal.modal("hide");
 | 
						|
			loadFields();
 | 
						|
		}
 | 
						|
	});
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
███████  █████  ██████  ███    ███ ███████
 | 
						|
██      ██   ██ ██   ██ ████  ████ ██     
 | 
						|
█████   ███████ ██████  ██ ████ ██ ███████
 | 
						|
██      ██   ██ ██   ██ ██  ██  ██      ██
 | 
						|
██      ██   ██ ██   ██ ██      ██ ███████
 | 
						|
*/
 | 
						|
let farmsDatatable = $("#farms-container").DataTable( {
 | 
						|
	"lengthMenu": [10, 15, 20],
 | 
						|
	"createdRow": function ( row, data, index ) {
 | 
						|
		$(row).addClass("clickable");
 | 
						|
 | 
						|
		$(row).click(function() {
 | 
						|
			let id = parseInt( data[0] );
 | 
						|
 | 
						|
			editFarm(id);
 | 
						|
		})
 | 
						|
	},
 | 
						|
});
 | 
						|
 | 
						|
let farms = {};
 | 
						|
 | 
						|
function loadFarms() {
 | 
						|
	$.post(`https://${resName}/getAllFarms`, {}, async function(rawFarms) {
 | 
						|
 | 
						|
		// Manually create the table to avoid incompatibilities due table indexing
 | 
						|
		farms = {};
 | 
						|
 | 
						|
		for(const[k, farmData] of Object.entries(rawFarms)) {
 | 
						|
			farms[farmData.id] = farmData;
 | 
						|
		}
 | 
						|
 | 
						|
		farmsDatatable.clear();
 | 
						|
 | 
						|
		for(const[id, farmData] of Object.entries(farms)) {
 | 
						|
			farmsDatatable.row.add([
 | 
						|
				id,
 | 
						|
				farmData.label,
 | 
						|
			]);
 | 
						|
		}
 | 
						|
 | 
						|
		farmsDatatable.draw();
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
function setDefaultDataOfFarm() {
 | 
						|
	// Generic
 | 
						|
	$("#farm-label").val("Default");
 | 
						|
	$("#farm-minimum-police").val(0);
 | 
						|
	$("#farm-radius").val(5);
 | 
						|
	
 | 
						|
	// Options
 | 
						|
	$("#farm-always-active").prop("checked", true).change();
 | 
						|
	$("#farm-requires-to-be-in-vehicle").prop("checked", false).change();
 | 
						|
	$("#farm-allowed-vehicles-list").empty();
 | 
						|
 | 
						|
	// Coordinates
 | 
						|
	$("#farm-coords-x").val("");
 | 
						|
	$("#farm-coords-y").val("");
 | 
						|
	$("#farm-coords-z").val("");
 | 
						|
 | 
						|
	// Items
 | 
						|
	$("#farm-reward-min-objects-amount").val(1);
 | 
						|
	$("#farm-reward-max-objects-amount").val(1);
 | 
						|
 | 
						|
	$("#farm-required-items-list").empty();
 | 
						|
	$("#farm-reward-items-list").empty();
 | 
						|
 | 
						|
	// Other
 | 
						|
	let farmModal = $("#farm-modal");
 | 
						|
	farmModal.data("animations", [defaultPlantInteractionAnimData]);
 | 
						|
	farmModal.data("blipData", getDefaultBlipCustomizationForFarms());
 | 
						|
	farmModal.data("markerData", getDefaultMarkerCustomization());
 | 
						|
	farmModal.data("objectData", getDefaultObjectCustomization());
 | 
						|
	farmModal.data("allowedJobs", null);
 | 
						|
}
 | 
						|
 | 
						|
$("#new-farm-btn").click(function() {
 | 
						|
	let farmModal = $("#farm-modal");
 | 
						|
 | 
						|
	// Converts from edit modal to create modal
 | 
						|
	farmModal.data("action", "create");
 | 
						|
	
 | 
						|
	$("#delete-farm-btn").hide();
 | 
						|
	$("#save-farm-btn").text( getLocalizedText("menu:create") );
 | 
						|
	
 | 
						|
	setDefaultDataOfFarm();
 | 
						|
 | 
						|
	farmModal.modal("show");
 | 
						|
});
 | 
						|
 | 
						|
$("#farm-animations-btn").click(async function() {
 | 
						|
	let farmModal = $("#farm-modal");
 | 
						|
 | 
						|
	let oldAnimations = farmModal.data("animations");
 | 
						|
	let newAnimations = await animationsDialog(oldAnimations);
 | 
						|
 | 
						|
	farmModal.data("animations", newAnimations);
 | 
						|
})
 | 
						|
 | 
						|
$("#farm-customize-blip-btn").click(async function() {
 | 
						|
	let farmModal = $("#farm-modal");
 | 
						|
 | 
						|
	let oldBlipData = farmModal.data("blipData");
 | 
						|
	let newBlipData = await blipDialog(oldBlipData);
 | 
						|
 | 
						|
	farmModal.data("blipData", newBlipData);
 | 
						|
})
 | 
						|
 | 
						|
$("#farm-customize-marker-btn").click(async function() {
 | 
						|
	let farmModal = $("#farm-modal");
 | 
						|
 | 
						|
	let oldMarkerData = farmModal.data("markerData");
 | 
						|
	let newMarkerData = await markerDialog(oldMarkerData);
 | 
						|
 | 
						|
	farmModal.data("markerData", newMarkerData);
 | 
						|
})
 | 
						|
 | 
						|
$("#farm-customize-object-btn").click(async function() {
 | 
						|
	let farmModal = $("#farm-modal");
 | 
						|
 | 
						|
	let oldObjectData = farmModal.data("objectData");
 | 
						|
	let newObjectData = await objectCustomizationDialog(oldObjectData);
 | 
						|
 | 
						|
	farmModal.data("objectData", newObjectData);
 | 
						|
});
 | 
						|
 | 
						|
$("#farm-allowed-jobs-btn").click(async function() {
 | 
						|
	let farmModal = $("#farm-modal");
 | 
						|
 | 
						|
	let oldAllowedJobs = farmModal.data("allowedJobs");
 | 
						|
	let newAllowedJobs = await jobsDialog(oldAllowedJobs);
 | 
						|
 | 
						|
	farmModal.data("allowedJobs", newAllowedJobs);
 | 
						|
})
 | 
						|
 | 
						|
$("#farm-current-coords-btn").click(async function() {
 | 
						|
	const coords = await getCurrentCoords();
 | 
						|
	
 | 
						|
	$("#farm-coords-x").val(coords.x);
 | 
						|
	$("#farm-coords-y").val(coords.y);
 | 
						|
	$("#farm-coords-z").val(coords.z);
 | 
						|
})
 | 
						|
 | 
						|
$("#farm-always-active").change(function() {
 | 
						|
	let isChecked = $(this).prop("checked");
 | 
						|
 | 
						|
	$("#farm-active-start-time").prop("disabled", isChecked);
 | 
						|
	$("#farm-active-end-time").prop("disabled", isChecked);
 | 
						|
 | 
						|
	if (isChecked) {
 | 
						|
		$("#farm-active-start-time").val("00:00");
 | 
						|
		$("#farm-active-end-time").val("23:59");
 | 
						|
	}
 | 
						|
});
 | 
						|
 | 
						|
$("#farm-requires-to-be-in-vehicle").change(function() {
 | 
						|
	const isChecked = $(this).prop("checked");
 | 
						|
 | 
						|
	$("#farm-requires-specific-vehicle").prop("disabled", !isChecked);
 | 
						|
 | 
						|
	if (!isChecked) {
 | 
						|
		$("#farm-requires-specific-vehicle").prop("checked", false).change();
 | 
						|
	}
 | 
						|
})
 | 
						|
 | 
						|
$("#farm-requires-specific-vehicle").change(function() {
 | 
						|
	const isChecked = $(this).prop("checked");
 | 
						|
 | 
						|
	$("#farm-allowed-vehicles-div").toggle(isChecked);
 | 
						|
})
 | 
						|
 | 
						|
async function addRequiredItemToFarm(requiredItem) {
 | 
						|
	let itemDiv = $(`
 | 
						|
		<div class="row g-2 row-cols-auto align-items-center text-body my-2 required-item justify-content-center">
 | 
						|
			<button type="button" class="btn-close delete-required-item-btn me-3" ></button>	
 | 
						|
 | 
						|
			<select class="form-select required-item-type" style="width: auto;">
 | 
						|
				<option selected value="item">${getLocalizedText("menu:item")}</option>
 | 
						|
				<option value="account">${getLocalizedText("menu:account")}</option>
 | 
						|
				${await getFramework() == "ESX" ? `<option value="weapon">${getLocalizedText("menu:weapon")}</option>` : ""}
 | 
						|
			</select>
 | 
						|
			
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="text" class="form-control required-item-name" placeholder="Name" required>
 | 
						|
				<label>${ getLocalizedText("menu:object_name") }</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button type="button" class="btn btn-secondary col-auto choose-item-btn" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose") }"><i class="bi bi-list-ul"></i></button>	
 | 
						|
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="number" min=0 class="form-control required-item-min-quantity" placeholder="${getLocalizedText("menu:min_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:min_quantity")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div class="form-check my-auto fs-4 ms-1">
 | 
						|
				<input class="form-check-input required-item-lose-on-use-checkbox" type="checkbox" value="">
 | 
						|
				<label class="form-check-label">${getLocalizedText("menu:lose_on_use")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
	
 | 
						|
	itemDiv.find(".delete-required-item-btn").click(function() {
 | 
						|
		itemDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	itemDiv.find(".choose-item-btn").click(async function() {
 | 
						|
		let objectType = itemDiv.find(".required-item-type").val();
 | 
						|
 | 
						|
		let objectName = await objectDialog(objectType);
 | 
						|
 | 
						|
		itemDiv.find(".required-item-name").val(objectName);
 | 
						|
	}).tooltip();
 | 
						|
 | 
						|
	
 | 
						|
	if(requiredItem) {
 | 
						|
		itemDiv.find(".required-item-type").val(requiredItem.type);
 | 
						|
		itemDiv.find(".required-item-name").val(requiredItem.name);
 | 
						|
		itemDiv.find(".required-item-min-quantity").val(requiredItem.minQuantity);
 | 
						|
		itemDiv.find(".required-item-lose-on-use-checkbox").prop("checked", requiredItem.loseOnUse);
 | 
						|
	}
 | 
						|
 | 
						|
	$("#farm-required-items-list").append(itemDiv);
 | 
						|
}
 | 
						|
$("#farm-add-required-item-btn").click(function() {
 | 
						|
	addRequiredItemToFarm();
 | 
						|
});
 | 
						|
 | 
						|
async function addRewardItemToFarm(rewardItem) {
 | 
						|
	let itemDiv = $(`
 | 
						|
		<div class="row g-2 row-cols-auto align-items-center text-body my-2 reward-item justify-content-center">
 | 
						|
			<button type="button" class="btn-close delete-reward-item-btn me-3" ></button>	
 | 
						|
 | 
						|
			<select class="form-select reward-item-type" style="width: auto;">
 | 
						|
				<option selected value="item">${getLocalizedText("menu:item")}</option>
 | 
						|
				<option value="account">${getLocalizedText("menu:account")}</option>
 | 
						|
				${await getFramework() == "ESX" ? `<option value="weapon">${getLocalizedText("menu:weapon")}</option>` : ""}
 | 
						|
			</select>
 | 
						|
			
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="text" class="form-control reward-item-name" placeholder="Name" required>
 | 
						|
				<label>${ getLocalizedText("menu:object_name") }</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button type="button" class="btn btn-secondary col-auto choose-item-btn" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose") }"><i class="bi bi-list-ul"></i></button>	
 | 
						|
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number" min=0 class="form-control reward-item-min-quantity" placeholder="${getLocalizedText("menu:min_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:min_quantity")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number" min=0 class="form-control reward-item-max-quantity" placeholder="${getLocalizedText("menu:max_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:max_quantity")}</label>
 | 
						|
			</div>
 | 
						|
			
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number"  class="form-control reward-item-chances" placeholder="${getLocalizedText("menu:probability")}" required>
 | 
						|
				<label>${getLocalizedText("menu:probability")}</label>
 | 
						|
			</div>
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
	
 | 
						|
	itemDiv.find(".delete-reward-item-btn").click(function() {
 | 
						|
		itemDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	itemDiv.find(".choose-item-btn").click(async function() {
 | 
						|
		let objectType = itemDiv.find(".reward-item-type").val();
 | 
						|
 | 
						|
		let objectName = await objectDialog(objectType);
 | 
						|
 | 
						|
		itemDiv.find(".reward-item-name").val(objectName);
 | 
						|
	}).tooltip();
 | 
						|
 | 
						|
	
 | 
						|
	if(rewardItem) {
 | 
						|
		itemDiv.find(".reward-item-type").val(rewardItem.type);
 | 
						|
		itemDiv.find(".reward-item-name").val(rewardItem.name);
 | 
						|
		itemDiv.find(".reward-item-min-quantity").val(rewardItem.minQuantity);
 | 
						|
		itemDiv.find(".reward-item-max-quantity").val(rewardItem.maxQuantity);
 | 
						|
		itemDiv.find(".reward-item-chances").val(rewardItem.chances);
 | 
						|
	}
 | 
						|
 | 
						|
	$("#farm-reward-items-list").append(itemDiv);
 | 
						|
}
 | 
						|
$("#farm-add-reward-item-btn").click(function() {
 | 
						|
	addRewardItemToFarm();
 | 
						|
});
 | 
						|
 | 
						|
function addAllowedVehicleToFarm(vehicleName) {
 | 
						|
	let allowedVehicleDiv = $(`
 | 
						|
		<ul class="row g-2 row-cols-auto align-items-center justify-content-center allowed-vehicle mb-2">
 | 
						|
			<button type="button" class="btn-close delete-allowed-vehicle-btn" ></button>	
 | 
						|
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="text" class="form-control vehicle-spawn-name" placeholder="Vehicle name" required>
 | 
						|
				<label>${ getLocalizedText("menu:vehicle_name") }</label>
 | 
						|
			</div>
 | 
						|
		</ul>
 | 
						|
	`);
 | 
						|
 | 
						|
	allowedVehicleDiv.find(".delete-allowed-vehicle-btn").click(function() {
 | 
						|
		allowedVehicleDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	if(vehicleName) {
 | 
						|
		allowedVehicleDiv.find(".vehicle-spawn-name").val(vehicleName);
 | 
						|
	}
 | 
						|
 | 
						|
	$("#farm-allowed-vehicles-list").append(allowedVehicleDiv);
 | 
						|
}
 | 
						|
$("#farm-add-farm-allowed-vehicle-btn").click(function() {
 | 
						|
	addAllowedVehicleToFarm();
 | 
						|
});
 | 
						|
 | 
						|
function editFarm(id) {
 | 
						|
	let farmModal = $("#farm-modal");
 | 
						|
 | 
						|
	// Converts from create modal to edit modal
 | 
						|
	farmModal.data("action", "edit");
 | 
						|
	farmModal.data("farmId", id);
 | 
						|
 | 
						|
	$("#delete-farm-btn").show();
 | 
						|
	$("#save-farm-btn").text( getLocalizedText("menu:save") );
 | 
						|
 | 
						|
	let farmData = farms[id];
 | 
						|
 | 
						|
	// Generic
 | 
						|
	$("#farm-label").val(farmData.label);
 | 
						|
	$("#farm-minimum-police").val(farmData.data.minimumPolice);
 | 
						|
	$("#farm-radius").val(farmData.data.radius);
 | 
						|
 | 
						|
	// Coordinates
 | 
						|
	$("#farm-coords-x").val(farmData.data.coords.x);
 | 
						|
	$("#farm-coords-y").val(farmData.data.coords.y);
 | 
						|
	$("#farm-coords-z").val(farmData.data.coords.z);
 | 
						|
	
 | 
						|
	// Options
 | 
						|
	$("#farm-active-start-time").val(farmData.data.activeTimeStart);
 | 
						|
	$("#farm-active-end-time").val(farmData.data.activeTimeEnd);
 | 
						|
	$("#farm-requires-to-be-in-vehicle").prop("checked", farmData.data.requiresToBeInVehicle).change();
 | 
						|
	$("#farm-requires-specific-vehicle").prop("checked", farmData.data.requiresSpecificVehicle).change();
 | 
						|
 | 
						|
	$("#farm-allowed-vehicles-list").empty();
 | 
						|
	if(farmData.data.allowedVehicles) {
 | 
						|
		for(const vehicleName of Object.keys(farmData.data.allowedVehicles)) {
 | 
						|
			addAllowedVehicleToFarm(vehicleName);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if(farmData.data.activeTimeStart === "00:00" && farmData.data.activeTimeEnd === "23:59") {
 | 
						|
		$("#farm-always-active").prop("checked", true).change();
 | 
						|
	} else {
 | 
						|
		$("#farm-always-active").prop("checked", false).change();
 | 
						|
	}
 | 
						|
 | 
						|
	// Items
 | 
						|
	$("#farm-reward-min-objects-amount").val(farmData.data.minObjectsAmount);
 | 
						|
	$("#farm-reward-max-objects-amount").val(farmData.data.maxObjectsAmount);
 | 
						|
 | 
						|
	$("#farm-required-items-list").empty();
 | 
						|
	if(farmData.data.requiredItems) {
 | 
						|
		for(let requiredItem of farmData.data.requiredItems) {
 | 
						|
			addRequiredItemToFarm(requiredItem);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	$("#farm-reward-items-list").empty();
 | 
						|
	if(farmData.data.rewardItems) {
 | 
						|
		for(let rewardItem of farmData.data.rewardItems) {
 | 
						|
			addRewardItemToFarm(rewardItem);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	farmModal.data("animations", farmData.data.animations);
 | 
						|
	farmModal.data("blipData", farmData.data.blipData);
 | 
						|
	farmModal.data("markerData", farmData.data.markerData);
 | 
						|
	farmModal.data("objectData", farmData.data.objectData);
 | 
						|
	farmModal.data("allowedJobs", farmData.data.allowedJobs || null);
 | 
						|
 | 
						|
	farmModal.modal("show");
 | 
						|
}
 | 
						|
 | 
						|
function getRewardItemsFromFarm() {
 | 
						|
	let rewardItems = [];
 | 
						|
 | 
						|
	$("#farm-reward-items-list").find(".reward-item").each(function() {
 | 
						|
		let rewardItem = {
 | 
						|
			type: $(this).find(".reward-item-type").val(),
 | 
						|
			name: $(this).find(".reward-item-name").val(),
 | 
						|
			minQuantity: parseInt(  $(this).find(".reward-item-min-quantity").val() ),
 | 
						|
			maxQuantity: parseInt(  $(this).find(".reward-item-max-quantity").val() ),
 | 
						|
			chances: parseInt( $(this).find(".reward-item-chances").val() )
 | 
						|
		}
 | 
						|
 | 
						|
		rewardItems.push(rewardItem);
 | 
						|
	});
 | 
						|
 | 
						|
	return rewardItems;
 | 
						|
}
 | 
						|
 | 
						|
function getRequiredItemsFromFarm() {
 | 
						|
	let requiredItems = [];
 | 
						|
 | 
						|
	$("#farm-required-items-list").find(".required-item").each(function() {
 | 
						|
		let requiredItem = {
 | 
						|
			type: $(this).find(".required-item-type").val(),
 | 
						|
			name: $(this).find(".required-item-name").val(),
 | 
						|
			minQuantity: parseInt( $(this).find(".required-item-min-quantity").val() ),
 | 
						|
			loseOnUse: $(this).find(".required-item-lose-on-use-checkbox").prop("checked")
 | 
						|
		}
 | 
						|
 | 
						|
		requiredItems.push(requiredItem);
 | 
						|
	});
 | 
						|
 | 
						|
	return requiredItems;
 | 
						|
}
 | 
						|
 | 
						|
function getFarmAllowedVehicles() {
 | 
						|
	let allowedVehicles = {};
 | 
						|
 | 
						|
	$("#farm-allowed-vehicles-list").find(".allowed-vehicle").each(function() {
 | 
						|
		let vehicleName = $(this).find(".vehicle-spawn-name").val();
 | 
						|
		allowedVehicles[vehicleName] = true;
 | 
						|
	});
 | 
						|
 | 
						|
	return allowedVehicles;
 | 
						|
}
 | 
						|
 | 
						|
$("#farm-form").submit(function(event) {
 | 
						|
	if(isThereAnyErrorInForm(event)) return;
 | 
						|
 | 
						|
	let farmModal = $("#farm-modal");
 | 
						|
	let action = farmModal.data("action");
 | 
						|
 | 
						|
	let farmData = {
 | 
						|
		label: $("#farm-label").val(),
 | 
						|
		data: {
 | 
						|
			minimumPolice: parseInt( $("#farm-minimum-police").val() ),
 | 
						|
			radius: parseFloat( $("#farm-radius").val() ),
 | 
						|
			animations: farmModal.data("animations"),
 | 
						|
			blipData: farmModal.data("blipData") || [getDefaultBlipCustomization()],
 | 
						|
			markerData: farmModal.data("markerData") || [getDefaultMarkerCustomization()],
 | 
						|
			objectData: farmModal.data("objectData") || [getDefaultObjectCustomization()],
 | 
						|
			allowedJobs: farmModal.data("allowedJobs" || null),
 | 
						|
			coords: {
 | 
						|
				x: parseFloat( $("#farm-coords-x").val() ),
 | 
						|
				y: parseFloat( $("#farm-coords-y").val() ),
 | 
						|
				z: parseFloat( $("#farm-coords-z").val() ),
 | 
						|
			},
 | 
						|
			minObjectsAmount: parseInt( $("#farm-reward-min-objects-amount").val() ),
 | 
						|
			maxObjectsAmount: parseInt( $("#farm-reward-max-objects-amount").val() ),
 | 
						|
			rewardItems: getRewardItemsFromFarm(),
 | 
						|
			requiredItems: getRequiredItemsFromFarm(),
 | 
						|
			activeTimeStart: $("#farm-active-start-time").val(),
 | 
						|
			activeTimeEnd: $("#farm-active-end-time").val(),
 | 
						|
			requiresToBeInVehicle: $("#farm-requires-to-be-in-vehicle").prop("checked"),
 | 
						|
			requiresSpecificVehicle: $("#farm-requires-specific-vehicle").prop("checked"),
 | 
						|
			allowedVehicles: getFarmAllowedVehicles()
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	switch(action) {
 | 
						|
		case "create": {
 | 
						|
			$.post(`https://${resName}/createFarm`, JSON.stringify(farmData), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					farmModal.modal("hide");
 | 
						|
					loadFarms();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case "edit": {
 | 
						|
			$.post(`https://${resName}/updateFarm`, JSON.stringify({farmId: farmModal.data("farmId"), farmData: farmData}), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					farmModal.modal("hide");
 | 
						|
					loadFarms();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
})
 | 
						|
 | 
						|
$("#delete-farm-btn").click(function() {
 | 
						|
	let farmModal = $("#farm-modal");
 | 
						|
	let farmId = farmModal.data("farmId");
 | 
						|
 | 
						|
	$.post(`https://${resName}/deleteFarm`, JSON.stringify({farmId: farmId}), function(isSuccessful) {
 | 
						|
		if(isSuccessful) {
 | 
						|
			farmModal.modal("hide");
 | 
						|
			loadFarms();
 | 
						|
		}
 | 
						|
	});
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
██     ██  ██████  ██████  ██   ██ ██████  ███████ ███    ██  ██████ ██   ██ ███████ ███████
 | 
						|
██     ██ ██    ██ ██   ██ ██  ██  ██   ██ ██      ████   ██ ██      ██   ██ ██      ██     
 | 
						|
██  █  ██ ██    ██ ██████  █████   ██████  █████   ██ ██  ██ ██      ███████ █████   ███████
 | 
						|
██ ███ ██ ██    ██ ██   ██ ██  ██  ██   ██ ██      ██  ██ ██ ██      ██   ██ ██           ██
 | 
						|
 ███ ███   ██████  ██   ██ ██   ██ ██████  ███████ ██   ████  ██████ ██   ██ ███████ ███████
 | 
						|
*/
 | 
						|
let workbenchesDatatable = $("#workbenches-container").DataTable( {
 | 
						|
	"lengthMenu": [10, 15, 20],
 | 
						|
	"createdRow": function ( row, data, index ) {
 | 
						|
		$(row).addClass("clickable");
 | 
						|
 | 
						|
		$(row).click(function() {
 | 
						|
			let id = parseInt( data[0] );
 | 
						|
 | 
						|
			editWorkbench(id);
 | 
						|
		})
 | 
						|
	},
 | 
						|
});
 | 
						|
 | 
						|
let workbenches = {};
 | 
						|
 | 
						|
function loadWorkbenches() {
 | 
						|
	$.post(`https://${resName}/getAllWorkbenches`, {}, async function(rawWorkbenches) {
 | 
						|
		// Manually create the table to avoid incompatibilities due table indexing
 | 
						|
		workbenches = {};
 | 
						|
 | 
						|
		for(const[k, workbenchData] of Object.entries(rawWorkbenches)) {
 | 
						|
			workbenches[workbenchData.id] = workbenchData;
 | 
						|
		}
 | 
						|
 | 
						|
		workbenchesDatatable.clear();
 | 
						|
 | 
						|
		for(const[id, workbenchData] of Object.entries(workbenches)) {
 | 
						|
			workbenchesDatatable.row.add([
 | 
						|
				id,
 | 
						|
				workbenchData.label,
 | 
						|
			]);
 | 
						|
		}
 | 
						|
 | 
						|
		workbenchesDatatable.draw();
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
function setDefaultDataOfWorkbench() {
 | 
						|
	// Generic
 | 
						|
	$("#workbench-label").val("Default");
 | 
						|
	$("#workbench-minimum-police").val(0);
 | 
						|
	$("#workbench-radius").val(5);
 | 
						|
 | 
						|
	// Coordinates
 | 
						|
	$("#workbench-coords-x").val("");
 | 
						|
	$("#workbench-coords-y").val("");
 | 
						|
	$("#workbench-coords-z").val("");
 | 
						|
 | 
						|
	// Other
 | 
						|
	let workbenchModal = $("#workbench-modal");
 | 
						|
	workbenchModal.data("animations", [defaultWorkbenchAnimData]);
 | 
						|
	workbenchModal.data("blipData", getDefaultBlipCustomizationForWorkbenches());
 | 
						|
	workbenchModal.data("markerData", getDefaultMarkerCustomization());
 | 
						|
	workbenchModal.data("allowedJobs", null);
 | 
						|
	workbenchModal.data("objectData", getDefaultObjectCustomizationForWorkbenches());
 | 
						|
 | 
						|
	// Empty craftings
 | 
						|
	$("#workbench-craftings-list").empty();
 | 
						|
}
 | 
						|
 | 
						|
$("#new-workbench-btn").click(function() {
 | 
						|
	let workbenchModal = $("#workbench-modal");
 | 
						|
 | 
						|
	// Converts from edit modal to create modal
 | 
						|
	workbenchModal.data("action", "create");
 | 
						|
	
 | 
						|
	$("#delete-workbench-btn").hide();
 | 
						|
	$("#save-workbench-btn").text( getLocalizedText("menu:create") );
 | 
						|
	
 | 
						|
	setDefaultDataOfWorkbench();
 | 
						|
 | 
						|
	workbenchModal.modal("show");
 | 
						|
})
 | 
						|
 | 
						|
$("#workbench-animations-btn").click(async function() {
 | 
						|
	let workbenchModal = $("#workbench-modal");
 | 
						|
 | 
						|
	let oldAnimations = workbenchModal.data("animations");
 | 
						|
	let newAnimations = await animationsDialog(oldAnimations);
 | 
						|
 | 
						|
	workbenchModal.data("animations", newAnimations);
 | 
						|
})
 | 
						|
 | 
						|
$("#workbench-customize-blip-btn").click(async function() {
 | 
						|
	let workbenchModal = $("#workbench-modal");
 | 
						|
 | 
						|
	let oldBlipData = workbenchModal.data("blipData");
 | 
						|
	let newBlipData = await blipDialog(oldBlipData);
 | 
						|
 | 
						|
	workbenchModal.data("blipData", newBlipData);
 | 
						|
})
 | 
						|
 | 
						|
$("#workbench-customize-marker-btn").click(async function() {
 | 
						|
	let workbenchModal = $("#workbench-modal");
 | 
						|
 | 
						|
	let oldMarkerData = workbenchModal.data("markerData");
 | 
						|
	let newMarkerData = await markerDialog(oldMarkerData);
 | 
						|
 | 
						|
	workbenchModal.data("markerData", newMarkerData);
 | 
						|
})
 | 
						|
 | 
						|
$("#workbench-allowed-jobs-btn").click(async function() {
 | 
						|
	let workbenchModal = $("#workbench-modal");
 | 
						|
 | 
						|
	let oldAllowedJobs = workbenchModal.data("allowedJobs");
 | 
						|
	let newAllowedJobs = await jobsDialog(oldAllowedJobs);
 | 
						|
 | 
						|
	workbenchModal.data("allowedJobs", newAllowedJobs);
 | 
						|
})
 | 
						|
 | 
						|
$("#workbench-current-coords-btn").click(async function() {
 | 
						|
	const coords = await getCurrentCoords();
 | 
						|
	
 | 
						|
	$("#workbench-coords-x").val(coords.x);
 | 
						|
	$("#workbench-coords-y").val(coords.y);
 | 
						|
	$("#workbench-coords-z").val(coords.z);
 | 
						|
})
 | 
						|
 | 
						|
$("#workbench-customize-object-btn").click(async function() {
 | 
						|
	let workbenchModal = $("#workbench-modal");
 | 
						|
 | 
						|
	let oldObjectData = workbenchModal.data("objectData");
 | 
						|
	let newObjectData = await objectCustomizationDialog(oldObjectData);
 | 
						|
 | 
						|
	workbenchModal.data("objectData", newObjectData);
 | 
						|
});
 | 
						|
 | 
						|
async function addRequiredItemToCrafting(craftingDiv, requiredItem) {
 | 
						|
	let itemDiv = $(`
 | 
						|
		<div class="row g-2 row-cols-auto align-items-center text-body my-2 required-item justify-content-center">
 | 
						|
			<button type="button" class="btn-close delete-required-item-btn me-3" ></button>	
 | 
						|
 | 
						|
			<select class="form-select required-item-type" style="width: auto;">
 | 
						|
				<option selected value="item">${getLocalizedText("menu:item")}</option>
 | 
						|
				<option value="account">${getLocalizedText("menu:account")}</option>
 | 
						|
				${await getFramework() == "ESX" ? `<option value="weapon">${getLocalizedText("menu:weapon")}</option>` : ""}
 | 
						|
			</select>
 | 
						|
			
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="text" class="form-control required-item-name" placeholder="Name" required>
 | 
						|
				<label>${ getLocalizedText("menu:object_name") }</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button type="button" class="btn btn-secondary col-auto choose-item-btn" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose") }"><i class="bi bi-list-ul"></i></button>	
 | 
						|
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="number" min=0 class="form-control required-item-min-quantity" placeholder="${getLocalizedText("menu:min_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:min_quantity")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div class="form-check my-auto fs-4 ms-1">
 | 
						|
				<input class="form-check-input required-item-lose-on-use-checkbox" type="checkbox" value="">
 | 
						|
				<label class="form-check-label">${getLocalizedText("menu:lose_on_use")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
	
 | 
						|
	itemDiv.find(".delete-required-item-btn").click(function() {
 | 
						|
		itemDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	itemDiv.find(".choose-item-btn").click(async function() {
 | 
						|
		let objectType = itemDiv.find(".required-item-type").val();
 | 
						|
 | 
						|
		let objectName = await objectDialog(objectType);
 | 
						|
 | 
						|
		itemDiv.find(".required-item-name").val(objectName);
 | 
						|
	}).tooltip();
 | 
						|
 | 
						|
	
 | 
						|
	if(requiredItem) {
 | 
						|
		itemDiv.find(".required-item-type").val(requiredItem.type);
 | 
						|
		itemDiv.find(".required-item-name").val(requiredItem.name);
 | 
						|
		itemDiv.find(".required-item-min-quantity").val(requiredItem.minQuantity);
 | 
						|
		itemDiv.find(".required-item-lose-on-use-checkbox").prop("checked", requiredItem.loseOnUse);
 | 
						|
	}
 | 
						|
 | 
						|
	craftingDiv.find(".required-items-list").append(itemDiv);
 | 
						|
}
 | 
						|
 
 | 
						|
async function addRewardItemToCrafting(craftingDiv, rewardItem) {
 | 
						|
	let itemDiv = $(`
 | 
						|
		<div class="row g-2 row-cols-auto align-items-center text-body my-2 reward-item justify-content-center">
 | 
						|
			<button type="button" class="btn-close delete-reward-item-btn me-3" ></button>	
 | 
						|
 | 
						|
			<select class="form-select reward-item-type" style="width: auto;">
 | 
						|
				<option selected value="item">${getLocalizedText("menu:item")}</option>
 | 
						|
				<option value="account">${getLocalizedText("menu:account")}</option>
 | 
						|
				${await getFramework() == "ESX" ? `<option value="weapon">${getLocalizedText("menu:weapon")}</option>` : ""}
 | 
						|
			</select>
 | 
						|
			
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="text" class="form-control reward-item-name" placeholder="Name" required>
 | 
						|
				<label>${ getLocalizedText("menu:object_name") }</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button type="button" class="btn btn-secondary col-auto choose-item-btn" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose") }"><i class="bi bi-list-ul"></i></button>	
 | 
						|
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number" min="1" class="form-control reward-item-quantity" placeholder="${getLocalizedText("menu:quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:quantity")}</label>
 | 
						|
			</div>
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
	
 | 
						|
	itemDiv.find(".delete-reward-item-btn").click(function() {
 | 
						|
		itemDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	itemDiv.find(".choose-item-btn").click(async function() {
 | 
						|
		let objectType = itemDiv.find(".reward-item-type").val();
 | 
						|
 | 
						|
		let objectName = await objectDialog(objectType);
 | 
						|
 | 
						|
		itemDiv.find(".reward-item-name").val(objectName);
 | 
						|
	}).tooltip();
 | 
						|
 | 
						|
	
 | 
						|
	if(rewardItem) {
 | 
						|
		itemDiv.find(".reward-item-type").val(rewardItem.type);
 | 
						|
		itemDiv.find(".reward-item-name").val(rewardItem.name);
 | 
						|
		itemDiv.find(".reward-item-quantity").val(rewardItem.quantity);
 | 
						|
	}
 | 
						|
 | 
						|
	craftingDiv.find(".reward-items-list").append(itemDiv);
 | 
						|
}
 | 
						|
 | 
						|
function addCraftingToWorkbench(craftingData) {
 | 
						|
	let craftingDiv = $(`
 | 
						|
		<div class="mb-4 crafting">
 | 
						|
			<div class="col-12 d-inline-block">
 | 
						|
				<button type="button" class="btn btn-danger float-end col-auto delete-crafting-btn"><i class="bi bi-trash-fill"></i></button>	
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div class="form-floating crafting-label-div col-3 mx-auto my-3" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:crafting_label_description") }">
 | 
						|
				<input type="text" class="form-control crafting-label" placeholder="Label">
 | 
						|
				<label>${ getLocalizedText("menu:crafting_label") }</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<p class="text-center fs-4">${getLocalizedText("menu:required_items_list")}</p>
 | 
						|
 | 
						|
			<div class="required-items-list">
 | 
						|
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button class="btn btn-secondary add-required-item-btn" type="button">${getLocalizedText("menu:add_required_item")}</button>
 | 
						|
 | 
						|
			<p class="text-center fs-4">${getLocalizedText("menu:reward_items_list")}</p>
 | 
						|
 | 
						|
			<div class="reward-items-list">
 | 
						|
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button class="btn btn-secondary add-reward-item-btn" type="button">${getLocalizedText("menu:add_reward_item")}</button>
 | 
						|
 | 
						|
			<hr>
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
 | 
						|
	craftingDiv.find(".crafting-label-div").tooltip();
 | 
						|
 | 
						|
	craftingDiv.find(".add-required-item-btn").click(function() {
 | 
						|
		addRequiredItemToCrafting(craftingDiv);
 | 
						|
	});
 | 
						|
 | 
						|
	craftingDiv.find(".add-reward-item-btn").click(function() {
 | 
						|
		addRewardItemToCrafting(craftingDiv);
 | 
						|
	});
 | 
						|
 | 
						|
	craftingDiv.find(".delete-crafting-btn").click(function() {
 | 
						|
		craftingDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	if(craftingData) {
 | 
						|
		for(let requiredItem of craftingData.requiredItems) {
 | 
						|
			addRequiredItemToCrafting(craftingDiv, requiredItem);
 | 
						|
		}
 | 
						|
 | 
						|
		for(let rewardItem of craftingData.rewardItems) {
 | 
						|
			addRewardItemToCrafting(craftingDiv, rewardItem);
 | 
						|
		}
 | 
						|
 | 
						|
		craftingDiv.find(".crafting-label").val(craftingData.label);
 | 
						|
	} else {
 | 
						|
		addRequiredItemToCrafting(craftingDiv);
 | 
						|
		addRewardItemToCrafting(craftingDiv);
 | 
						|
	}
 | 
						|
 | 
						|
	$("#workbench-craftings-list").append(craftingDiv);
 | 
						|
}
 | 
						|
$("#workbench-add-crafting-btn").click(async function() {
 | 
						|
	addCraftingToWorkbench();
 | 
						|
});
 | 
						|
 | 
						|
function getAllCraftingsFromWorkbench() {
 | 
						|
	let craftings = [];
 | 
						|
 | 
						|
	$("#workbench-craftings-list").find(".crafting").each(function() {
 | 
						|
		let crafting = {
 | 
						|
			label: $(this).find(".crafting-label").val(),
 | 
						|
			requiredItems: [],
 | 
						|
			rewardItems: []
 | 
						|
		};
 | 
						|
 | 
						|
		$(this).find(".required-item").each(function() {
 | 
						|
			let requiredItem = {
 | 
						|
				type: $(this).find(".required-item-type").val(),
 | 
						|
				name: $(this).find(".required-item-name").val(),
 | 
						|
				minQuantity: parseInt( $(this).find(".required-item-min-quantity").val() ),
 | 
						|
				loseOnUse: $(this).find(".required-item-lose-on-use-checkbox").prop("checked")
 | 
						|
			};
 | 
						|
 | 
						|
			crafting.requiredItems.push(requiredItem);
 | 
						|
		});
 | 
						|
 | 
						|
		$(this).find(".reward-item").each(function() {
 | 
						|
			let rewardItem = {
 | 
						|
				type: $(this).find(".reward-item-type").val(),
 | 
						|
				name: $(this).find(".reward-item-name").val(),
 | 
						|
				quantity: parseInt( $(this).find(".reward-item-quantity").val() )
 | 
						|
			};
 | 
						|
 | 
						|
			crafting.rewardItems.push(rewardItem);
 | 
						|
		});
 | 
						|
 | 
						|
		craftings.push(crafting);
 | 
						|
	});
 | 
						|
 | 
						|
	return craftings;
 | 
						|
}
 | 
						|
 | 
						|
function editWorkbench(id) {
 | 
						|
	let workbenchModal = $("#workbench-modal");
 | 
						|
 | 
						|
	// Converts from create modal to edit modal
 | 
						|
	workbenchModal.data("action", "edit");
 | 
						|
	workbenchModal.data("workbenchId", id);
 | 
						|
 | 
						|
	$("#delete-workbench-btn").show();
 | 
						|
	$("#save-workbench-btn").text( getLocalizedText("menu:save") );
 | 
						|
 | 
						|
	let workbenchData = workbenches[id];
 | 
						|
 | 
						|
	// Generic
 | 
						|
	$("#workbench-label").val(workbenchData.label);
 | 
						|
	$("#workbench-minimum-police").val(workbenchData.data.minimumPolice);
 | 
						|
	$("#workbench-radius").val(workbenchData.data.radius);
 | 
						|
 | 
						|
	// Coordinates
 | 
						|
	$("#workbench-coords-x").val(workbenchData.data.coords.x);
 | 
						|
	$("#workbench-coords-y").val(workbenchData.data.coords.y);
 | 
						|
	$("#workbench-coords-z").val(workbenchData.data.coords.z);
 | 
						|
 | 
						|
	// Craftings
 | 
						|
	$("#workbench-craftings-list").empty();
 | 
						|
	for(let craftingData of workbenchData.data.craftings) {
 | 
						|
		addCraftingToWorkbench(craftingData);
 | 
						|
	}
 | 
						|
 | 
						|
	workbenchModal.data("animations", workbenchData.data.animations);
 | 
						|
	workbenchModal.data("blipData", workbenchData.data.blipData);
 | 
						|
	workbenchModal.data("markerData", workbenchData.data.markerData);
 | 
						|
	workbenchModal.data("allowedJobs", workbenchData.data.allowedJobs || null);
 | 
						|
	workbenchModal.data("objectData", workbenchData.data.objectData);
 | 
						|
 | 
						|
	workbenchModal.modal("show");
 | 
						|
}
 | 
						|
 | 
						|
$("#workbench-form").submit(function(event) {
 | 
						|
	if(isThereAnyErrorInForm(event)) return;
 | 
						|
 | 
						|
	let workbenchModal = $("#workbench-modal");
 | 
						|
	let action = workbenchModal.data("action");
 | 
						|
 | 
						|
	let workbenchData = {
 | 
						|
		label: $("#workbench-label").val(),
 | 
						|
		data: {
 | 
						|
			minimumPolice: parseInt( $("#workbench-minimum-police").val() ),
 | 
						|
			radius: parseFloat( $("#workbench-radius").val() ),
 | 
						|
			animations: workbenchModal.data("animations"),
 | 
						|
			blipData: workbenchModal.data("blipData") || [getDefaultBlipCustomization()],
 | 
						|
			markerData: workbenchModal.data("markerData") || [getDefaultMarkerCustomization()],
 | 
						|
			objectData: workbenchModal.data("objectData") || [getDefaultObjectCustomization()],
 | 
						|
			allowedJobs: workbenchModal.data("allowedJobs" || null),
 | 
						|
			coords: {
 | 
						|
				x: parseFloat( $("#workbench-coords-x").val() ),
 | 
						|
				y: parseFloat( $("#workbench-coords-y").val() ),
 | 
						|
				z: parseFloat( $("#workbench-coords-z").val() ),
 | 
						|
			},
 | 
						|
			craftings: getAllCraftingsFromWorkbench()
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	switch(action) {
 | 
						|
		case "create": {
 | 
						|
			$.post(`https://${resName}/createWorkbench`, JSON.stringify(workbenchData), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					workbenchModal.modal("hide");
 | 
						|
					loadWorkbenches();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case "edit": {
 | 
						|
			$.post(`https://${resName}/updateWorkbench`, JSON.stringify({workbenchId: workbenchModal.data("workbenchId"), workbenchData: workbenchData}), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					workbenchModal.modal("hide");
 | 
						|
					loadWorkbenches();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
})
 | 
						|
 | 
						|
$("#delete-workbench-btn").click(function() {
 | 
						|
	let workbenchModal = $("#workbench-modal");
 | 
						|
	let workbenchId = workbenchModal.data("workbenchId");
 | 
						|
 | 
						|
	$.post(`https://${resName}/deleteWorkbench`, JSON.stringify({workbenchId: workbenchId}), function(isSuccessful) {
 | 
						|
		if(isSuccessful) {
 | 
						|
			workbenchModal.modal("hide");
 | 
						|
			loadWorkbenches();
 | 
						|
		}
 | 
						|
	});
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
███████  ██████  ██    ██ ███    ██ ██████  ██████  ██ ███████ ███████
 | 
						|
██      ██    ██ ██    ██ ████   ██ ██   ██ ██   ██ ██ ██      ██     
 | 
						|
█████   ██    ██ ██    ██ ██ ██  ██ ██   ██ ██████  ██ █████   ███████
 | 
						|
██      ██    ██ ██    ██ ██  ██ ██ ██   ██ ██   ██ ██ ██           ██
 | 
						|
██       ██████   ██████  ██   ████ ██████  ██   ██ ██ ███████ ███████
 | 
						|
*/
 | 
						|
let foundriesDatatable = $("#foundries-container").DataTable( {
 | 
						|
	"lengthMenu": [10, 15, 20],
 | 
						|
	"createdRow": function ( row, data, index ) {
 | 
						|
		$(row).addClass("clickable");
 | 
						|
 | 
						|
		$(row).click(function() {
 | 
						|
			let id = parseInt( data[0] );
 | 
						|
 | 
						|
			editFoundry(id);
 | 
						|
		})
 | 
						|
	},
 | 
						|
});
 | 
						|
 | 
						|
let foundries = {};
 | 
						|
 | 
						|
function loadFoundries() {
 | 
						|
	$.post(`https://${resName}/getAllFoundries`, {}, async function(rawFoundries) {
 | 
						|
		// Manually create the table to avoid incompatibilities due table indexing
 | 
						|
		foundries = {};
 | 
						|
 | 
						|
		for(const[k, foundryData] of Object.entries(rawFoundries)) {
 | 
						|
			foundries[foundryData.id] = foundryData;
 | 
						|
		}
 | 
						|
 | 
						|
		foundriesDatatable.clear();
 | 
						|
 | 
						|
		for(const[id, foundryData] of Object.entries(foundries)) {
 | 
						|
			foundriesDatatable.row.add([
 | 
						|
				id,
 | 
						|
				foundryData.label,
 | 
						|
			]);
 | 
						|
		}
 | 
						|
 | 
						|
		foundriesDatatable.draw();
 | 
						|
	})
 | 
						|
}
 | 
						|
function setDefaultDataOfFoundry() {
 | 
						|
	// Generic
 | 
						|
	$("#foundry-label").val("Default");
 | 
						|
	$("#foundry-minimum-police").val(0);
 | 
						|
	$("#foundry-radius").val(5);
 | 
						|
	$("#foundry-explode-on-failure-checkbox").prop("checked", false).change();
 | 
						|
	$("#foundry-alert-police-on-failure-checkbox").prop("checked", false).change();
 | 
						|
 | 
						|
	// Coordinates
 | 
						|
	$("#foundry-coords-x").val("");
 | 
						|
	$("#foundry-coords-y").val("");
 | 
						|
	$("#foundry-coords-z").val("");
 | 
						|
 | 
						|
	// Other
 | 
						|
	let foundryModal = $("#foundry-modal");
 | 
						|
	foundryModal.data("blipData", getDefaultBlipCustomizationForFoundries());
 | 
						|
	foundryModal.data("markerData", getDefaultMarkerCustomization());
 | 
						|
	foundryModal.data("allowedJobs", null);
 | 
						|
	foundryModal.data("objectData", getDefaultObjectCustomization());
 | 
						|
}
 | 
						|
 | 
						|
$("#new-foundry-btn").click(function() {
 | 
						|
	let foundryModal = $("#foundry-modal");
 | 
						|
 | 
						|
	// Converts from edit modal to create modal
 | 
						|
	foundryModal.data("action", "create");
 | 
						|
	
 | 
						|
	$("#delete-foundry-btn").hide();
 | 
						|
	$("#save-foundry-btn").text( getLocalizedText("menu:create") );
 | 
						|
	
 | 
						|
	setDefaultDataOfFoundry();
 | 
						|
 | 
						|
	foundryModal.modal("show");
 | 
						|
});
 | 
						|
 | 
						|
$("#foundry-customize-blip-btn").click(async function() {
 | 
						|
	let foundryModal = $("#foundry-modal");
 | 
						|
 | 
						|
	let oldBlipData = foundryModal.data("blipData");
 | 
						|
	let newBlipData = await blipDialog(oldBlipData);
 | 
						|
 | 
						|
	foundryModal.data("blipData", newBlipData);
 | 
						|
});
 | 
						|
 | 
						|
$("#foundry-customize-marker-btn").click(async function() {
 | 
						|
	let foundryModal = $("#foundry-modal");
 | 
						|
 | 
						|
	let oldMarkerData = foundryModal.data("markerData");
 | 
						|
	let newMarkerData = await markerDialog(oldMarkerData);
 | 
						|
 | 
						|
	foundryModal.data("markerData", newMarkerData);
 | 
						|
});
 | 
						|
 | 
						|
$("#foundry-customize-object-btn").click(async function() {
 | 
						|
	let foundryModal = $("#foundry-modal");
 | 
						|
 | 
						|
	let oldObjectData = foundryModal.data("objectData");
 | 
						|
	let newObjectData = await objectCustomizationDialog(oldObjectData);
 | 
						|
 | 
						|
	foundryModal.data("objectData", newObjectData);
 | 
						|
});
 | 
						|
 | 
						|
$("#foundry-allowed-jobs-btn").click(async function() {
 | 
						|
	let foundryModal = $("#foundry-modal");
 | 
						|
 | 
						|
	let oldAllowedJobs = foundryModal.data("allowedJobs");
 | 
						|
	let newAllowedJobs = await jobsDialog(oldAllowedJobs);
 | 
						|
 | 
						|
	foundryModal.data("allowedJobs", newAllowedJobs);
 | 
						|
});
 | 
						|
 | 
						|
$("#foundry-current-coords-btn").click(async function() {
 | 
						|
	const coords = await getCurrentCoords();
 | 
						|
	
 | 
						|
	$("#foundry-coords-x").val(coords.x);
 | 
						|
	$("#foundry-coords-y").val(coords.y);
 | 
						|
	$("#foundry-coords-z").val(coords.z);
 | 
						|
});
 | 
						|
 | 
						|
async function chooseAllowedFormulas() {
 | 
						|
	let foundryModal = $("#foundry-modal");
 | 
						|
 | 
						|
	let oldAllowedFormulas = foundryModal.data("allowedFormulas");
 | 
						|
	let newAllowedFormulas = await formulasDialog(oldAllowedFormulas);
 | 
						|
 | 
						|
	foundryModal.data("allowedFormulas", newAllowedFormulas);
 | 
						|
}
 | 
						|
 | 
						|
$("#foundry-allowed-formulas-btn").click(async function() {
 | 
						|
	chooseAllowedFormulas()
 | 
						|
});
 | 
						|
 | 
						|
$("#foundry-explode-on-failure-checkbox").change(function() {
 | 
						|
	const isEnabled = $(this).prop("checked");
 | 
						|
 | 
						|
	$("#foundry-seconds-before-explosion-div").toggle(isEnabled);
 | 
						|
	$("#foundry-seconds-before-explosion").prop("required", isEnabled);
 | 
						|
 | 
						|
	if(!isEnabled) {
 | 
						|
		$("#foundry-seconds-before-explosion").val(0);
 | 
						|
	}
 | 
						|
})
 | 
						|
 | 
						|
$("#foundry-alert-police-on-failure-checkbox").change(function() {
 | 
						|
	const isEnabled = $(this).prop("checked");
 | 
						|
 | 
						|
	$("#foundry-alert-police-probability-div").toggle(isEnabled);
 | 
						|
	$("#foundry-alert-police-probability").prop("required", isEnabled);
 | 
						|
 | 
						|
	if(!isEnabled) {
 | 
						|
		$("#foundry-alert-police-probability").val(0);
 | 
						|
	}
 | 
						|
})
 | 
						|
 | 
						|
function editFoundry(id) {
 | 
						|
	let foundryModal = $("#foundry-modal");
 | 
						|
 | 
						|
	// Converts from create modal to edit modal
 | 
						|
	foundryModal.data("action", "edit");
 | 
						|
	foundryModal.data("foundryId", id);
 | 
						|
 | 
						|
	$("#delete-foundry-btn").show();
 | 
						|
	$("#save-foundry-btn").text( getLocalizedText("menu:save") );
 | 
						|
 | 
						|
	let foundryData = foundries[id];
 | 
						|
 | 
						|
	// Generic
 | 
						|
	$("#foundry-label").val(foundryData.label);
 | 
						|
	$("#foundry-minimum-police").val(foundryData.data.minimumPolice);
 | 
						|
	$("#foundry-radius").val(foundryData.data.radius);
 | 
						|
	$("#foundry-explode-on-failure-checkbox").prop("checked", foundryData.data.explodeOnFailure).change();
 | 
						|
	$("#foundry-seconds-before-explosion").val(foundryData.data.secondsBeforeExplosion);
 | 
						|
	$("#foundry-alert-police-on-failure-checkbox").prop("checked", foundryData.data.alertPoliceOnFailure).change();
 | 
						|
	$("#foundry-alert-police-probability").val(foundryData.data.alertPoliceProbability);
 | 
						|
 | 
						|
	// Coordinates
 | 
						|
	$("#foundry-coords-x").val(foundryData.data.coords.x);
 | 
						|
	$("#foundry-coords-y").val(foundryData.data.coords.y);
 | 
						|
	$("#foundry-coords-z").val(foundryData.data.coords.z);
 | 
						|
 | 
						|
	foundryModal.data("blipData", foundryData.data.blipData);
 | 
						|
	foundryModal.data("markerData", foundryData.data.markerData);
 | 
						|
	foundryModal.data("allowedJobs", foundryData.data.allowedJobs || null);
 | 
						|
	foundryModal.data("allowedFormulas", foundryData.data.allowedFormulas);
 | 
						|
	foundryModal.data("objectData", foundryData.data.objectData);
 | 
						|
 | 
						|
	foundryModal.modal("show");
 | 
						|
}
 | 
						|
 | 
						|
$("#foundry-form").submit(function(event) {
 | 
						|
	if(isThereAnyErrorInForm(event)) return;
 | 
						|
 | 
						|
	let foundryModal = $("#foundry-modal");
 | 
						|
	let action = foundryModal.data("action");
 | 
						|
 | 
						|
	let foundryData = {
 | 
						|
		label: $("#foundry-label").val(),
 | 
						|
		data: {
 | 
						|
			minimumPolice: parseInt( $("#foundry-minimum-police").val() ),
 | 
						|
			radius: parseFloat( $("#foundry-radius").val() ),
 | 
						|
			explodeOnFailure: $("#foundry-explode-on-failure-checkbox").prop("checked"),
 | 
						|
			secondsBeforeExplosion: parseInt( $("#foundry-seconds-before-explosion").val() ),
 | 
						|
			alertPoliceOnFailure: $("#foundry-alert-police-on-failure-checkbox").prop("checked"),
 | 
						|
			alertPoliceProbability: parseInt( $("#foundry-alert-police-probability").val() ),
 | 
						|
			blipData: foundryModal.data("blipData") || [getDefaultBlipCustomization()],
 | 
						|
			markerData: foundryModal.data("markerData") || [getDefaultMarkerCustomization()],
 | 
						|
			objectData: foundryModal.data("objectData") || [getDefaultObjectCustomization()],
 | 
						|
			allowedJobs: foundryModal.data("allowedJobs" || null),
 | 
						|
			allowedFormulas: foundryModal.data("allowedFormulas"),
 | 
						|
			coords: {
 | 
						|
				x: parseFloat( $("#foundry-coords-x").val() ),
 | 
						|
				y: parseFloat( $("#foundry-coords-y").val() ),
 | 
						|
				z: parseFloat( $("#foundry-coords-z").val() ),
 | 
						|
			},
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	switch(action) {
 | 
						|
		case "create": {
 | 
						|
			$.post(`https://${resName}/createFoundry`, JSON.stringify(foundryData), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					foundryModal.modal("hide");
 | 
						|
					loadFoundries();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case "edit": {
 | 
						|
			$.post(`https://${resName}/updateFoundry`, JSON.stringify({foundryId: foundryModal.data("foundryId"), foundryData: foundryData}), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					foundryModal.modal("hide");
 | 
						|
					loadFoundries();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
})
 | 
						|
 | 
						|
$("#delete-foundry-btn").click(function() {
 | 
						|
	let foundryModal = $("#foundry-modal");
 | 
						|
	let foundryId = foundryModal.data("foundryId");
 | 
						|
 | 
						|
	$.post(`https://${resName}/deleteFoundry`, JSON.stringify({foundryId: foundryId}), function(isSuccessful) {
 | 
						|
		if(isSuccessful) {
 | 
						|
			foundryModal.modal("hide");
 | 
						|
			loadFoundries();
 | 
						|
		}
 | 
						|
	});
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
███████  ██████  ██████  ███    ███ ██    ██ ██       █████  ███████
 | 
						|
██      ██    ██ ██   ██ ████  ████ ██    ██ ██      ██   ██ ██     
 | 
						|
█████   ██    ██ ██████  ██ ████ ██ ██    ██ ██      ███████ ███████
 | 
						|
██      ██    ██ ██   ██ ██  ██  ██ ██    ██ ██      ██   ██      ██
 | 
						|
██       ██████  ██   ██ ██      ██  ██████  ███████ ██   ██ ███████
 | 
						|
*/
 | 
						|
 | 
						|
let formulas = {};
 | 
						|
 | 
						|
async function loadFormulas() {
 | 
						|
	$.post(`https://${resName}/getAllFormulas`, {}, async function(rawFormulas) {
 | 
						|
		// Manually create the table to avoid incompatibilities due table indexing
 | 
						|
		formulas = {};
 | 
						|
 | 
						|
		for(const[k, formulaData] of Object.entries(rawFormulas)) {
 | 
						|
			formulas[formulaData.id] = formulaData;
 | 
						|
		}
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
$("#formula-produces-smoke-checkbox").change(function() {
 | 
						|
	const isEnabled = $(this).prop("checked");
 | 
						|
 | 
						|
	$("#formula-smoke-color").toggle(isEnabled);
 | 
						|
});
 | 
						|
 | 
						|
function setDefaultDataOfFormula() {
 | 
						|
	// Generic
 | 
						|
	$("#formula-label").val("Default");
 | 
						|
	$("#formula-probability-of-failure").val(0);
 | 
						|
	$("#formula-produces-smoke-checkbox").prop("checked", false).change();
 | 
						|
	$("#formula-smoke-color").val("#FF0000");
 | 
						|
 | 
						|
	// Required items
 | 
						|
	$("#formula-required-items-list").empty();
 | 
						|
 | 
						|
	// Reward items
 | 
						|
	$("#formula-reward-items-list").empty();
 | 
						|
 | 
						|
	$("#formula-reward-min-objects-amount").val(1);
 | 
						|
	$("#formula-reward-max-objects-amount").val(1);
 | 
						|
 | 
						|
	// Other
 | 
						|
	let formulaModal = $("#formula-modal");
 | 
						|
	formulaModal.data("animations", [defaultFormulaAnimData]);
 | 
						|
}
 | 
						|
 | 
						|
$("#formula-animations-btn").click(async function() {
 | 
						|
	let formulaModal = $("#formula-modal");
 | 
						|
	
 | 
						|
	const oldAnimations = formulaModal.data("animations");
 | 
						|
	const newAnimations = await animationsDialog(oldAnimations);
 | 
						|
 | 
						|
	formulaModal.data("animations", newAnimations);
 | 
						|
});
 | 
						|
 | 
						|
async function addRequiredItemToFormula(requiredItem) {
 | 
						|
	let itemDiv = $(`
 | 
						|
		<div class="row g-2 row-cols-auto align-items-center text-body my-2 required-item justify-content-center">
 | 
						|
			<button type="button" class="btn-close delete-required-item-btn me-3" ></button>	
 | 
						|
 | 
						|
			<select class="form-select required-item-type" style="width: auto;">
 | 
						|
				<option selected value="item">${getLocalizedText("menu:item")}</option>
 | 
						|
				<option value="account">${getLocalizedText("menu:account")}</option>
 | 
						|
				${await getFramework() == "ESX" ? `<option value="weapon">${getLocalizedText("menu:weapon")}</option>` : ""}
 | 
						|
			</select>
 | 
						|
			
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="text" class="form-control required-item-name" placeholder="Name" required>
 | 
						|
				<label>${ getLocalizedText("menu:object_name") }</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button type="button" class="btn btn-secondary col-auto choose-item-btn" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose") }"><i class="bi bi-list-ul"></i></button>	
 | 
						|
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="number" min=0 class="form-control required-item-quantity" placeholder="${getLocalizedText("menu:quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:quantity")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div class="form-check my-auto fs-4 ms-1">
 | 
						|
				<input class="form-check-input required-item-lose-on-use-checkbox" type="checkbox" value="">
 | 
						|
				<label class="form-check-label">${getLocalizedText("menu:lose_on_use")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
	
 | 
						|
	itemDiv.find(".delete-required-item-btn").click(function() {
 | 
						|
		itemDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	itemDiv.find(".choose-item-btn").click(async function() {
 | 
						|
		let objectType = itemDiv.find(".required-item-type").val();
 | 
						|
 | 
						|
		let objectName = await objectDialog(objectType);
 | 
						|
 | 
						|
		itemDiv.find(".required-item-name").val(objectName);
 | 
						|
	}).tooltip();
 | 
						|
 | 
						|
	
 | 
						|
	if(requiredItem) {
 | 
						|
		itemDiv.find(".required-item-type").val(requiredItem.type);
 | 
						|
		itemDiv.find(".required-item-name").val(requiredItem.name);
 | 
						|
		itemDiv.find(".required-item-quantity").val(requiredItem.quantity);
 | 
						|
		itemDiv.find(".required-item-lose-on-use-checkbox").prop("checked", requiredItem.loseOnUse);
 | 
						|
	}
 | 
						|
 | 
						|
	$("#formula-required-items-list").append(itemDiv);
 | 
						|
}
 | 
						|
$("#formula-add-required-item-btn").click(function() {
 | 
						|
	addRequiredItemToFormula();
 | 
						|
});
 | 
						|
 | 
						|
async function addRewardItemToFormula(rewardItem) {
 | 
						|
	let itemDiv = $(`
 | 
						|
		<div class="row g-2 row-cols-auto align-items-center text-body my-2 reward-item justify-content-center">
 | 
						|
			<button type="button" class="btn-close delete-reward-item-btn me-3" ></button>	
 | 
						|
 | 
						|
			<select class="form-select reward-item-type" style="width: auto;">
 | 
						|
				<option selected value="item">${getLocalizedText("menu:item")}</option>
 | 
						|
				<option value="account">${getLocalizedText("menu:account")}</option>
 | 
						|
				${await getFramework() == "ESX" ? `<option value="weapon">${getLocalizedText("menu:weapon")}</option>` : ""}
 | 
						|
			</select>
 | 
						|
			
 | 
						|
			<div class="form-floating">
 | 
						|
				<input type="text" class="form-control reward-item-name" placeholder="Name" required>
 | 
						|
				<label>${ getLocalizedText("menu:object_name") }</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<button type="button" class="btn btn-secondary col-auto choose-item-btn" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose") }"><i class="bi bi-list-ul"></i></button>	
 | 
						|
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number" min=0 class="form-control reward-item-min-quantity" placeholder="${getLocalizedText("menu:min_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:min_quantity")}</label>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number" min=0 class="form-control reward-item-max-quantity" placeholder="${getLocalizedText("menu:max_quantity")}" required>
 | 
						|
				<label>${getLocalizedText("menu:max_quantity")}</label>
 | 
						|
			</div>
 | 
						|
			
 | 
						|
			<div class="form-floating col-2">
 | 
						|
				<input type="number"  class="form-control reward-item-chances" placeholder="${getLocalizedText("menu:probability")}" required>
 | 
						|
				<label>${getLocalizedText("menu:probability")}</label>
 | 
						|
			</div>
 | 
						|
		</div>
 | 
						|
	`);
 | 
						|
	
 | 
						|
	itemDiv.find(".delete-reward-item-btn").click(function() {
 | 
						|
		itemDiv.remove();
 | 
						|
	});
 | 
						|
 | 
						|
	itemDiv.find(".choose-item-btn").click(async function() {
 | 
						|
		let objectType = itemDiv.find(".reward-item-type").val();
 | 
						|
 | 
						|
		let objectName = await objectDialog(objectType);
 | 
						|
 | 
						|
		itemDiv.find(".reward-item-name").val(objectName);
 | 
						|
	}).tooltip();
 | 
						|
 | 
						|
	
 | 
						|
	if(rewardItem) {
 | 
						|
		itemDiv.find(".reward-item-type").val(rewardItem.type);
 | 
						|
		itemDiv.find(".reward-item-name").val(rewardItem.name);
 | 
						|
		itemDiv.find(".reward-item-min-quantity").val(rewardItem.minQuantity);
 | 
						|
		itemDiv.find(".reward-item-max-quantity").val(rewardItem.maxQuantity);
 | 
						|
		itemDiv.find(".reward-item-chances").val(rewardItem.chances);
 | 
						|
	}
 | 
						|
 | 
						|
	$("#formula-reward-items-list").append(itemDiv);
 | 
						|
}
 | 
						|
$("#formula-add-reward-item-btn").click(function() {
 | 
						|
	addRewardItemToFormula();
 | 
						|
});
 | 
						|
 | 
						|
function getRewardItemsFromformula() {
 | 
						|
	let rewardItems = [];
 | 
						|
 | 
						|
	$("#formula-reward-items-list").find(".reward-item").each(function() {
 | 
						|
		let rewardItem = {
 | 
						|
			type: $(this).find(".reward-item-type").val(),
 | 
						|
			name: $(this).find(".reward-item-name").val(),
 | 
						|
			minQuantity: parseInt( $(this).find(".reward-item-min-quantity").val() ),
 | 
						|
			maxQuantity: parseInt( $(this).find(".reward-item-max-quantity").val() ),
 | 
						|
			chances: parseInt( $(this).find(".reward-item-chances").val() )
 | 
						|
		}
 | 
						|
 | 
						|
		rewardItems.push(rewardItem);
 | 
						|
	});
 | 
						|
 | 
						|
	return rewardItems;
 | 
						|
}
 | 
						|
 | 
						|
function getRequiredItemsFromformula() {
 | 
						|
	let requiredItems = [];
 | 
						|
 | 
						|
	$("#formula-required-items-list").find(".required-item").each(function() {
 | 
						|
		let requiredItem = {
 | 
						|
			type: $(this).find(".required-item-type").val(),
 | 
						|
			name: $(this).find(".required-item-name").val(),
 | 
						|
			quantity: parseInt( $(this).find(".required-item-quantity").val() ),
 | 
						|
			loseOnUse: $(this).find(".required-item-lose-on-use-checkbox").prop("checked")
 | 
						|
		}
 | 
						|
 | 
						|
		requiredItems.push(requiredItem);
 | 
						|
	});
 | 
						|
 | 
						|
	return requiredItems;
 | 
						|
}
 | 
						|
 | 
						|
$("#new-formula-btn").click(function() {
 | 
						|
	let formulaModal = $("#formula-modal");
 | 
						|
 | 
						|
	// Converts from edit modal to create modal
 | 
						|
	formulaModal.data("action", "create");
 | 
						|
	
 | 
						|
	$("#delete-formula-btn").hide();
 | 
						|
	$("#save-formula-btn").text( getLocalizedText("menu:create") );
 | 
						|
	
 | 
						|
	setDefaultDataOfFormula();
 | 
						|
 | 
						|
	formulaModal.modal("show");
 | 
						|
});
 | 
						|
 | 
						|
function editFormula(id) {
 | 
						|
	let formulaModal = $("#formula-modal");
 | 
						|
 | 
						|
	// Converts from create modal to edit modal
 | 
						|
	formulaModal.data("action", "edit");
 | 
						|
	formulaModal.data("formulaId", id);
 | 
						|
 | 
						|
	$("#delete-formula-btn").show();
 | 
						|
	$("#save-formula-btn").text( getLocalizedText("menu:save") );
 | 
						|
 | 
						|
	let formulaData = formulas[id];
 | 
						|
 | 
						|
	// Generic
 | 
						|
	$("#formula-label").val(formulaData.label);
 | 
						|
	$("#formula-probability-of-failure").val(formulaData.data.probabilityOfFailure);
 | 
						|
	$("#formula-produces-smoke-checkbox").prop("checked", formulaData.data.producesSmoke);
 | 
						|
	$("#formula-smoke-color").val( rgbToHex(formulaData.data.smokeColor.r, formulaData.data.smokeColor.g, formulaData.data.smokeColor.b) );
 | 
						|
	$("#formula-reward-min-objects-amount").val(formulaData.data.minObjectsAmount);
 | 
						|
	$("#formula-reward-max-objects-amount").val(formulaData.data.maxObjectsAmount);
 | 
						|
 | 
						|
	// Required items
 | 
						|
	$("#formula-required-items-list").empty();
 | 
						|
	if(formulaData.data.requiredItems) {
 | 
						|
		for(let requiredItem of formulaData.data.requiredItems) {
 | 
						|
			addRequiredItemToFormula(requiredItem);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Reward items
 | 
						|
	$("#formula-reward-items-list").empty();
 | 
						|
	if(formulaData.data.rewardItems) {
 | 
						|
		for(let rewardItem of formulaData.data.rewardItems) {
 | 
						|
			addRewardItemToFormula(rewardItem);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Anims
 | 
						|
	formulaModal.data("animations", formulaData.data.animations);
 | 
						|
 | 
						|
	formulaModal.modal("show");
 | 
						|
}
 | 
						|
 | 
						|
$("#formula-form").submit(function(event) {
 | 
						|
	if(isThereAnyErrorInForm(event)) return;
 | 
						|
 | 
						|
	let formulaModal = $("#formula-modal");
 | 
						|
	let action = formulaModal.data("action");
 | 
						|
 | 
						|
	let formulaData = {
 | 
						|
		label: $("#formula-label").val(),
 | 
						|
		data: {
 | 
						|
			animations: formulaModal.data("animations"),
 | 
						|
			probabilityOfFailure: parseInt( $("#formula-probability-of-failure").val() ),
 | 
						|
			producesSmoke: $("#formula-produces-smoke-checkbox").prop("checked"),
 | 
						|
			smokeColor: hexToRgb( $("#formula-smoke-color").val() ),
 | 
						|
			requiredItems: getRequiredItemsFromformula(),
 | 
						|
			rewardItems: getRewardItemsFromformula(),
 | 
						|
			minObjectsAmount: parseInt( $("#formula-reward-min-objects-amount").val() ),
 | 
						|
			maxObjectsAmount: parseInt( $("#formula-reward-max-objects-amount").val() ),
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	switch(action) {
 | 
						|
		case "create": {
 | 
						|
			$.post(`https://${resName}/createFormula`, JSON.stringify(formulaData), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					formulaModal.modal("hide");
 | 
						|
					loadFormulas();
 | 
						|
					chooseAllowedFormulas();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case "edit": {
 | 
						|
			$.post(`https://${resName}/updateFormula`, JSON.stringify({formulaId: formulaModal.data("formulaId"), formulaData: formulaData}), function(isSuccessful) {
 | 
						|
				if(isSuccessful) {
 | 
						|
					formulaModal.modal("hide");
 | 
						|
					loadFormulas();
 | 
						|
					chooseAllowedFormulas();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
})
 | 
						|
 | 
						|
$("#delete-formula-btn").click(function() {
 | 
						|
	let formulaModal = $("#formula-modal");
 | 
						|
	let formulaId = formulaModal.data("formulaId");
 | 
						|
 | 
						|
	$.post(`https://${resName}/deleteFormula`, JSON.stringify({formulaId: formulaId}), function(isSuccessful) {
 | 
						|
		if(isSuccessful) {
 | 
						|
			formulaModal.modal("hide");
 | 
						|
			loadFormulas();
 | 
						|
			chooseAllowedFormulas();
 | 
						|
		}
 | 
						|
	});
 | 
						|
});
 |