forked from Simnation/Main
3569 lines
No EOL
123 KiB
JavaScript
3569 lines
No EOL
123 KiB
JavaScript
const resName = GetParentResourceName();
|
|
|
|
// Utils
|
|
|
|
function maxTwoDecimals() {
|
|
if(isNaN(this.value)) {
|
|
return
|
|
}
|
|
|
|
let number = parseFloat(this.value);
|
|
|
|
if(number) {
|
|
this.value = number.toFixed(2);
|
|
}
|
|
}
|
|
|
|
$(".max-two-decimals").change(maxTwoDecimals)
|
|
|
|
function componentToHex(c) {
|
|
var hex = c.toString(16);
|
|
|
|
return hex.length == 1 ? "0" + hex : hex;
|
|
}
|
|
|
|
function rgbToHex(r, g, b) {
|
|
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
|
|
}
|
|
|
|
function hexToRgb(hex) {
|
|
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
|
|
return result ? {
|
|
r: parseInt(result[1], 16),
|
|
g: parseInt(result[2], 16),
|
|
b: parseInt(result[3], 16)
|
|
} : null;
|
|
}
|
|
|
|
// Harvestable items
|
|
let harvestableItemsDatatable = $("#harvestable-items-container").DataTable( {
|
|
"lengthMenu": [10, 15, 20],
|
|
"createdRow": function ( row, data, index ) {
|
|
$(row).addClass("clickable");
|
|
|
|
$(row).click(function() {
|
|
let harvestableItemId = parseInt(data[0]);
|
|
|
|
editHarvestableItem(harvestableItemId);
|
|
})
|
|
},
|
|
} );
|
|
|
|
let harvestableItems = {};
|
|
|
|
function loadHarvestableItems() {
|
|
$.post(`https://${resName}/getAllHarvestableItems`, {}, async function(rawHarvestableItems) {
|
|
// Manually create the table to avoid incompatibilities due table indexing
|
|
|
|
harvestableItems = {};
|
|
|
|
for(const[k, itemData] of Object.entries(rawHarvestableItems)) {
|
|
harvestableItems[itemData.id] = itemData;
|
|
}
|
|
|
|
harvestableItemsDatatable.clear();
|
|
|
|
for(const[itemId, itemData] of Object.entries(harvestableItems)) {
|
|
harvestableItemsDatatable.row.add([
|
|
itemId,
|
|
await getItemLabel(itemData.name),
|
|
itemData.minQuantity,
|
|
itemData.maxQuantity,
|
|
itemData.coords.x,
|
|
itemData.coords.y,
|
|
itemData.coords.z,
|
|
])
|
|
}
|
|
|
|
harvestableItemsDatatable.draw()
|
|
})
|
|
}
|
|
|
|
// Harvestable items creation
|
|
$("#new-harvestable-item-btn").click(function() {
|
|
let harvestableModal = $("#harvestable-item-modal");
|
|
|
|
harvestableModal.data("action", "create");
|
|
|
|
harvestableModal.find("input").val("");
|
|
|
|
$("#harvestable-scale-x").val(1.5);
|
|
$("#harvestable-scale-y").val(1.5);
|
|
$("#harvestable-scale-z").val(1.5);
|
|
|
|
$("#harvestable-icon-type").val(1);
|
|
$("#harvestable-min-quantity").val(1);
|
|
$("#harvestable-max-quantity").val(1);
|
|
|
|
$("#harvestable-harvest-time").val(5);
|
|
|
|
// Converts from edit modal to create modal
|
|
$("#delete-harvestable-item-btn").hide();
|
|
$("#save-harvestable-item-btn").text( getLocalizedText("menu:create") );
|
|
|
|
$("#harvestable-item-map-blip").change();
|
|
|
|
harvestableModal.modal("show");
|
|
})
|
|
|
|
$("#harvestable-choose-item-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
$("#harvestable-item-name").val(itemName);
|
|
})
|
|
|
|
$("#harvestable-current-coords-btn").click(async function(event) {
|
|
if(event.shiftKey) {
|
|
const coordsInUI = {
|
|
x: parseFloat( $("#harvestable-coords-x").val() ),
|
|
y: parseFloat( $("#harvestable-coords-y").val() ),
|
|
z: parseFloat( $("#harvestable-coords-z").val() )
|
|
}
|
|
|
|
teleportToCoords(coordsInUI);
|
|
return;
|
|
}
|
|
|
|
let coords = await getCurrentCoords()
|
|
|
|
$("#harvestable-coords-x").val(coords.x);
|
|
$("#harvestable-coords-y").val(coords.y);
|
|
$("#harvestable-coords-z").val(coords.z);
|
|
})
|
|
|
|
$("#harvestable-item-map-blip").change(function() {
|
|
let enabled = $(this).prop("checked");
|
|
|
|
$("#harvestable-item-map-blip-inputs").find("input").prop("disabled", !enabled);
|
|
})
|
|
|
|
$("#harvestable-form").submit(async function(event) {
|
|
if(isThereAnyErrorInForm(event)) return;
|
|
|
|
let harvestableItem = {
|
|
name: $("#harvestable-item-name").val(),
|
|
|
|
iconType: parseInt( $("#harvestable-icon-type").val() ),
|
|
|
|
coords: {
|
|
x: parseFloat( $("#harvestable-coords-x").val() ),
|
|
y: parseFloat( $("#harvestable-coords-y").val() ),
|
|
z: parseFloat( $("#harvestable-coords-z").val() ),
|
|
},
|
|
|
|
scale: {
|
|
x: parseFloat( $("#harvestable-scale-x").val() ),
|
|
y: parseFloat( $("#harvestable-scale-y").val() ),
|
|
z: parseFloat( $("#harvestable-scale-z").val() ),
|
|
},
|
|
|
|
bounce: $("#harvestable-bounce").prop("checked"),
|
|
followCamera: $("#harvestable-follow-camera").prop("checked"),
|
|
rotate: $("#harvestable-rotate").prop("checked"),
|
|
|
|
color: hexToRgb( $("#harvestable-color").val() ),
|
|
opacity: parseInt( $("#harvestable-opacity").val() ),
|
|
|
|
timeToHarvest: parseInt( $("#harvestable-harvest-time").val() ),
|
|
|
|
minQuantity: parseInt( $("#harvestable-min-quantity").val() ),
|
|
maxQuantity: parseInt( $("#harvestable-max-quantity").val() )
|
|
}
|
|
|
|
let isBlipEnabled = $("#harvestable-item-map-blip").prop("checked");
|
|
if( isBlipEnabled ) {
|
|
harvestableItem.blipName = $("#harvestable-item-blip-name").val();
|
|
harvestableItem.blipSprite = parseInt( $("#harvestable-item-sprite-id").val() );
|
|
harvestableItem.blipColor = parseInt( $("#harvestable-item-blip-color").val() );
|
|
harvestableItem.blipScale = parseFloat( $("#harvestable-item-blip-scale").val() );
|
|
}
|
|
|
|
let harvestableItemModal = $("#harvestable-item-modal");
|
|
let action = $(harvestableItemModal).data("action");
|
|
|
|
let response = null;
|
|
|
|
switch(action) {
|
|
case "create": {
|
|
response = await $.post(`https://${resName}/createNewHarvestableItem`, JSON.stringify(harvestableItem));
|
|
break;
|
|
}
|
|
|
|
case "edit": {
|
|
let itemId = harvestableItemModal.data("itemId");
|
|
|
|
response = await $.post(`https://${resName}/updateHarvestableItem`, JSON.stringify({
|
|
itemId: itemId,
|
|
itemData: harvestableItem
|
|
}));
|
|
break;
|
|
}
|
|
}
|
|
|
|
harvestableItemModal.modal("hide");
|
|
loadHarvestableItems();
|
|
|
|
showServerResponse(response);
|
|
})
|
|
|
|
// Harvestable items edit
|
|
function editHarvestableItem(harvestableItemId) {
|
|
let itemData = harvestableItems[harvestableItemId];
|
|
|
|
$("#harvestable-item-name").val(itemData.name);
|
|
$("#harvestable-icon-type").val(itemData.iconType);
|
|
|
|
$("#harvestable-coords-x").val(itemData.coords.x);
|
|
$("#harvestable-coords-y").val(itemData.coords.y);
|
|
$("#harvestable-coords-z").val(itemData.coords.z);
|
|
|
|
$("#harvestable-scale-x").val(itemData.scale.x);
|
|
$("#harvestable-scale-y").val(itemData.scale.y);
|
|
$("#harvestable-scale-z").val(itemData.scale.z);
|
|
|
|
$("#harvestable-bounce").prop("checked", itemData.bounce);
|
|
$("#harvestable-follow-camera").prop("checked", itemData.followCamera);
|
|
$("#harvestable-rotate").prop("checked", itemData.rotate);
|
|
|
|
$("#harvestable-color").val( rgbToHex(itemData.color.r, itemData.color.g, itemData.color.b) );
|
|
$("#harvestable-opacity").val(itemData.opacity);
|
|
|
|
$("#harvestable-harvest-time").val(itemData.timeToHarvest);
|
|
|
|
$("#harvestable-min-quantity").val(itemData.minQuantity);
|
|
$("#harvestable-max-quantity").val(itemData.maxQuantity);
|
|
|
|
let mapBlipCheckbox = $("#harvestable-item-map-blip");
|
|
if(itemData.blipSprite) {
|
|
$("#harvestable-item-blip-name").val(itemData.blipName);
|
|
$("#harvestable-item-sprite-id").val(itemData.blipSprite);
|
|
$("#harvestable-item-blip-color").val(itemData.blipColor);
|
|
$("#harvestable-item-blip-scale").val(itemData.blipScale);
|
|
|
|
mapBlipCheckbox.prop("checked", true);
|
|
} else {
|
|
mapBlipCheckbox.prop("checked", false);
|
|
}
|
|
mapBlipCheckbox.change();
|
|
|
|
let harvestableModal = $("#harvestable-item-modal");
|
|
|
|
harvestableModal.data("itemId", harvestableItemId);
|
|
harvestableModal.data("action", "edit");
|
|
|
|
$("#delete-harvestable-item-btn").show();
|
|
$("#save-harvestable-item-btn").text( getLocalizedText("menu:save") );
|
|
|
|
harvestableModal.modal("show");
|
|
}
|
|
|
|
// Harvestable item delete
|
|
$("#delete-harvestable-item-btn").click(async function() {
|
|
if(!await confirmDeletion()) return;
|
|
|
|
let itemId = $("#harvestable-item-modal").data("itemId");
|
|
|
|
const response = await $.post(`https://${resName}/deleteHarvestableItem`, JSON.stringify({
|
|
itemId: itemId
|
|
}));
|
|
|
|
$("#harvestable-item-modal").modal("hide");
|
|
loadHarvestableItems();
|
|
showServerResponse(response);
|
|
})
|
|
|
|
// Drugs Fields
|
|
let drugsFieldsDatatable = $("#fields-container").DataTable({
|
|
"lengthMenu": [10, 15, 20],
|
|
"createdRow": function ( row, data, index ) {
|
|
$(row).addClass("clickable");
|
|
|
|
$(row).click(function() {
|
|
let fieldId = parseInt(data[0]);
|
|
|
|
editDrugField(fieldId);
|
|
})
|
|
},
|
|
} );
|
|
|
|
let drugsFields = {};
|
|
|
|
function loadDrugsFields() {
|
|
$.post(`https://${resName}/getAllDrugsFields`, {}, function(rawDrugsFields) {
|
|
// Manually create the table to avoid incompatibilities due table indexing
|
|
|
|
drugsFields = {};
|
|
|
|
for(const[k, fieldData] of Object.entries(rawDrugsFields)) {
|
|
drugsFields[fieldData.id] = fieldData;
|
|
}
|
|
|
|
drugsFieldsDatatable.clear();
|
|
|
|
for(const[fieldId, fieldData] of Object.entries(drugsFields)) {
|
|
drugsFieldsDatatable.row.add([
|
|
fieldId,
|
|
fieldData.label,
|
|
fieldData.radius,
|
|
fieldData.coords.x,
|
|
fieldData.coords.y,
|
|
fieldData.coords.z,
|
|
])
|
|
}
|
|
|
|
drugsFieldsDatatable.draw()
|
|
})
|
|
}
|
|
|
|
$("#new-field-btn").click(function() {
|
|
let fieldModal = $("#field-modal");
|
|
|
|
fieldModal.data("action", "create");
|
|
|
|
fieldModal.find("input").val("");
|
|
$("#field-map-blip").prop("checked", false).change();
|
|
|
|
// Converts from edit modal to create modal
|
|
$("#delete-field-btn").hide();
|
|
$("#save-field-btn").text( getLocalizedText("menu:create") );
|
|
|
|
$("#fields-items").empty();
|
|
|
|
addItemToField();
|
|
|
|
fieldModal.modal("show");
|
|
})
|
|
|
|
$("#field-current-coords-btn").click(async function(event) {
|
|
if(event.shiftKey) {
|
|
const coordsInUI = {
|
|
x: parseFloat( $("#field-coords-x").val() ),
|
|
y: parseFloat( $("#field-coords-y").val() ),
|
|
z: parseFloat( $("#field-coords-z").val() )
|
|
}
|
|
|
|
teleportToCoords(coordsInUI);
|
|
return;
|
|
}
|
|
|
|
let coords = await getCurrentCoords();
|
|
|
|
$("#field-coords-x").val(coords.x);
|
|
$("#field-coords-y").val(coords.y);
|
|
$("#field-coords-z").val(coords.z);
|
|
});
|
|
|
|
$("#field-required-item-choose-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
$("#field-required-item-name").val(itemName);
|
|
})
|
|
|
|
$("#field-required-item-toggle").change(function() {
|
|
let enabled = $(this).prop("checked");
|
|
|
|
|
|
$("#field-required-item-choose-btn").prop("disabled", !enabled);
|
|
$("#field-required-item-lose-on-use").prop("disabled", !enabled);
|
|
$("#field-required-item-name").prop("disabled", !enabled).prop("required", enabled);
|
|
})
|
|
|
|
async function addItemToField(itemData = {}) {
|
|
let itemDiv = $(`
|
|
<div class="item">
|
|
<div class="row g-2 row-cols-auto my-1 align-items-center justify-content-center">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control field-item-label" placeholder="Label" value="${itemData.name ? await getItemLabel(itemData.name) : ""}" disabled>
|
|
<label>${ getLocalizedText("menu:label") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control field-item-name" placeholder="Item name" value="${itemData.name || ""}" required>
|
|
<label>${ getLocalizedText("menu:item_name") }</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-secondary col-auto choose-item-btn me-3" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose_item") }"><i class="bi bi-list-ul"></i></button>
|
|
|
|
<div class="form-floating">
|
|
<input type="number" class="form-control" id="field-item-chances" placeholder="Chances" value="${itemData.chances}" required>
|
|
<label>${ getLocalizedText("menu:chances") }</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn-close btn-close remove-btn ms-3"></button>
|
|
</div>
|
|
|
|
<div class="row g-2 row-cols-2 my-3">
|
|
<div class="form-floating">
|
|
<input type="number" class="form-control field-item-min-quantity" placeholder="Min quantity" value="${itemData.minQuantity}" required>
|
|
<label>${ getLocalizedText("menu:minimum_quantity") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating">
|
|
<input type="number" class="form-control field-item-max-quantity" placeholder="Max quantity" value="${itemData.maxQuantity}" required>
|
|
<label>${ getLocalizedText("menu:maximum_quantity") }</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
`);
|
|
|
|
itemDiv.find(".choose-item-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
itemDiv.find(".field-item-name").val(itemName);
|
|
|
|
let itemLabel = await getItemLabel(itemName);
|
|
|
|
itemDiv.find(".field-item-label").val(itemLabel);
|
|
}).tooltip();
|
|
|
|
itemDiv.find(".remove-btn").click(function() {
|
|
itemDiv.remove();
|
|
})
|
|
|
|
$("#fields-items").append(itemDiv);
|
|
}
|
|
$("#field-add-item").click(addItemToField);
|
|
|
|
function getItemFieldDataFromDiv(itemDiv) {
|
|
let itemData = {
|
|
name: itemDiv.find(".field-item-name").val(),
|
|
chances: parseInt( itemDiv.find("#field-item-chances").val() ),
|
|
minQuantity: parseInt( itemDiv.find(".field-item-min-quantity").val() ),
|
|
maxQuantity: parseInt( itemDiv.find(".field-item-max-quantity").val() )
|
|
}
|
|
|
|
return itemData;
|
|
}
|
|
|
|
function getAllItemsFieldData() {
|
|
let items = [];
|
|
|
|
$("#fields-items").children(".item").each(function() {
|
|
items.push(getItemFieldDataFromDiv( $(this) ))
|
|
})
|
|
|
|
return items;
|
|
}
|
|
|
|
function editDrugField(fieldId) {
|
|
let fieldModal = $("#field-modal");
|
|
|
|
fieldModal.data("action", "edit");
|
|
fieldModal.data("fieldId", fieldId);
|
|
|
|
let fieldData = drugsFields[fieldId];
|
|
|
|
$("#field-label").val(fieldData.label);
|
|
$("#field-radius").val(fieldData.radius);
|
|
$("#field-coords-x").val(fieldData.coords.x);
|
|
$("#field-coords-y").val(fieldData.coords.y);
|
|
$("#field-coords-z").val(fieldData.coords.z);
|
|
|
|
$("#field-object-model").val(fieldData.objectModel);
|
|
$("#field-max-objects").val(fieldData.maxObjects);
|
|
$("#field-seconds-to-harvest").val(fieldData.time);
|
|
|
|
$("#delete-field-btn").show();
|
|
$("#save-field-btn").text( getLocalizedText("menu:save") );
|
|
|
|
let mapBlipCheckbox = $("#field-map-blip");
|
|
if(fieldData.blipSprite) {
|
|
$("#field-blip-name").val(fieldData.blipName);
|
|
$("#field-sprite-id").val(fieldData.blipSprite);
|
|
$("#field-blip-color").val(fieldData.blipColor);
|
|
$("#field-blip-scale").val(fieldData.blipScale);
|
|
|
|
mapBlipCheckbox.prop("checked", true);
|
|
} else {
|
|
mapBlipCheckbox.prop("checked", false);
|
|
}
|
|
mapBlipCheckbox.change();
|
|
|
|
$("#field-required-item-toggle").prop("checked", fieldData.requiredItem ? true : false).change();
|
|
$("#field-required-item-name").val(fieldData.requiredItem?.name || "");
|
|
$("#field-required-item-lose-on-use").prop("checked", fieldData.requiredItem?.loseOnUse ?? false);
|
|
|
|
$("#fields-items").empty();
|
|
|
|
if(fieldData.items) {
|
|
for(const itemData of fieldData.items) {
|
|
addItemToField(itemData);
|
|
}
|
|
}
|
|
|
|
fieldModal.modal("show");
|
|
}
|
|
|
|
$("#field-map-blip").change(function() {
|
|
let enabled = $(this).prop("checked");
|
|
|
|
$("#field-map-blip-inputs").find("input").prop("disabled", !enabled);
|
|
})
|
|
|
|
$("#field-form").submit(async function(event) {
|
|
if(isThereAnyErrorInForm(event)) return;
|
|
|
|
let fieldData = {
|
|
label: $("#field-label").val(),
|
|
radius: parseInt( $("#field-radius").val() ),
|
|
coords: {
|
|
x: parseFloat( $("#field-coords-x").val() ),
|
|
y: parseFloat( $("#field-coords-y").val() ),
|
|
z: parseFloat( $("#field-coords-z").val() )
|
|
},
|
|
items: getAllItemsFieldData(),
|
|
objectModel: $("#field-object-model").val(),
|
|
maxObjects: parseInt( $("#field-max-objects").val() ),
|
|
time: parseInt( $("#field-seconds-to-harvest").val() ),
|
|
requiredItem: $("#field-required-item-toggle").prop("checked") ? {
|
|
name: $("#field-required-item-name").val(),
|
|
loseOnUse: $("#field-required-item-lose-on-use").prop("checked")
|
|
} : undefined
|
|
}
|
|
|
|
let isBlipEnabled = $("#field-map-blip").prop("checked");
|
|
if( isBlipEnabled ) {
|
|
fieldData.blipName = $("#field-blip-name").val();
|
|
fieldData.blipSprite = parseInt( $("#field-sprite-id").val() );
|
|
fieldData.blipColor = parseInt( $("#field-blip-color").val() );
|
|
fieldData.blipScale = parseFloat( $("#field-blip-scale").val() );
|
|
}
|
|
|
|
let fieldModal = $("#field-modal");
|
|
let action = fieldModal.data("action");
|
|
|
|
let response = null;
|
|
|
|
switch(action) {
|
|
case "create": {
|
|
response = await $.post(`https://${resName}/createNewField`, JSON.stringify(fieldData))
|
|
break;
|
|
}
|
|
|
|
case "edit": {
|
|
const fieldId = fieldModal.data("fieldId");
|
|
|
|
response = await $.post(`https://${resName}/updateField`, JSON.stringify({fieldId: fieldId, fieldData: fieldData}));
|
|
break;
|
|
}
|
|
}
|
|
|
|
showServerResponse(response);
|
|
fieldModal.modal("hide");
|
|
loadDrugsFields()
|
|
})
|
|
|
|
$("#delete-field-btn").click(async function() {
|
|
if(!await confirmDeletion()) return;
|
|
|
|
let fieldId = $("#field-modal").data("fieldId");
|
|
|
|
const response = await $.post(`https://${resName}/deleteField`, JSON.stringify({fieldId: fieldId}))
|
|
showServerResponse(response);
|
|
|
|
$("#field-modal").modal("hide");
|
|
loadDrugsFields();
|
|
})
|
|
|
|
// Craftings creation
|
|
let craftingRecipesDatatable = $("#crafting-recipes-container").DataTable( {
|
|
"lengthMenu": [10, 15, 20],
|
|
"createdRow": function ( row, data, index ) {
|
|
$(row).addClass("clickable");
|
|
|
|
$(row).click(function() {
|
|
let craftingId = parseInt(data[0]);
|
|
|
|
editCraftingRecipe(craftingId);
|
|
})
|
|
},
|
|
} );
|
|
|
|
let craftingRecipes = {};
|
|
function loadCraftingRecipes() {
|
|
$.post(`https://${resName}/getAllCraftingRecipes`, {}, function(rawCraftingRecipes) {
|
|
// Manually create the table to avoid incompatibilities due table indexing
|
|
craftingRecipes = {};
|
|
|
|
for(const[k, craftingRecipe] of Object.entries(rawCraftingRecipes)) {
|
|
craftingRecipes[craftingRecipe.id] = craftingRecipe;
|
|
}
|
|
|
|
craftingRecipesDatatable.clear();
|
|
|
|
for(const[craftingId, craftingRecipe] of Object.entries(craftingRecipes)) {
|
|
craftingRecipesDatatable.row.add([
|
|
craftingId,
|
|
craftingRecipe.name,
|
|
craftingRecipe.resultItems.map(resultItem => `${resultItem.itemName}`).join(", "),
|
|
craftingRecipe.time
|
|
])
|
|
}
|
|
|
|
craftingRecipesDatatable.draw()
|
|
})
|
|
}
|
|
|
|
$("#new-crafting-btn").click(function() {
|
|
let craftingRecipeModal = $("#crafting-recipe-modal");
|
|
|
|
craftingRecipeModal.data("action", "create");
|
|
craftingRecipeModal.modal("show");
|
|
|
|
$("#crafting-recipe-result-items-container").empty();
|
|
$("#crafting-recipe-ingredients-container").empty();
|
|
$("#crafting-recipe-perfect-recipe-reward-container").empty();
|
|
|
|
// Adapts the modal to creation instead of edit
|
|
$("#crafting-recipe-modal-confirm-btn").text( getLocalizedText("menu:create") );
|
|
$("#delete-crafting-recipe-btn").hide();
|
|
|
|
$("#crafting-recipe-modal").find("input").val("");
|
|
|
|
// Adds one empty result item and ingredient
|
|
addResultItemInCraftingRecipe();
|
|
addIngredientInCraftingRecipe();
|
|
addRewardItemInCraftingRecipe();
|
|
});
|
|
|
|
// Adds a result item in the crafting recipe
|
|
function addResultItemInCraftingRecipe(itemName = "", itemQuantity = 1) {
|
|
let craftingRecipeContainer = $("#crafting-recipe-result-items-container");
|
|
|
|
let resultItemDiv = $(`
|
|
<div class="row g-2 mb-2 align-items-center result-item">
|
|
<div class="col-md">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control result-item-name" placeholder="Item name" value="${itemName}" required>
|
|
<label>${getLocalizedText("menu:item_name")}</label>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-secondary col-2 choose-item-btn">${getLocalizedText("menu:choose_item")}</button>
|
|
|
|
<div class="col-md ms-2">
|
|
<div class="form-floating">
|
|
<input type="number" class="form-control result-item-quantity" placeholder="Item quantity" value="${itemQuantity}" required>
|
|
<label>${getLocalizedText("menu:quantity")}</label>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="button" class="btn-close remove-result-item-btn" aria-label="Close"></button>
|
|
</div>
|
|
`);
|
|
|
|
resultItemDiv.find(".choose-item-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
resultItemDiv.find(".result-item-name").val(itemName);
|
|
})
|
|
|
|
resultItemDiv.find(".remove-result-item-btn").click(function() {
|
|
resultItemDiv.remove();
|
|
})
|
|
|
|
craftingRecipeContainer.append(resultItemDiv);
|
|
}
|
|
$("#crafting-recipe-add-result-item-btn").click(function() {
|
|
addResultItemInCraftingRecipe();
|
|
})
|
|
|
|
function addIngredientInCraftingRecipe(ingredientName = "", ingredientMinQuantity = 1, ingredientMaxQuantity = 3, ingredientPerfectQuantity = 2, loseOnUse = true) {
|
|
let ingredientsContainer = $("#crafting-recipe-ingredients-container");
|
|
|
|
let ingredientDiv = $(`
|
|
<div class="d-flex g-2 gap-2 align-items-center mb-3 ingredient">
|
|
<div class="form-floating col">
|
|
<input type="text" class="form-control ingredient-item-name" placeholder="Item name" value="${ingredientName}" required>
|
|
<label>${getLocalizedText("menu:item_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_item") }"><i class="bi bi-list-ul"></i></button>
|
|
|
|
<div class="form-check my-auto">
|
|
<input class="form-check-input" type="checkbox" ${loseOnUse ? "checked" : null}>
|
|
<label class="form-check-label">${ getLocalizedText("menu:lose_on_use") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating col">
|
|
<input type="number" class="form-control ingredient-min-quantity" placeholder="Item name" value="${ingredientMinQuantity}" required>
|
|
<label>${getLocalizedText("menu:minimum_quantity")}</label>
|
|
</div>
|
|
|
|
<div class="form-floating col">
|
|
<input type="number" class="form-control ingredient-max-quantity" placeholder="Item name" value="${ingredientMaxQuantity}" required>
|
|
<label>${getLocalizedText("menu:maximum_quantity")}</label>
|
|
</div>
|
|
|
|
<div class="form-floating col">
|
|
<input type="number" class="form-control ingredient-perfect-quantity" placeholder="Item name" value="${ingredientPerfectQuantity}" required>
|
|
<label>${getLocalizedText("menu:perfect_quantity")}</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn-close remove-ingredient-btn"></button>
|
|
</div>
|
|
|
|
<hr>
|
|
`);
|
|
|
|
ingredientDiv.find(".choose-item-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
ingredientDiv.find(".ingredient-item-name").val(itemName);
|
|
}).tooltip();
|
|
|
|
ingredientDiv.find(".remove-ingredient-btn").click(function() {
|
|
ingredientDiv.remove();
|
|
})
|
|
|
|
ingredientsContainer.append(ingredientDiv);
|
|
}
|
|
$("#crafting-recipe-add-ingredient-btn").click(function() {
|
|
addIngredientInCraftingRecipe();
|
|
})
|
|
|
|
// Get the data from crafting recipe modal
|
|
function getCraftingRecipeData() {
|
|
let resultItems = [];
|
|
$("#crafting-recipe-result-items-container").children(".result-item").each(function() {
|
|
let resultItem = {
|
|
itemName: $(this).find(".result-item-name").val(),
|
|
itemQuantity: parseInt( $(this).find(".result-item-quantity").val() )
|
|
}
|
|
|
|
resultItems.push(resultItem);
|
|
});
|
|
|
|
let ingredients = [];
|
|
$("#crafting-recipe-ingredients-container").children(".ingredient").each(function() {
|
|
let ingredient = {
|
|
ingredientItemName: $(this).find(".ingredient-item-name").val(),
|
|
ingredientMinQuantity: parseInt( $(this).find(".ingredient-min-quantity").val() ),
|
|
ingredientMaxQuantity: parseInt( $(this).find(".ingredient-max-quantity").val() ),
|
|
ingredientPerfectQuantity: parseInt( $(this).find(".ingredient-perfect-quantity").val() ),
|
|
loseOnUse: $(this).find(".form-check-input").is(":checked")
|
|
}
|
|
|
|
ingredients.push(ingredient);
|
|
});
|
|
|
|
let perfectRecipeReward = [];
|
|
$("#crafting-recipe-perfect-recipe-reward-container").children(".reward-item").each(function() {
|
|
let perfectRecipeRewardItem = {
|
|
itemName: $(this).find(".reward-item-name").val(),
|
|
itemQuantity: parseInt( $(this).find(".reward-item-quantity").val() )
|
|
}
|
|
|
|
perfectRecipeReward.push(perfectRecipeRewardItem);
|
|
});
|
|
|
|
let timeToCraft = parseInt( $("#crafting-recipe-time-to-craft").val() );
|
|
let craftName = $("#crafting-recipe-name").val();
|
|
|
|
return {
|
|
resultItems: resultItems,
|
|
ingredients: ingredients,
|
|
perfectRecipeReward: perfectRecipeReward,
|
|
time: timeToCraft,
|
|
name: craftName
|
|
}
|
|
}
|
|
|
|
// Saves the new crafting recipe created
|
|
$("#crafting-recipe-form").submit(async function(event) {
|
|
if(isThereAnyErrorInForm(event)) return;
|
|
|
|
let craftingRecipeData = getCraftingRecipeData();
|
|
|
|
let craftingRecipeModal = $("#crafting-recipe-modal");
|
|
let action = craftingRecipeModal.data("action");
|
|
|
|
let response = null;
|
|
|
|
switch(action) {
|
|
case "create": {
|
|
response = await $.post(`https://${resName}/createNewCraftingRecipe`, JSON.stringify(craftingRecipeData))
|
|
break;
|
|
}
|
|
|
|
case "edit": {
|
|
let craftingId = craftingRecipeModal.data("craftingId");
|
|
|
|
response = await $.post(`https://${resName}/updateCraftingRecipe`, JSON.stringify({craftingId: craftingId, craftingRecipe: craftingRecipeData}));
|
|
break;
|
|
}
|
|
}
|
|
|
|
showServerResponse(response);
|
|
|
|
craftingRecipeModal.modal("hide");
|
|
loadCraftingRecipes();
|
|
})
|
|
|
|
// To edit an existing crafting recipe
|
|
function editCraftingRecipe(craftingId) {
|
|
|
|
// Adapts the modal to edit instead of create
|
|
$("#delete-crafting-recipe-btn").show();
|
|
$("#crafting-recipe-modal-confirm-btn").text( getLocalizedText("menu:save") );
|
|
|
|
let craftingData = craftingRecipes[craftingId];
|
|
|
|
$("#crafting-recipe-time-to-craft").val(craftingData.time);
|
|
$("#crafting-recipe-name").val(craftingData.name);
|
|
|
|
$("#crafting-recipe-result-items-container").empty();
|
|
$("#crafting-recipe-ingredients-container").empty();
|
|
$("#crafting-recipe-perfect-recipe-reward-container").empty();
|
|
|
|
for (let resultItem of craftingData.resultItems) {
|
|
addResultItemInCraftingRecipe(resultItem.itemName, resultItem.itemQuantity);
|
|
}
|
|
|
|
for (let rewardItem of craftingData.perfectRecipeReward) {
|
|
addRewardItemInCraftingRecipe(rewardItem.itemName, rewardItem.itemQuantity);
|
|
}
|
|
|
|
for (let ingredient of craftingData.ingredients) {
|
|
addIngredientInCraftingRecipe(ingredient.ingredientItemName, ingredient.ingredientMinQuantity, ingredient.ingredientMaxQuantity, ingredient.ingredientPerfectQuantity, ingredient.loseOnUse);
|
|
}
|
|
|
|
let craftingRecipeModal = $("#crafting-recipe-modal");
|
|
|
|
craftingRecipeModal.modal("show");
|
|
craftingRecipeModal.data("action", "edit");
|
|
craftingRecipeModal.data("craftingId", craftingId);
|
|
}
|
|
|
|
// Deletes crafting recipe
|
|
$("#delete-crafting-recipe-btn").click(async function() {
|
|
if(!await confirmDeletion()) return;
|
|
|
|
let craftingId = $("#crafting-recipe-modal").data("craftingId");
|
|
|
|
const response = await $.post(`https://${resName}/deleteCraftingRecipe`, JSON.stringify({craftingId: craftingId}));
|
|
showServerResponse(response);
|
|
|
|
$("#crafting-recipe-modal").modal("hide");
|
|
loadCraftingRecipes();
|
|
})
|
|
|
|
// Adds a reward item in the crafting recipe for perfect crafting
|
|
function addRewardItemInCraftingRecipe(itemName = "", itemQuantity = 1) {
|
|
let rewardsContainer = $("#crafting-recipe-perfect-recipe-reward-container");
|
|
|
|
let rewardItemDiv = $(`
|
|
<div class="row g-2 mb-2 align-items-center reward-item">
|
|
<div class="col-md">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control reward-item-name" placeholder="Item name" value="${itemName}" required>
|
|
<label>${getLocalizedText("menu:item_name")}</label>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-secondary col-2 choose-item-btn">${getLocalizedText("menu:choose_item")}</button>
|
|
|
|
<div class="col-md ms-2">
|
|
<div class="form-floating">
|
|
<input type="number" class="form-control reward-item-quantity" placeholder="Item quantity" value="${itemQuantity}" required>
|
|
<label>${getLocalizedText("menu:quantity")}</label>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="button" class="btn-close remove-reward-item-btn" aria-label="Close"></button>
|
|
</div>
|
|
`);
|
|
|
|
rewardItemDiv.find(".choose-item-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
rewardItemDiv.find(".reward-item-name").val(itemName);
|
|
})
|
|
|
|
rewardItemDiv.find(".remove-reward-item-btn").click(function() {
|
|
rewardItemDiv.remove();
|
|
})
|
|
|
|
rewardsContainer.append(rewardItemDiv);
|
|
}
|
|
$("#crafting-recipe-add-reward-item-btn").click(function() {
|
|
addRewardItemInCraftingRecipe();
|
|
})
|
|
|
|
// Laboratories
|
|
let laboratoriesDatatable = $("#laboratories-container").DataTable( {
|
|
"lengthMenu": [10, 15, 20],
|
|
"createdRow": function ( row, data, index ) {
|
|
$(row).addClass("clickable");
|
|
|
|
$(row).click(function() {
|
|
let laboratoryId = parseInt(data[0]);
|
|
|
|
editLaboratory(laboratoryId);
|
|
})
|
|
},
|
|
} );
|
|
|
|
let laboratories = {};
|
|
function loadLaboratories() {
|
|
$.post(`https://${resName}/getAllLaboratories`, {}, function(rawLaboratories) {
|
|
// Manually create the table to avoid incompatibilities due table indexing
|
|
laboratories = {};
|
|
|
|
for(const[k, laboratoryData] of Object.entries(rawLaboratories)) {
|
|
laboratories[laboratoryData.id] = laboratoryData;
|
|
}
|
|
|
|
laboratoriesDatatable.clear();
|
|
|
|
for(const[laboratoryId, laboratoryData] of Object.entries(laboratories)) {
|
|
laboratoriesDatatable.row.add([
|
|
laboratoryId,
|
|
laboratoryData.name,
|
|
laboratoryData.coords.x,
|
|
laboratoryData.coords.y,
|
|
laboratoryData.coords.z,
|
|
])
|
|
}
|
|
|
|
laboratoriesDatatable.draw()
|
|
})
|
|
}
|
|
|
|
// Create new laboratory
|
|
$("#new-laboratory-btn").click(function() {
|
|
let laboratoryModal = $("#laboratory-modal");
|
|
|
|
laboratoryModal.modal("show");
|
|
laboratoryModal.data("action", "create");
|
|
|
|
$("#laboratory-name").val("");
|
|
$("#laboratory-coords-x").val("");
|
|
$("#laboratory-coords-y").val("");
|
|
$("#laboratory-coords-z").val("");
|
|
|
|
$("#laboratory-scale-x").val("1.5");
|
|
$("#laboratory-scale-y").val("1.5");
|
|
$("#laboratory-scale-z").val("0.5");
|
|
|
|
$("#laboratory-icon-type").val("1");
|
|
|
|
$("#laboratory-opacity").val("100");
|
|
|
|
$("#laboratory-map-blip").prop("checked", false).change();
|
|
|
|
// Resets stuff related to allowed jobs (all allowed)
|
|
setLaboratoryAllowedJobsText(false);
|
|
$("#laboratory-allowed-jobs").data("jobs", false);
|
|
|
|
// Resets stuff related to allowed recipes
|
|
$("#laboratory-allowed-crafting-recipes").text(getLocalizedText("menu:no_crafting_recipe_allowed"));
|
|
$("#laboratory-allowed-crafting-recipes").data("craftingRecipes", {});
|
|
|
|
// Adapts the modal from edit mode to create mode
|
|
$("#delete-laboratory-btn").hide();
|
|
$("#laboratory-modal-confirm-btn").text( getLocalizedText("menu:create") );
|
|
})
|
|
|
|
function setLaboratoryAllowedJobsText(jobs) {
|
|
if(jobs === false) {
|
|
$("#laboratory-allowed-jobs").text(getLocalizedText("menu:all_jobs_allowed"));
|
|
} else if(Object.keys(jobs).length == 0) {
|
|
$("#laboratory-allowed-jobs").text(getLocalizedText("menu:no_job_allowed"));
|
|
} else {
|
|
$("#laboratory-allowed-jobs").text( Object.keys(jobs).join(", ") );
|
|
}
|
|
}
|
|
|
|
$("#laboratory-choose-jobs").click(async function() {
|
|
const oldJobs = $("#laboratory-allowed-jobs").data("jobs");
|
|
const jobs = await jobsDialog(oldJobs);
|
|
|
|
setLaboratoryAllowedJobsText(jobs);
|
|
|
|
$("#laboratory-allowed-jobs").data("jobs", jobs)
|
|
})
|
|
|
|
// Dialog that shows all crafting recipes and returns the selected ones
|
|
function craftingRecipesDialog(cb) {
|
|
let inputCraftingRecipesModal = $("#input-crafting-recipes-dialog-modal")
|
|
inputCraftingRecipesModal.modal("show");
|
|
|
|
$("#input-crafting-recipes-search").val("");
|
|
|
|
$.post(`https://${resName}/getAllCraftingRecipes`, JSON.stringify({}), function (craftingRecipes) {
|
|
let craftingRecipesListDiv = $("#crafting-recipes-list");
|
|
|
|
craftingRecipesListDiv.empty();
|
|
|
|
for(const[k, craftingRecipeData] of Object.entries(craftingRecipes)) {
|
|
let craftingRecipeDiv = $(`
|
|
<div class="form-check fs-3">
|
|
<input class="form-check-input" type="checkbox" value="${craftingRecipeData.id}">
|
|
<label class="form-check-label">
|
|
${craftingRecipeData.name}
|
|
</label>
|
|
</div>
|
|
`);
|
|
|
|
craftingRecipesListDiv.append(craftingRecipeDiv);
|
|
}
|
|
|
|
// Unbinds the button and rebinds it to callback the selected crafting recipes
|
|
$("#input-crafting-recipes-confirm-btn").unbind().click(function() {
|
|
let selectedCraftingRecipes = {};
|
|
|
|
craftingRecipesListDiv.find("input:checked").each(function() {
|
|
let recipeId = $(this).val();
|
|
|
|
selectedCraftingRecipes[recipeId] = true;
|
|
});
|
|
|
|
inputCraftingRecipesModal.modal("hide");
|
|
|
|
cb(selectedCraftingRecipes);
|
|
})
|
|
});
|
|
}
|
|
$("#input-crafting-recipe-search").on("keyup", function() {
|
|
let text = $(this).val().toLowerCase();
|
|
|
|
$("#crafting-recipes-list .form-check").filter(function() {
|
|
$(this).toggle($(this).text().toLowerCase().indexOf(text) > -1)
|
|
});
|
|
})
|
|
|
|
// Choose crafting recipes for laboratory
|
|
$("#laboratory-choose-recipes").click(function() {
|
|
craftingRecipesDialog((selectedRecipes) => {
|
|
if(Object.keys(selectedRecipes).length == 0) {
|
|
$("#laboratory-allowed-crafting-recipes").text(getLocalizedText("menu:no_crafting_recipe_allowed"));
|
|
} else {
|
|
$("#laboratory-allowed-crafting-recipes").text(Object.keys(selectedRecipes).map(recipeId => craftingRecipes[recipeId].name).join(", "));
|
|
}
|
|
|
|
$("#laboratory-allowed-crafting-recipes").data("recipes", selectedRecipes);
|
|
})
|
|
})
|
|
|
|
// Sets current coords for laboratory
|
|
$("#laboratory-current-coords-btn").click(async function(event) {
|
|
if(event.shiftKey) {
|
|
const coordsInUI = {
|
|
x: parseFloat( $("#laboratory-coords-x").val() ),
|
|
y: parseFloat( $("#laboratory-coords-y").val() ),
|
|
z: parseFloat( $("#laboratory-coords-z").val() )
|
|
}
|
|
|
|
teleportToCoords(coordsInUI);
|
|
return;
|
|
}
|
|
|
|
const coords = await getCurrentCoords();
|
|
$("#laboratory-coords-x").val(coords.x);
|
|
$("#laboratory-coords-y").val(coords.y);
|
|
$("#laboratory-coords-z").val(coords.z);
|
|
|
|
})
|
|
|
|
// To edit an existing laboratory
|
|
function editLaboratory(laboratoryId) {
|
|
let laboratoryModal = $("#laboratory-modal");
|
|
|
|
laboratoryModal.data("laboratoryId", laboratoryId);
|
|
laboratoryModal.data("action", "edit");
|
|
|
|
let laboratoryData = laboratories[laboratoryId];
|
|
|
|
$("#laboratory-name").val(laboratoryData.name);
|
|
|
|
$("#laboratory-coords-x").val(laboratoryData.coords.x);
|
|
$("#laboratory-coords-y").val(laboratoryData.coords.y);
|
|
$("#laboratory-coords-z").val(laboratoryData.coords.z);
|
|
|
|
$("#laboratory-scale-x").val(laboratoryData.scale.x);
|
|
$("#laboratory-scale-y").val(laboratoryData.scale.y);
|
|
$("#laboratory-scale-z").val(laboratoryData.scale.z);
|
|
|
|
$("#laboratory-bounce").prop("checked", laboratoryData.bounce);
|
|
$("#laboratory-follow-camera").prop("checked", laboratoryData.followCamera);
|
|
$("#laboratory-rotate").prop("checked", laboratoryData.rotate);
|
|
|
|
$("#laboratory-icon-type").val(laboratoryData.iconType);
|
|
|
|
$("#laboratory-color").val( rgbToHex(laboratoryData.color.r, laboratoryData.color.g, laboratoryData.color.b) );
|
|
$("#laboratory-opacity").val(laboratoryData.opacity);
|
|
|
|
let mapBlipCheckbox = $("#laboratory-map-blip");
|
|
if(laboratoryData.blipSprite) {
|
|
$("#laboratory-blip-name").val(laboratoryData.blipName);
|
|
$("#laboratory-sprite-id").val(laboratoryData.blipSprite);
|
|
$("#laboratory-blip-color").val(laboratoryData.blipColor);
|
|
$("#laboratory-blip-scale").val(laboratoryData.blipScale);
|
|
|
|
mapBlipCheckbox.prop("checked", true);
|
|
} else {
|
|
mapBlipCheckbox.prop("checked", false);
|
|
}
|
|
mapBlipCheckbox.change();
|
|
|
|
|
|
setLaboratoryAllowedJobsText(laboratoryData.allowedJobs);
|
|
$("#laboratory-allowed-jobs").data("jobs", laboratoryData.allowedJobs);
|
|
|
|
let sanitizedAllowedRecipes = {};
|
|
|
|
Object.keys(laboratoryData.allowedRecipes).forEach(recipeId => {
|
|
if(recipeId == 0) {
|
|
recipeId = 1;
|
|
}
|
|
|
|
sanitizedAllowedRecipes[recipeId] = true;
|
|
});
|
|
|
|
$("#laboratory-allowed-crafting-recipes").text( Object.keys(sanitizedAllowedRecipes).map(recipeId => craftingRecipes[recipeId] ? craftingRecipes[recipeId].name : null).join(", ") );
|
|
$("#laboratory-allowed-crafting-recipes").data("recipes", sanitizedAllowedRecipes);
|
|
|
|
// Adatps the modal from create mode to edit mode
|
|
$("#delete-laboratory-btn").show();
|
|
$("#laboratory-modal-confirm-btn").text( getLocalizedText("menu:save") );
|
|
|
|
laboratoryModal.modal("show");
|
|
}
|
|
|
|
// To delete an existing laboratory
|
|
$("#delete-laboratory-btn").click(async function() {
|
|
if(!await confirmDeletion()) return;
|
|
|
|
let laboratoryId = $("#laboratory-modal").data("laboratoryId");
|
|
if(laboratoryId == null) return;
|
|
|
|
const response = await $.post(`https://${resName}/deleteLaboratory`, JSON.stringify({
|
|
laboratoryId: laboratoryId
|
|
}));
|
|
|
|
showServerResponse(response);
|
|
|
|
$("#laboratory-modal").modal("hide");
|
|
loadLaboratories();
|
|
})
|
|
|
|
$("#laboratory-map-blip").change(function() {
|
|
let enabled = $(this).prop("checked");
|
|
$("#laboratory-map-blip-inputs").find("input").prop("disabled", !enabled);
|
|
})
|
|
|
|
// Gets the laboratory data from its modal
|
|
function getLaboratoryData() {
|
|
let laboratoryData = {
|
|
name: $("#laboratory-name").val(),
|
|
|
|
coords: {
|
|
x: parseFloat( $("#laboratory-coords-x").val() ),
|
|
y: parseFloat( $("#laboratory-coords-y").val() ),
|
|
z: parseFloat( $("#laboratory-coords-z").val() )
|
|
},
|
|
|
|
iconType: parseInt( $("#laboratory-icon-type").val() ),
|
|
|
|
scale: {
|
|
x: parseFloat( $("#laboratory-scale-x").val() ),
|
|
y: parseFloat( $("#laboratory-scale-y").val() ),
|
|
z: parseFloat( $("#laboratory-scale-z").val() ),
|
|
},
|
|
|
|
bounce: $("#laboratory-bounce").prop("checked"),
|
|
followCamera: $("#laboratory-follow-camera").prop("checked"),
|
|
rotate: $("#laboratory-rotate").prop("checked"),
|
|
|
|
color: hexToRgb( $("#laboratory-color").val() ),
|
|
opacity: parseInt( $("#laboratory-opacity").val() ),
|
|
|
|
allowedJobs: $("#laboratory-allowed-jobs").data("jobs"),
|
|
allowedRecipes: $("#laboratory-allowed-crafting-recipes").data("recipes") || {}
|
|
}
|
|
|
|
let isBlipEnabled = $("#laboratory-map-blip").prop("checked");
|
|
if( isBlipEnabled ) {
|
|
laboratoryData.blipName = $("#laboratory-blip-name").val();
|
|
laboratoryData.blipSprite = parseInt( $("#laboratory-sprite-id").val() );
|
|
laboratoryData.blipColor = parseInt( $("#laboratory-blip-color").val() );
|
|
laboratoryData.blipScale = parseFloat( $("#laboratory-blip-scale").val() );
|
|
}
|
|
|
|
return laboratoryData;
|
|
}
|
|
|
|
// Saves laboratory data
|
|
$("#laboratory-form").submit(async function(event) {
|
|
if(isThereAnyErrorInForm(event)) return;
|
|
|
|
let laboratoryModal = $("#laboratory-modal");
|
|
let laboratoryData = getLaboratoryData();
|
|
let action = laboratoryModal.data("action");
|
|
|
|
let response = null;
|
|
|
|
switch(action) {
|
|
case "create": {
|
|
response = await $.post(`https://${resName}/createNewLaboratory`, JSON.stringify(laboratoryData))
|
|
break;
|
|
}
|
|
|
|
case "edit": {
|
|
let laboratoryId = laboratoryModal.data("laboratoryId");
|
|
|
|
response = await $.post(`https://${resName}/updateLaboratory`, JSON.stringify({laboratoryId: laboratoryId, laboratoryData: laboratoryData}))
|
|
break;
|
|
}
|
|
}
|
|
showServerResponse(response);
|
|
|
|
laboratoryModal.modal("hide");
|
|
loadLaboratories();
|
|
})
|
|
|
|
function addRequiredItemInPocketCrafting(pocketCraftingDiv, itemName = "", itemQuantity = 1, loseOnUse = true) {
|
|
let requiredItemsContainer = pocketCraftingDiv.find(".pocket-crafting-required-items");
|
|
|
|
let requiredItemDiv = $(`
|
|
<div class="d-flex justify-content-center align-items-center gap-3 mb-2 required-item">
|
|
<div class="form-floating col-3">
|
|
<input type="text" class="form-control required-item-name" value="${itemName}" required>
|
|
<label>${getLocalizedText("menu:item_name")}</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-secondary col-auto choose-item-btn me-3" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose_item") }"><i class="bi bi-list-ul"></i></button>
|
|
|
|
<div class="form-check my-auto me-3">
|
|
<input class="form-check-input" type="checkbox" ${loseOnUse ? "checked" : null}>
|
|
<label class="form-check-label">${ getLocalizedText("menu:lose_on_use") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating col-3">
|
|
<input type="number" class="form-control required-item-quantity" value="${itemQuantity}" required>
|
|
<label>${getLocalizedText("menu:quantity")}</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn-close remove-required-item-btn"></button>
|
|
</div>
|
|
`);
|
|
|
|
requiredItemDiv.find(".choose-item-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
requiredItemDiv.find(".required-item-name").val(itemName);
|
|
}).tooltip();
|
|
|
|
requiredItemDiv.find(".remove-required-item-btn").click(function() {
|
|
requiredItemDiv.remove();
|
|
});
|
|
|
|
requiredItemsContainer.append(requiredItemDiv);
|
|
}
|
|
|
|
async function addPocketCrafting(itemName, pocketCraftingData, isNew = false) {
|
|
const itemLabel = await getItemLabel(itemName) || "Unknown item - " + itemName;
|
|
|
|
const div = $(`
|
|
<div class="pocket-crafting mb-4" data-item-to-use="${itemName}">
|
|
<h2 class="text-center title">${getLocalizedText("menu:you_have_to_use")} <span class="text-success">'${itemLabel}'</span> ${getLocalizedText("menu:to_start_the_crafting")} <span class="fs-5 fw-lighter fst-italic">(${itemName})</span></h2>
|
|
|
|
<div class="d-flex justify-content-center align-items-center gap-3 mb-5">
|
|
<h3 class="my-auto">${getLocalizedText("menu:to_receive")}</h3>
|
|
|
|
<div class="form-floating col-3">
|
|
<input type="text" class="form-control result-item-name" required>
|
|
<label>${getLocalizedText("menu:pocket_craftings:result_item")}</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-secondary col-auto choose-item-btn me-5" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose_item") }"><i class="bi bi-list-ul"></i></button>
|
|
|
|
<div class="form-floating col-3">
|
|
<input type="number" min="1" class="form-control time-to-craft" required>
|
|
<label>${getLocalizedText("menu:crafting_recipe:time_to_craft")}</label>
|
|
</div>
|
|
|
|
<div class="form-floating col-3">
|
|
<input type="number" min="1" class="form-control item-quantity" required>
|
|
<label>${getLocalizedText("menu:quantity")}</label>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 class="text-center">${getLocalizedText("menu:pocket_craftings:required_items")}</h3>
|
|
|
|
<div class="pocket-crafting-required-items">
|
|
|
|
</div>
|
|
|
|
<div class="d-inline-block col-12 my-2">
|
|
<button type="button" class="btn float-start btn-danger delete-pocket-crafting">${getLocalizedText("menu:pocket_craftings:delete_pocket_crafting")}</button>
|
|
<button type="button" class="btn float-end btn-secondary add-required-item-btn">${getLocalizedText("menu:pocket_craftings:add_required_item")}</button>
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
`);
|
|
|
|
div.find(".choose-item-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
div.find(".result-item-name").val(itemName);
|
|
}).tooltip();
|
|
|
|
div.find(".add-required-item-btn").click(function() {
|
|
addRequiredItemInPocketCrafting(div);
|
|
});
|
|
|
|
div.find(".delete-pocket-crafting").click(function() {
|
|
div.remove();
|
|
});
|
|
|
|
if(pocketCraftingData) {
|
|
div.find(".result-item-name").val(pocketCraftingData.resultItem.name);
|
|
div.find(".item-quantity").val(pocketCraftingData.resultItem.quantity);
|
|
div.find(".time-to-craft").val(pocketCraftingData.timeToCraft);
|
|
|
|
for(const requiredItem of pocketCraftingData.requiredItems) {
|
|
addRequiredItemInPocketCrafting(div, requiredItem.name, requiredItem.quantity, requiredItem.loseOnUse);
|
|
}
|
|
}
|
|
|
|
if(isNew) {
|
|
div.find(".title").append(`<span class="fs-5 fw-lighter fst-italic text-danger"> - ${ getLocalizedText("menu:effects:may_need_restart") }</span>`)
|
|
}
|
|
|
|
$("#pocket-craftings-list").append(div);
|
|
}
|
|
|
|
// Pocket sellings
|
|
$("#create-new-pocket-crafting-btn").click(async function() {
|
|
const itemName = await itemsDialog(getLocalizedText("menu:choose_the_item_that_will_start_pocket_crafting_on_use"));
|
|
if(!itemName) return;
|
|
|
|
addPocketCrafting(itemName, null, true);
|
|
});
|
|
|
|
function getAllPocketCraftings() {
|
|
const pocketCraftings = {};
|
|
|
|
$("#pocket-craftings-list").find(".pocket-crafting").each(function() {
|
|
const itemName = $(this).data("itemToUse");
|
|
|
|
const pocketCrafting = {
|
|
resultItem: {
|
|
name: $(this).find(".result-item-name").val(),
|
|
quantity: parseInt($(this).find(".item-quantity").val())
|
|
},
|
|
timeToCraft: parseInt($(this).find(".time-to-craft").val()),
|
|
requiredItems: []
|
|
};
|
|
|
|
$(this).find(".required-item").each(function() {
|
|
const requiredItem = {
|
|
name: $(this).find(".required-item-name").val(),
|
|
quantity: parseInt($(this).find(".required-item-quantity").val()),
|
|
loseOnUse: $(this).find(".form-check-input").prop("checked")
|
|
};
|
|
|
|
pocketCrafting.requiredItems.push(requiredItem);
|
|
});
|
|
|
|
pocketCraftings[itemName] = pocketCrafting;
|
|
});
|
|
|
|
return pocketCraftings;
|
|
}
|
|
|
|
$("#pocket-craftings").submit(async function(event) {
|
|
if(isThereAnyErrorInForm(event)) return;
|
|
|
|
let serverSettings = {
|
|
pocketCraftings: getAllPocketCraftings(),
|
|
}
|
|
|
|
const response = await $.post(`https://${resName}/saveSettings`, JSON.stringify({
|
|
clientSettings: {},
|
|
serverSettings: serverSettings,
|
|
sharedSettings: {}
|
|
}));
|
|
showServerResponse(response);
|
|
});
|
|
|
|
// Plane selling
|
|
$("#plane-selling-current-coords-btn").click(async function(event) {
|
|
if(event.shiftKey) {
|
|
const coordsInUI = {
|
|
x: parseFloat( $("#plane-selling-coords-x").val() ),
|
|
y: parseFloat( $("#plane-selling-coords-y").val() ),
|
|
z: parseFloat( $("#plane-selling-coords-z").val() )
|
|
}
|
|
|
|
teleportToCoords(coordsInUI);
|
|
return;
|
|
}
|
|
|
|
const coords = await getCurrentCoords();
|
|
|
|
$("#plane-selling-coords-x").val(coords.x);
|
|
$("#plane-selling-coords-y").val(coords.y);
|
|
$("#plane-selling-coords-z").val(coords.z);
|
|
})
|
|
|
|
function togglePlaneWholeOcean() {
|
|
let enabled = $("#plane-use-the-whole-ocean").prop("checked");
|
|
|
|
$("#plane-selling-coords").find("input").prop("disabled", enabled);
|
|
}
|
|
$("#plane-use-the-whole-ocean").change(togglePlaneWholeOcean);
|
|
|
|
$("#plane-add-new-drug").click(function() {
|
|
addAcceptableDrugToDiv("#plane-selling-acceptable-drugs");
|
|
})
|
|
|
|
// Enables/Disables all inputs for plane selling
|
|
function togglePlaneSelling() {
|
|
let enable = $("#enable-plane-selling").prop("checked");
|
|
|
|
$("#selling-plane").find("input, button").not("#enable-plane-selling").prop("disabled", !enable);
|
|
}
|
|
$("#enable-plane-selling").change(togglePlaneSelling);
|
|
|
|
// Boat selling
|
|
$("#boat-selling-current-coords-btn").click(async function(event) {
|
|
if(event.shiftKey) {
|
|
const coordsInUI = {
|
|
x: parseFloat( $("#boat-selling-coords-x").val() ),
|
|
y: parseFloat( $("#boat-selling-coords-y").val() ),
|
|
z: parseFloat( $("#boat-selling-coords-z").val() )
|
|
}
|
|
|
|
teleportToCoords(coordsInUI);
|
|
return;
|
|
}
|
|
|
|
const coords = await getCurrentCoords();
|
|
|
|
$("#boat-selling-coords-x").val(coords.x);
|
|
$("#boat-selling-coords-y").val(coords.y);
|
|
$("#boat-selling-coords-z").val(coords.z);
|
|
});
|
|
|
|
function toggleBoatWholeOcean() {
|
|
let enabled = $("#boat-use-the-whole-ocean").prop("checked");
|
|
|
|
$("#boat-selling-coords").find("input").prop("disabled", enabled);
|
|
}
|
|
$("#boat-use-the-whole-ocean").change(toggleBoatWholeOcean);
|
|
|
|
$("#boat-add-new-drug").click(function() {
|
|
addAcceptableDrugToDiv("#boat-selling-acceptable-drugs");
|
|
})
|
|
|
|
function toggleBoatSelling() {
|
|
let enable = $("#enable-boat-selling").prop("checked");
|
|
|
|
$("#selling-boat").find("input, button").not("#enable-boat-selling").prop("disabled", !enable);
|
|
}
|
|
$("#enable-boat-selling").change(toggleBoatSelling);
|
|
|
|
// NPC Selling
|
|
function toggleNpcSelling() {
|
|
let enable = $("#enable-npc-selling").prop("checked");
|
|
|
|
$("#selling-npc").find("input, button").not("#enable-npc-selling").prop("disabled", !enable);
|
|
}
|
|
$("#enable-npc-selling").change(toggleNpcSelling);
|
|
|
|
function addAcceptableDrugToDiv(drugsListDiv, drugName = "", minPrice = 500, maxPrice = 1000) {
|
|
let drugDiv = $(`
|
|
<div class="d-flex gap-3 align-items-center justify-content-center mt-2 mb-3 drug">
|
|
<div class="form-floating col-4">
|
|
<input type="text" class="form-control drug-name" placeholder="Name" required value=${drugName}>
|
|
<label>Drug name</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-secondary col-auto choose-item-btn me-5" data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:choose_item") }"><i class="bi bi-list-ul"></i></button>
|
|
|
|
<div class="form-floating">
|
|
<input type="number" class="form-control drug-min-price" placeholder="Max quantity" required value=${minPrice}>
|
|
<label>${ getLocalizedText("menu:min_price") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating">
|
|
<input type="number" class="form-control drug-max-price" placeholder="Max quantity" required value=${maxPrice}>
|
|
<label>${ getLocalizedText("menu:max_price") }</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn-close btn-close-white remove-btn ms-3"></button>
|
|
</div>
|
|
`)
|
|
|
|
drugDiv.find(".choose-item-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
drugDiv.find(".drug-name").val(itemName);
|
|
}).tooltip();
|
|
|
|
drugDiv.find(".remove-btn").click(function() {
|
|
drugDiv.remove();
|
|
});
|
|
|
|
$(drugsListDiv).append(drugDiv);
|
|
}
|
|
|
|
function fillAcceptableDrugsForDiv(drugsListDiv, drugs) {
|
|
$(drugsListDiv).empty();
|
|
|
|
if(drugs) {
|
|
for(drug of drugs) {
|
|
addAcceptableDrugToDiv(drugsListDiv, drug.name, drug.minPrice, drug.maxPrice);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getAcceptableDrugFromDiv(drugsListDiv) {
|
|
let drugs = [];
|
|
|
|
$(drugsListDiv).find(".drug").each(function() {
|
|
let drug = {
|
|
name: $(this).find(".drug-name").val(),
|
|
minPrice: parseInt( $(this).find(".drug-min-price").val() ),
|
|
maxPrice: parseInt( $(this).find(".drug-max-price").val() ),
|
|
}
|
|
|
|
drugs.push(drug);
|
|
})
|
|
|
|
return drugs;
|
|
}
|
|
|
|
$("#npc-add-new-drug").click(function() {
|
|
addAcceptableDrugToDiv("#npc-selling-acceptable-drugs");
|
|
})
|
|
|
|
$("#npc-selling-command-is-enabled").change(function() {
|
|
const enabled = $(this).prop("checked");
|
|
|
|
$("#npc-selling-command").prop("disabled", !enabled);
|
|
$("#npc-selling-command-has-to-spawn-npc").prop("disabled", !enabled);
|
|
});
|
|
|
|
// Save selling
|
|
$("#selling").submit(async function(event) {
|
|
if(isThereAnyErrorInForm(event)) return;
|
|
|
|
let clientSettings = {
|
|
// Boat and plane selling
|
|
heightToSell: parseInt( $("#minimum-plane-height").val() ),
|
|
|
|
sellUseWholeOcean: {
|
|
plane: $("#plane-use-the-whole-ocean").prop("checked"),
|
|
boat: $("#boat-use-the-whole-ocean").prop("checked")
|
|
},
|
|
|
|
sellArea: {
|
|
plane: {
|
|
coords: {
|
|
x: parseFloat( $("#plane-selling-coords-x").val() ),
|
|
y: parseFloat( $("#plane-selling-coords-y").val() ),
|
|
z: parseFloat( $("#plane-selling-coords-z").val() )
|
|
},
|
|
|
|
radius: parseFloat( $("#plane-selling-coords-radius").val() )
|
|
},
|
|
|
|
boat: {
|
|
coords: {
|
|
x: parseFloat( $("#boat-selling-coords-x").val() ),
|
|
y: parseFloat( $("#boat-selling-coords-y").val() ),
|
|
z: parseFloat( $("#boat-selling-coords-z").val() )
|
|
},
|
|
|
|
radius: parseFloat( $("#boat-selling-coords-radius").val() )
|
|
}
|
|
},
|
|
|
|
sellShowRadius: {
|
|
plane: $("#plane-show-radius-while-selling").prop("checked"),
|
|
boat: $("#boat-show-radius-while-selling").prop("checked")
|
|
},
|
|
|
|
// Narcos selling
|
|
narcosModel: $("#narcos-model").val(),
|
|
|
|
showNarcosBlip: $("#enable-narcos-blip").prop("checked"),
|
|
narcosBlip: {
|
|
name: $("#narcos-selling-blip-name").val(),
|
|
color: parseInt( $("#narcos-selling-blip-color").val() ),
|
|
scale: parseFloat( $("#narcos-selling-blip-scale").val() ),
|
|
sprite: parseInt( $("#narcos-selling-blip-sprite").val() )
|
|
},
|
|
|
|
// Pushers selling
|
|
pusherModel: $("#pushers-model").val(),
|
|
showPushersBlips: $("#enable-pushers-blip").prop("checked"),
|
|
pusherBlip: {
|
|
name: $("#pushers-selling-blip-name").val(),
|
|
color: parseInt( $("#pushers-selling-blip-color").val() ),
|
|
scale: parseFloat( $("#pushers-selling-blip-scale").val() ),
|
|
sprite: parseInt( $("#pushers-selling-blip-sprite").val() )
|
|
}
|
|
}
|
|
|
|
let serverSettings = {
|
|
rewards: {
|
|
plane: getRewardDivData("#plane-selling-reward-div"),
|
|
boat: getRewardDivData("#boat-selling-reward-div"),
|
|
npc: getRewardDivData("#npc-selling-reward-div"),
|
|
narcos: getRewardDivData("#narcos-selling-reward-div"),
|
|
pushers: getRewardDivData("#pushers-selling-reward-div")
|
|
},
|
|
|
|
// Plane selling
|
|
planeAcceptableDrugs: getAcceptableDrugFromDiv("#plane-selling-acceptable-drugs"),
|
|
planeSellingMinimumPolice: parseInt( $("#plane-minimum-police").val() ),
|
|
|
|
// Boat selling
|
|
boatAcceptableDrugs: getAcceptableDrugFromDiv("#boat-selling-acceptable-drugs"),
|
|
boatSellingMinimumPolice: parseInt( $("#boat-minimum-police").val() ),
|
|
|
|
// NPC selling
|
|
minNPCSellQuantity: parseInt( $("#npc-selling-min-quantity").val() ),
|
|
maxNPCSellQuantity: parseInt( $("#npc-selling-max-quantity").val() ),
|
|
|
|
sellToNPCChancesToAccept: parseInt( $("#npc-accept-chances").val() ),
|
|
|
|
maxNPCsSellableDrugQuantity: parseInt( $("#npc-max-drug-quantity").val() ),
|
|
|
|
npcAcceptableDrugs: getAcceptableDrugFromDiv("#npc-selling-acceptable-drugs"),
|
|
|
|
npcSellingMinimumPolice: parseInt( $("#npc-minimum-police").val() ),
|
|
|
|
npcAlertPoliceChances: parseInt( $("#npc-alert-police-chances").val() ),
|
|
|
|
canNPCRobPlayer: $("#npc-can-rob-player").prop("checked"),
|
|
canNPCAttackPlayer: $("#npc-can-attack-player").prop("checked"),
|
|
|
|
// Narcos selling
|
|
narcosBuyerLocations: getNarcosBuyerLocations(),
|
|
narcosLocationChangeTime: parseInt( $("#narcos-location-change").val() ),
|
|
narcosCallPoliceChances: parseInt( $("#narcos-police-alert-chances").val() ),
|
|
narcosAcceptsOnlyOneBuyerPerLocation: $("#narcos-accepts-only-one-buyer-per-location").prop("checked"),
|
|
|
|
narcosNeededDrugs: getNarcosDrugs(),
|
|
|
|
narcosSellingMinimumPolice: parseInt( $("#narcos-minimum-police").val() ),
|
|
|
|
// Pushers selling
|
|
pushersCallPoliceChances: parseInt( $("#pushers-alert-police-chances").val() ),
|
|
pushers: getPushers(),
|
|
pushersSellingMinimumPolice: parseInt( $("#pushers-minimum-police").val() ),
|
|
}
|
|
|
|
let sharedSettings = {
|
|
enableAirplaneSell: $("#enable-plane-selling").prop("checked"),
|
|
enableBoatSell: $("#enable-boat-selling").prop("checked"),
|
|
|
|
timeToSellInPlane: parseInt( $("#time-to-sell-in-plane").val() ),
|
|
timeToSellInBoat: parseInt( $("#time-to-sell-in-boat").val() ),
|
|
|
|
alarmPoliceInPlane: $("#enable-plane-police-alert").prop("checked"),
|
|
alarmPoliceInBoat: $("#enable-boat-police-alert").prop("checked"),
|
|
|
|
// NPC selling
|
|
npcSecondsToSell: parseInt( $("#npc-seconds-to-sell").val() ),
|
|
|
|
// Pushers selling
|
|
arePushersEnabled: $("#enable-pushers-selling").prop("checked"),
|
|
|
|
// Narcos selling
|
|
enableNarcosSelling: $("#enable-narcos-selling").prop("checked"),
|
|
|
|
enableNPCSell: $("#enable-npc-selling").prop("checked"),
|
|
}
|
|
|
|
const response = await $.post(`https://${resName}/saveSettings`, JSON.stringify({
|
|
clientSettings: clientSettings,
|
|
serverSettings: serverSettings,
|
|
sharedSettings: sharedSettings
|
|
}));
|
|
showServerResponse(response);
|
|
})
|
|
|
|
// Narcos selling
|
|
function toggleNarcosSelling() {
|
|
let narcosSelling = $("#enable-narcos-selling").prop("checked");
|
|
|
|
$("#selling-narcos").find("input, button").not("#enable-narcos-selling").prop("disabled", !narcosSelling);
|
|
}
|
|
$("#enable-narcos-selling").change(toggleNarcosSelling);
|
|
|
|
function toggleNarcosBlip() {
|
|
let narcosBlip = $("#enable-narcos-blip").prop("checked");
|
|
|
|
$("#narcos-selling-blip-name").prop("disabled", !narcosBlip);
|
|
$("#narcos-selling-blip-color").prop("disabled", !narcosBlip);
|
|
$("#narcos-selling-blip-scale").prop("disabled", !narcosBlip);
|
|
$("#narcos-selling-blip-sprite").prop("disabled", !narcosBlip);
|
|
}
|
|
$("#enable-narcos-blip").change(toggleNarcosBlip);
|
|
|
|
function addNarcosSpawnLocation(coords = {}, heading = 0.0) {
|
|
let newSpawnDiv = $(`
|
|
<div class="mb-5 spawn-location">
|
|
<div class="row g-2 row-cols-auto align-items-center text-body my-2">
|
|
<div class="form-floating col-3">
|
|
<input type="number" step="0.01" class="form-control max-two-decimals coord-x" placeholder="X" required value=${coords.x || ""}>
|
|
<label>${ getLocalizedText("menu:x") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating col-3">
|
|
<input type="number" step="0.01" class="form-control max-two-decimals coord-y" placeholder="Y" required value=${coords.y || ""}>
|
|
<label>${ getLocalizedText("menu:y") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating col-3">
|
|
<input type="number" step="0.01" class="form-control max-two-decimals coord-z" placeholder="Z" required value=${coords.z || ""}>
|
|
<label>${ getLocalizedText("menu:z") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating col-2">
|
|
<input type="number" step="0.01" class="form-control max-two-decimals heading" placeholder="Heading" required value=${heading}>
|
|
<label>${ getLocalizedText("menu:heading") }</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn-close btn-close-white ms-3 remove-btn"></button>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-secondary current-coords-btn">${ getLocalizedText("menu:current_coords_heading") }</button>
|
|
</div>
|
|
`)
|
|
|
|
newSpawnDiv.find(".max-two-decimals").on("change", maxTwoDecimals);
|
|
|
|
newSpawnDiv.find(".current-coords-btn").click(async function(event) {
|
|
if(event.shiftKey) {
|
|
const coordsInUI = {
|
|
x: parseFloat( newSpawnDiv.find(".coord-x").val() ),
|
|
y: parseFloat( newSpawnDiv.find(".coord-y").val() ),
|
|
z: parseFloat( newSpawnDiv.find(".coord-z").val() )
|
|
}
|
|
|
|
teleportToCoords(coordsInUI, parseFloat(newSpawnDiv.find(".heading").val()));
|
|
return;
|
|
}
|
|
|
|
const data = await getCurrentCoordsAndHeading();
|
|
|
|
newSpawnDiv.find(".coord-x").val(data.coords.x);
|
|
newSpawnDiv.find(".coord-y").val(data.coords.y);
|
|
newSpawnDiv.find(".coord-z").val(data.coords.z);
|
|
newSpawnDiv.find(".heading").val(data.heading);
|
|
});
|
|
|
|
newSpawnDiv.find(".remove-btn").click(function() {
|
|
newSpawnDiv.remove();
|
|
});
|
|
|
|
$("#narcos-spawn-locations").append(newSpawnDiv);
|
|
}
|
|
$("#narcos-add-spawn-btn").click(function() {
|
|
addNarcosSpawnLocation();
|
|
})
|
|
|
|
function getNarcosBuyerLocations() {
|
|
let buyerLocations = [];
|
|
|
|
$("#narcos-spawn-locations").find(".spawn-location").each(function() {
|
|
let spawnDiv = $(this);
|
|
|
|
let coords = {
|
|
x: parseFloat( spawnDiv.find(".coord-x").val() ),
|
|
y: parseFloat( spawnDiv.find(".coord-y").val() ),
|
|
z: parseFloat( spawnDiv.find(".coord-z").val() )
|
|
};
|
|
|
|
let heading = parseFloat( spawnDiv.find(".heading").val() );
|
|
|
|
buyerLocations.push({
|
|
coords: coords,
|
|
heading: heading
|
|
});
|
|
})
|
|
|
|
return buyerLocations;
|
|
}
|
|
|
|
function addNarcosDrug(drugName = "", minQuantity = 1, maxQuantity = 2, minPrice = 500, maxPrice = 1000) {
|
|
let drugDiv = $(`
|
|
<div class="d-flex gap-3 justify-content-center align-items-center mt-2 mb-3 drug">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control drug-name" placeholder="Name" required value=${drugName}>
|
|
<label>Drug 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_item") }"><i class="bi bi-list-ul"></i></button>
|
|
|
|
<div class="form-floating col-2">
|
|
<input type="number" class="form-control drug-min-quantity" placeholder="Min quantity" required value=${minQuantity}>
|
|
<label>Min quantity</label>
|
|
</div>
|
|
|
|
<div class="form-floating col-2">
|
|
<input type="number" class="form-control drug-max-quantity" placeholder="Max quantity" required value=${maxQuantity}>
|
|
<label>Max quantity</label>
|
|
</div>
|
|
|
|
<div class="form-floating col-2">
|
|
<input type="number" class="form-control drug-min-price" placeholder="Max quantity" required value=${minPrice}>
|
|
<label>${ getLocalizedText("menu:min_price") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating col-2">
|
|
<input type="number" class="form-control drug-max-price" placeholder="Max quantity" required value=${maxPrice}>
|
|
<label>${ getLocalizedText("menu:max_price") }</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn-close btn-close-white remove-btn ms-2"></button>
|
|
</div>
|
|
`)
|
|
|
|
drugDiv.find(".choose-item-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
drugDiv.find(".drug-name").val(itemName);
|
|
}).tooltip();
|
|
|
|
drugDiv.find(".remove-btn").click(function() {
|
|
drugDiv.remove();
|
|
});
|
|
|
|
$("#narcos-selling-acceptable-drugs").append(drugDiv);
|
|
}
|
|
|
|
$("#narcos-add-new-drug").click(function() {
|
|
addNarcosDrug();
|
|
})
|
|
|
|
function getNarcosDrugs() {
|
|
let drugs = [];
|
|
|
|
$("#narcos-selling-acceptable-drugs").find(".drug").each(function() {
|
|
let drug = {
|
|
name: $(this).find(".drug-name").val(),
|
|
minPrice: parseInt( $(this).find(".drug-min-price").val() ),
|
|
maxPrice: parseInt( $(this).find(".drug-max-price").val() ),
|
|
minQuantity: parseInt( $(this).find(".drug-min-quantity").val() ),
|
|
maxQuantity: parseInt( $(this).find(".drug-max-quantity").val() )
|
|
}
|
|
|
|
drugs.push(drug);
|
|
})
|
|
|
|
return drugs;
|
|
}
|
|
|
|
// Pushers
|
|
function togglePushersSelling() {
|
|
let enabled = $("#enable-pushers-selling").prop("checked");
|
|
|
|
$("#selling-pushers").find("input, button").not("#enable-pushers-selling").prop("disabled", !enabled);
|
|
}
|
|
$("#enable-pushers-selling").change(togglePushersSelling);
|
|
|
|
function addPusherDrug(pusherDiv, drugData = {}) {
|
|
let drugDiv = $(`
|
|
<div class="d-flex justify-content-center align-items-center gap-3 mb-6 pusher-drug">
|
|
<div class="form-floating col-3">
|
|
<input type="text" class="form-control drug-name" placeholder="Name" required value=${drugData.name || ""}>
|
|
<label>${ getLocalizedText("menu:item_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_item") }"><i class="bi bi-list-ul"></i></button>
|
|
|
|
<div class="form-floating col-auto">
|
|
<input type="number" class="form-control drug-min-price" placeholder="Min price" required value=${drugData.minPrice || 0}>
|
|
<label>${ getLocalizedText("menu:min_price") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating col-auto">
|
|
<input type="number" class="form-control drug-max-price" placeholder="Max price" required value=${drugData.maxPrice || 1000}>
|
|
<label>${ getLocalizedText("menu:max_price") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating col-auto">
|
|
<input type="number" class="form-control drug-max-quantity" placeholder="Max quantity" required value=${drugData.maxQuantity || 100} data-bs-toggle="tooltip" data-bs-placement="top" title="${ getLocalizedText("menu:pushers:max_quantity_description") }">
|
|
<label>${ getLocalizedText("menu:maximum_quantity") }</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn-close btn-close-white remove-result-item-btn ms-4"></button>
|
|
</div>
|
|
`);
|
|
|
|
drugDiv.find(".btn-close").click(function() {
|
|
drugDiv.remove();
|
|
});
|
|
|
|
// Initialize tooltip so player knows what the field is for
|
|
drugDiv.find(".drug-max-quantity").tooltip();
|
|
drugDiv.find(".choose-item-btn").tooltip();
|
|
|
|
drugDiv.find(".choose-item-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
drugDiv.find(".drug-name").val(itemName);
|
|
})
|
|
|
|
pusherDiv.find(".pusher-drugs-list").append(drugDiv);
|
|
}
|
|
|
|
function addPusher(pusherData = {}) {
|
|
let pusherDiv = $(`
|
|
<div class="pusher mb-4">
|
|
<div class="mb-6 pusher-spawn-location">
|
|
<p class="text-center fs-5">${ getLocalizedText("menu:pushers:location") }</p>
|
|
|
|
<div class="row g-2 row-cols-4 text-body my-2">
|
|
<div class="form-floating">
|
|
<input type="number" step="0.01" class="form-control max-two-decimals coord-x" placeholder="X" required>
|
|
<label>${ getLocalizedText("menu:x") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating">
|
|
<input type="number" step="0.01" class="form-control max-two-decimals coord-y" placeholder="Y" required>
|
|
<label>${ getLocalizedText("menu:y") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating">
|
|
<input type="number" step="0.01" class="form-control max-two-decimals coord-z" placeholder="Z" required>
|
|
<label>${ getLocalizedText("menu:z") }</label>
|
|
</div>
|
|
|
|
<div class="form-floating">
|
|
<input type="number" step="0.01" class="form-control max-two-decimals heading" placeholder="Heading" required>
|
|
<label>${ getLocalizedText("menu:heading") }</label>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-secondary mx-1 current-coords-btn">${ getLocalizedText("menu:current_coords_heading") }</button>
|
|
</div>
|
|
|
|
<p class="text-center fs-3 mb-1">${ getLocalizedText("menu:pushers:work_time") }</p>
|
|
<p class="text-center fs-5">${ getLocalizedText("menu:pushers:work_time:subtitle") } - <span class="text-warning server-clock"></span></p>
|
|
|
|
<div class="d-flex justify-content-center gap-3 mb-6">
|
|
<div class="form-check form-check-inline my-auto">
|
|
<input class="form-check-input is-always-active" type="checkbox" checked>
|
|
<label class="form-check-label">${getLocalizedText("menu:is_always_active")}</label>
|
|
</div>
|
|
|
|
<div class="form-floating col">
|
|
<input type="number" min="0" max="23" value="0" class="form-control start-hour" placeholder="Start hour" disabled required>
|
|
<label>${ getLocalizedText("menu:start_hour") }</label>
|
|
<p class="invalid-feedback">${getLocalizedText("menu:invalid_hours")}</p>
|
|
</div>
|
|
|
|
<div class="form-floating col">
|
|
<input type="number" min="0" max="23" value="23" class="form-control finish-hour" placeholder="Finish hour" disabled required>
|
|
<label>${ getLocalizedText("menu:finish_hour") }</label>
|
|
<p class="invalid-feedback">${getLocalizedText("menu:invalid_hours")}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="my-4">
|
|
<p class="text-center fs-3">${ getLocalizedText("menu:pushers:acceptable_drugs") }</p>
|
|
|
|
<div class="pusher-drugs-list">
|
|
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mt-3">
|
|
<div class="form-check form-switch fs-4 my-auto">
|
|
<input class="form-check-input pusher-one-drug-only" type="checkbox" role="switch">
|
|
<label class="form-check-label">${ getLocalizedText("menu:pushers:accepts_only_one_random_drug") }</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-secondary add-drug">${ getLocalizedText("menu:pushers:add_drug") }</button>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-danger mx-1 remove-btn">${ getLocalizedText("menu:remove") }</button>
|
|
</div>
|
|
|
|
<hr>
|
|
`);
|
|
|
|
pusherDiv.find(".is-always-active").change(function() {
|
|
let isAlwaysActive = $(this).prop("checked");
|
|
pusherDiv.find(".start-hour").prop("disabled", isAlwaysActive);
|
|
pusherDiv.find(".finish-hour").prop("disabled", isAlwaysActive);
|
|
|
|
if(isAlwaysActive) {
|
|
pusherDiv.find(".start-hour").val(0);
|
|
pusherDiv.find(".finish-hour").val(23);
|
|
}
|
|
});
|
|
|
|
pusherDiv.find(".remove-btn").click(function() {
|
|
pusherDiv.remove();
|
|
});
|
|
|
|
pusherDiv.find(".current-coords-btn").click(async function(event) {
|
|
if(event.shiftKey) {
|
|
const coordsInUI = {
|
|
x: parseFloat( pusherDiv.find(".coord-x").val() ),
|
|
y: parseFloat( pusherDiv.find(".coord-y").val() ),
|
|
z: parseFloat( pusherDiv.find(".coord-z").val() )
|
|
}
|
|
|
|
teleportToCoords(coordsInUI, parseFloat(pusherDiv.find(".heading").val()));
|
|
return;
|
|
}
|
|
|
|
const data = await getCurrentCoordsAndHeading();
|
|
pusherDiv.find(".coord-x").val(data.coords.x);
|
|
pusherDiv.find(".coord-y").val(data.coords.y);
|
|
pusherDiv.find(".coord-z").val(data.coords.z);
|
|
pusherDiv.find(".heading").val(data.heading);
|
|
});
|
|
|
|
pusherDiv.find(".add-drug").click(function() {
|
|
addPusherDrug(pusherDiv);
|
|
});
|
|
|
|
if (pusherData.coords) {
|
|
pusherDiv.find(".coord-x").val(pusherData.coords.x);
|
|
pusherDiv.find(".coord-y").val(pusherData.coords.y);
|
|
pusherDiv.find(".coord-z").val(pusherData.coords.z);
|
|
}
|
|
|
|
if(pusherData.heading) {
|
|
pusherDiv.find(".heading").val(pusherData.heading);
|
|
}
|
|
|
|
if (pusherData.workTime) {
|
|
pusherDiv.find(".start-hour").val(pusherData.workTime.startHour);
|
|
pusherDiv.find(".finish-hour").val(pusherData.workTime.finishHour);
|
|
|
|
const isAlwaysActive = pusherData.workTime.startHour == 0 && pusherData.workTime.finishHour == 23;
|
|
pusherDiv.find(".is-always-active").prop("checked", isAlwaysActive).change();
|
|
}
|
|
|
|
if(pusherData.drugsNeeded) {
|
|
pusherData.drugsNeeded.forEach(drug => {
|
|
addPusherDrug(pusherDiv, drug);
|
|
});
|
|
}
|
|
|
|
$.post(`https://${resName}/getServerClock`, {}, function(time) {
|
|
pusherDiv.find(".server-clock").text(time);
|
|
});
|
|
|
|
pusherDiv.find(".pusher-one-drug-only").prop("checked", pusherData.acceptsOnlyOneRandomDrug);
|
|
|
|
$("#pushers-list").append(pusherDiv);
|
|
|
|
return pusherDiv;
|
|
}
|
|
|
|
$("#add-pusher-btn").click(function() {
|
|
let pusherDiv = addPusher();
|
|
|
|
addPusherDrug(pusherDiv);
|
|
});
|
|
|
|
function getPusherDrugs(pusherDiv) {
|
|
let drugs = [];
|
|
|
|
pusherDiv.find(".pusher-drug").each(function() {
|
|
let drug = {
|
|
name: $(this).find(".drug-name").val(),
|
|
minPrice: parseInt( $(this).find(".drug-min-price").val() ),
|
|
maxPrice: parseInt( $(this).find(".drug-max-price").val() ),
|
|
maxQuantity: parseInt( $(this).find(".drug-max-quantity").val() ),
|
|
};
|
|
|
|
drugs.push(drug);
|
|
});
|
|
|
|
return drugs;
|
|
}
|
|
|
|
function getPushers() {
|
|
let pushers = {};
|
|
let pushersCount = 1;
|
|
|
|
$("#pushers-list").find(".pusher").each(function() {
|
|
pushers[pushersCount] = {
|
|
coords: {
|
|
x: parseFloat( $(this).find(".coord-x").val() ),
|
|
y: parseFloat( $(this).find(".coord-y").val() ),
|
|
z: parseFloat( $(this).find(".coord-z").val() )
|
|
},
|
|
|
|
heading: parseFloat( $(this).find(".heading").val() ),
|
|
|
|
workTime: {
|
|
startHour: parseInt( $(this).find(".start-hour").val() ),
|
|
finishHour: parseInt( $(this).find(".finish-hour").val() )
|
|
},
|
|
|
|
drugsNeeded: getPusherDrugs($(this)),
|
|
|
|
acceptsOnlyOneRandomDrug: $(this).find(".pusher-one-drug-only").prop("checked"),
|
|
};
|
|
|
|
pushersCount++;
|
|
})
|
|
|
|
return pushers;
|
|
}
|
|
|
|
$("#enable-explosion-on-error").change(function() {
|
|
let enabled = $(this).prop("checked");
|
|
|
|
$("#seconds-before-explosion").prop("disabled", !enabled);
|
|
})
|
|
|
|
// Load settings
|
|
function loadSettings(fullConfig) {
|
|
loadModulesSettings(fullConfig.modules);
|
|
setSelectiveTargetingSettings(fullConfig.selectiveTargeting);
|
|
|
|
// Plane selling
|
|
$("#enable-plane-selling").prop("checked", fullConfig.enableAirplaneSell);
|
|
$("#enable-plane-selling").change();
|
|
|
|
$("#minimum-plane-height").val(fullConfig.heightToSell);
|
|
|
|
$("#time-to-sell-in-plane").val(fullConfig.timeToSellInPlane);
|
|
|
|
$("#enable-plane-police-alert").prop("checked", fullConfig.alarmPoliceInPlane);
|
|
|
|
$("#plane-use-the-whole-ocean").prop("checked", fullConfig.sellUseWholeOcean.plane);
|
|
$("#plane-use-the-whole-ocean").change();
|
|
|
|
$("#plane-selling-coords-x").val(fullConfig.sellArea.plane.coords.x);
|
|
$("#plane-selling-coords-y").val(fullConfig.sellArea.plane.coords.y);
|
|
$("#plane-selling-coords-z").val(fullConfig.sellArea.plane.coords.z);
|
|
$("#plane-selling-coords-radius").val(fullConfig.sellArea.plane.radius);
|
|
|
|
fillAcceptableDrugsForDiv("#plane-selling-acceptable-drugs", fullConfig.planeAcceptableDrugs);
|
|
|
|
// Rewards
|
|
setRewardDivData("#plane-selling-reward-div", fullConfig.rewards.plane);
|
|
setRewardDivData("#boat-selling-reward-div", fullConfig.rewards.boat);
|
|
setRewardDivData("#npc-selling-reward-div", fullConfig.rewards.npc);
|
|
setRewardDivData("#narcos-selling-reward-div", fullConfig.rewards.narcos);
|
|
setRewardDivData("#pushers-selling-reward-div", fullConfig.rewards.pushers);
|
|
|
|
$("#plane-minimum-police").val(fullConfig.planeSellingMinimumPolice);
|
|
|
|
// Boat selling
|
|
$("#enable-boat-selling").prop("checked", fullConfig.enableBoatSell);
|
|
$("#enable-boat-selling").change();
|
|
|
|
$("#time-to-sell-in-boat").val(fullConfig.timeToSellInBoat);
|
|
|
|
$("#enable-boat-police-alert").prop("checked", fullConfig.alarmPoliceInBoat);
|
|
|
|
$("#boat-use-the-whole-ocean").prop("checked", fullConfig.sellUseWholeOcean.boat);
|
|
$("#boat-use-the-whole-ocean").change();
|
|
$("#boat-selling-coords-x").val(fullConfig.sellArea.boat.coords.x);
|
|
$("#boat-selling-coords-y").val(fullConfig.sellArea.boat.coords.y);
|
|
$("#boat-selling-coords-z").val(fullConfig.sellArea.boat.coords.z);
|
|
$("#boat-selling-coords-radius").val(fullConfig.sellArea.boat.radius);
|
|
|
|
fillAcceptableDrugsForDiv("#boat-selling-acceptable-drugs", fullConfig.boatAcceptableDrugs);
|
|
|
|
$("#boat-minimum-police").val(fullConfig.boatSellingMinimumPolice);
|
|
|
|
// NPC Selling
|
|
$("#enable-npc-selling").prop("checked", fullConfig.enableNPCSell);
|
|
$("#enable-npc-selling").change();
|
|
|
|
$("#npc-selling-min-quantity").val(fullConfig.minNPCSellQuantity);
|
|
$("#npc-selling-max-quantity").val(fullConfig.maxNPCSellQuantity);
|
|
|
|
$("#npc-accept-chances").val(fullConfig.sellToNPCChancesToAccept);
|
|
|
|
$("#npc-max-drug-quantity").val(fullConfig.maxNPCsSellableDrugQuantity);
|
|
|
|
fillAcceptableDrugsForDiv("#npc-selling-acceptable-drugs", fullConfig.npcAcceptableDrugs);
|
|
|
|
$("#npc-minimum-police").val(fullConfig.npcSellingMinimumPolice);
|
|
|
|
$("#npc-seconds-to-sell").val(fullConfig.npcSecondsToSell);
|
|
|
|
$("#npc-alert-police-chances").val(fullConfig.npcAlertPoliceChances);
|
|
|
|
$("#npc-can-rob-player").prop("checked", fullConfig.canNPCRobPlayer);
|
|
$("#npc-can-attack-player").prop("checked", fullConfig.canNPCAttackPlayer);
|
|
|
|
// NPC Selling command
|
|
$("#npc-selling-command-is-enabled").prop("checked", fullConfig.npcSellingCommand.enabled).change();
|
|
$("#npc-selling-command").val(fullConfig.npcSellingCommand.command);
|
|
$("#npc-selling-command-has-to-spawn-npc").prop("checked", fullConfig.npcSellingCommand.hasToSpawnPed);
|
|
|
|
// Narcos selling
|
|
$("#enable-narcos-selling").prop("checked", fullConfig.enableNarcosSelling);
|
|
$("#enable-narcos-selling").change();
|
|
|
|
$("#narcos-model").val(fullConfig.narcosModel),
|
|
|
|
$("#enable-narcos-blip").prop("checked", fullConfig.showNarcosBlip),
|
|
$("#enable-narcos-blip").change();
|
|
|
|
$("#narcos-selling-blip-name").val(fullConfig.narcosBlip.name);
|
|
$("#narcos-selling-blip-color").val(fullConfig.narcosBlip.color);
|
|
$("#narcos-selling-blip-scale").val(fullConfig.narcosBlip.scale);
|
|
$("#narcos-selling-blip-sprite").val(fullConfig.narcosBlip.sprite);
|
|
|
|
$("#narcos-location-change").val(fullConfig.narcosLocationChangeTime);
|
|
$("#narcos-police-alert-chances").val(fullConfig.narcosCallPoliceChances);
|
|
$("#narcos-accepts-only-one-buyer-per-location").prop("checked", fullConfig.narcosAcceptsOnlyOneBuyerPerLocation);
|
|
|
|
$("#narcos-spawn-locations").empty();
|
|
if(fullConfig.narcosBuyerLocations) {
|
|
fullConfig.narcosBuyerLocations.forEach(location => {
|
|
addNarcosSpawnLocation(location.coords, location.heading);
|
|
});
|
|
}
|
|
|
|
$("#narcos-selling-acceptable-drugs").empty();
|
|
if(fullConfig.narcosNeededDrugs) {
|
|
fullConfig.narcosNeededDrugs.forEach(drug => {
|
|
addNarcosDrug(drug.name, drug.minQuantity, drug.maxQuantity, drug.minPrice, drug.maxPrice);
|
|
});
|
|
}
|
|
|
|
$("#narcos-minimum-police").val(fullConfig.narcosSellingMinimumPolice);
|
|
|
|
// Pushers
|
|
$("#enable-pushers-selling").prop("checked", fullConfig.arePushersEnabled);
|
|
$("#enable-pushers-selling").change();
|
|
|
|
$("#pushers-model").val(fullConfig.pusherModel),
|
|
|
|
$("#enable-pushers-blip").prop("checked", fullConfig.showPushersBlips);
|
|
$("#pushers-selling-blip-name").val(fullConfig.pusherBlip.name);
|
|
$("#pushers-selling-blip-color").val(fullConfig.pusherBlip.color);
|
|
$("#pushers-selling-blip-scale").val(fullConfig.pusherBlip.scale);
|
|
$("#pushers-selling-blip-sprite").val(fullConfig.pusherBlip.sprite);
|
|
|
|
$("#pushers-alert-police-chances").val(fullConfig.pushersCallPoliceChances);
|
|
|
|
$("#pushers-list").empty();
|
|
if(fullConfig.pushers) {
|
|
for(const[pusherId, pusherData] of Object.entries(fullConfig.pushers)) {
|
|
addPusher(pusherData);
|
|
}
|
|
}
|
|
|
|
$("#pushers-minimum-police").val(fullConfig.pushersSellingMinimumPolice);
|
|
|
|
// Pocket craftings
|
|
$("#pocket-craftings-list").empty();
|
|
if(fullConfig.pocketCraftings) {
|
|
for(const[itemName, itemData] of Object.entries(fullConfig.pocketCraftings)) {
|
|
addPocketCrafting(itemName, itemData);
|
|
}
|
|
}
|
|
|
|
// Effects
|
|
$("#drugs-effects").empty();
|
|
if(fullConfig.drugsEffects) {
|
|
for(const[itemName, effectData] of Object.entries(fullConfig.drugsEffects)) {
|
|
addItemEffect(itemName, effectData);
|
|
}
|
|
}
|
|
|
|
// Settings
|
|
setTomSelectValue("#settings_locale", fullConfig.locale)
|
|
setTomSelectValue("#settings_menuPosition", fullConfig.menuPosition)
|
|
setTomSelectValue("#settings-targeting-script", fullConfig.targetingScript)
|
|
|
|
$("#enable-fire-on-error").prop("checked", fullConfig.enableFireOnError);
|
|
$("#enable-explosion-on-error").prop("checked", fullConfig.enableExplosionOnError).change();
|
|
$("#seconds-before-explosion").val(fullConfig.secondsBeforeExplosion);
|
|
|
|
$("#enable-discord-logs").prop("checked", fullConfig.areDiscordLogsActive).change()
|
|
|
|
$("#main-discord-webhook").val(fullConfig.mainDiscordWebhook);
|
|
|
|
if(fullConfig.specificWebhooks) {
|
|
for(const[webhookType, webhookUrl] of Object.entries(fullConfig.specificWebhooks)) {
|
|
$("#discord-specific-webhooks").find(`[data-webhook-type="${webhookType}"]`).val(webhookUrl);
|
|
}
|
|
}
|
|
|
|
// Key to sell to NPCs
|
|
$("#key-to-sell-to-npcs").val(fullConfig.keyToSellToNPCs);
|
|
|
|
// Minimum police
|
|
$("#harvestable-items-minimum-police").val(fullConfig.harvestingItemsMinimumPolice);
|
|
$("#laboratory-minimum-police").val(fullConfig.useLaboratoryMinimumPolice);
|
|
$("#fields-minimum-police").val(fullConfig.harvestFieldMinimumPolice);
|
|
|
|
// Automatic farm
|
|
$("#allow-afk-farming-for-harvest").prop("checked", fullConfig.allowAfkFarmingForHarvest);
|
|
$("#allow-afk-farming-for-laboratories").prop("checked", fullConfig.allowAfkFarmingForLaboratories);
|
|
|
|
// Price reduction
|
|
$("#minimum-cops-for-base-price").val(fullConfig.priceReduction.minimumPolice);
|
|
$("#price-reduction-percentage").val(fullConfig.priceReduction.reductionPercentage);
|
|
$("#price-reduction-interval").val(fullConfig.priceReduction.reductionInterval);
|
|
|
|
// Auctions
|
|
$("#auction-check-interval").val(fullConfig.auctions.nextAuctionEachMinutes);
|
|
}
|
|
|
|
// Settings
|
|
function getSpecificWebhooks() {
|
|
let webhooks = {};
|
|
|
|
$("#discord-specific-webhooks").find(".webhook").each(function() {
|
|
let webhookType = $(this).data("webhookType");
|
|
let webhook = $(this).val();
|
|
|
|
if(webhook) {
|
|
webhooks[webhookType] = webhook;
|
|
}
|
|
})
|
|
|
|
return webhooks;
|
|
}
|
|
|
|
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 = {
|
|
secondsBeforeExplosion: parseInt( $("#seconds-before-explosion").val() ),
|
|
menuPosition: $("#settings_menuPosition").val(),
|
|
|
|
// Key to sell to NPCs
|
|
keyToSellToNPCs: parseInt( $("#key-to-sell-to-npcs").val() ),
|
|
|
|
// Targeting
|
|
targetingScript: $("#settings-targeting-script").val(),
|
|
|
|
|
|
selectiveTargeting: getSelectiveTargetingSettings()
|
|
}
|
|
|
|
let sharedSettings = {
|
|
locale: $("#settings_locale").val(),
|
|
|
|
// NPC selling command
|
|
npcSellingCommand: {
|
|
enabled: $("#npc-selling-command-is-enabled").prop("checked"),
|
|
command: $("#npc-selling-command").val(),
|
|
hasToSpawnPed: $("#npc-selling-command-has-to-spawn-npc").prop("checked")
|
|
},
|
|
|
|
modules: getModulesSettings(),
|
|
}
|
|
|
|
let serverSettings = {
|
|
enableFireOnError: $("#enable-fire-on-error").prop("checked"),
|
|
enableExplosionOnError: $("#enable-explosion-on-error").prop("checked"),
|
|
|
|
areDiscordLogsActive: $("#enable-discord-logs").prop("checked"),
|
|
mainDiscordWebhook: $("#main-discord-webhook").val(),
|
|
|
|
specificWebhooks: getSpecificWebhooks(),
|
|
|
|
harvestingItemsMinimumPolice: parseInt( $("#harvestable-items-minimum-police").val() ),
|
|
useLaboratoryMinimumPolice: parseInt( $("#laboratory-minimum-police").val() ),
|
|
harvestFieldMinimumPolice: parseInt( $("#fields-minimum-police").val() ),
|
|
|
|
allowAfkFarmingForHarvest: $("#allow-afk-farming-for-harvest").prop("checked"),
|
|
allowAfkFarmingForLaboratories: $("#allow-afk-farming-for-laboratories").prop("checked"),
|
|
|
|
priceReduction: {
|
|
minimumPolice: parseInt( $("#minimum-cops-for-base-price").val()),
|
|
reductionPercentage: parseInt( $("#price-reduction-percentage").val()),
|
|
reductionInterval: parseInt( $("#price-reduction-interval").val())
|
|
},
|
|
|
|
auctions: {
|
|
nextAuctionEachMinutes: parseInt( $("#auction-check-interval").val() ),
|
|
}
|
|
}
|
|
|
|
const response = await $.post(`https://${resName}/saveSettings`, JSON.stringify({
|
|
clientSettings: clientSettings,
|
|
serverSettings: serverSettings,
|
|
sharedSettings: sharedSettings
|
|
}));
|
|
showServerResponse(response);
|
|
|
|
refreshTranslations(sharedSettings.locale);
|
|
})
|
|
|
|
$("#enable-discord-logs").change(function() {
|
|
let enabled = $(this).prop("checked");
|
|
|
|
$("#main-discord-webhook").prop("disabled", !enabled);
|
|
$("#main-discord-webhook").prop("required", enabled);
|
|
|
|
$("#discord-specific-webhooks").find(".webhook").each(function() {
|
|
$(this).prop("disabled", !enabled);
|
|
});
|
|
})
|
|
|
|
// Restore to default config
|
|
$(".restore-to-default-config").click(async function() {
|
|
if(!await confirmDeletion( getLocalizedText("menu:are_you_sure_to_restore_settings") )) return;
|
|
|
|
const defaultConfig = await $.post(`https://${resName}/getDefaultConfiguration`);
|
|
loadSettings(defaultConfig)
|
|
})
|
|
|
|
// Drugs effects
|
|
async function getItemLabel(itemName) {
|
|
return new Promise(resolve => {
|
|
$.post(`https://${resName}/getItemLabel`, JSON.stringify({itemName: itemName}), function(itemLabel) {
|
|
resolve(itemLabel);
|
|
});
|
|
})
|
|
}
|
|
|
|
async function addItemEffect(itemName, itemData={}, isNew = false) {
|
|
let itemLabel = await getItemLabel(itemName);
|
|
|
|
let itemEffectDiv = $(`
|
|
<div class="drug-effect mb-4">
|
|
<p class="text-center fs-2 fw-bold title">${itemLabel} <span class="fs-5 fw-lighter fst-italic">(${itemName})</span></p>
|
|
|
|
<div class="effects mt-3">
|
|
<p class="text-start fs-3 fw-bold mb-2">${ getLocalizedText("menu:effects:taking_method") }</p>
|
|
|
|
<div class="d-flex mb-4">
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input" type="radio" name="drug-assume-type-${itemName}" value="pill" required checked>
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:pill") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input" type="radio" name="drug-assume-type-${itemName}" value="drink">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:drink") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input" type="radio" name="drug-assume-type-${itemName}" value="drink_soda">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:drink_soda") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input" type="radio" name="drug-assume-type-${itemName}" value="smoke">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:smoke") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input" type="radio" name="drug-assume-type-${itemName}" value="snort">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:snort") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input" type="radio" name="drug-assume-type-${itemName}" value="needle">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:needle") }
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="text-start fs-3 fw-bold mb-2">${ getLocalizedText("menu:effects:drunk_effects") }</p>
|
|
|
|
<div class="d-flex mb-4">
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input stackable-effect" type="checkbox" value="visual_shaking">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:visual_shaking") }</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input stackable-effect" type="checkbox" value="drunk_walk">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:drunk_walk") }</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input stackable-effect" type="checkbox" value="fall">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:fall") }</label>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="text-start fs-3 fw-bold mb-2">${ getLocalizedText("menu:effects:visual_color_effects") }</p>
|
|
|
|
<div class="d-flex row-cols-3 flex-wrap mb-4 mb-4">
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input" type="radio" name="camera-color-effects-${itemName}" value="none" required checked>
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:none") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input" type="radio" name="camera-color-effects-${itemName}" value="pink_visual">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:pink_visual") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input" type="radio" name="camera-color-effects-${itemName}" value="green_visual">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:green_visual") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input" type="radio" name="camera-color-effects-${itemName}" value="confused_visual">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:confused_visual") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input" type="radio" name="camera-color-effects-${itemName}" value="yellow_visual">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:yellow_visual") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input" type="radio" name="camera-color-effects-${itemName}" value="blurred_visual">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:blurred_visual") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input" type="radio" name="camera-color-effects-${itemName}" value="red_visual">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:red_visual") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input" type="radio" name="camera-color-effects-${itemName}" value="foggy_visual">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:foggy_visual") }
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input" type="radio" name="camera-color-effects-${itemName}" value="blue_visual">
|
|
<label class="form-check-label">
|
|
${ getLocalizedText("menu:effects:blue_visual") }
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="text-start fs-3 fw-bold mb-2">${ getLocalizedText("menu:effects:perks") }</p>
|
|
|
|
<div class="d-flex row-cols-3 flex-wrap mb-4 mb-4">
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input stackable-effect" type="checkbox" value="armor50">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:50_percent_armor") }</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input stackable-effect" type="checkbox" value="armor100">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:100_percent_armor") }</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input stackable-effect" type="checkbox" value="health50">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:50_percent_health") }</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input stackable-effect" type="checkbox" value="health100">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:100_percent_health") }</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input stackable-effect" type="checkbox" value="sprint_faster">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:faster_sprint") }</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input stackable-effect" type="checkbox" value="swim_faster">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:faster_swim") }</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input stackable-effect" type="checkbox" value="infinite_stamina">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:infinite_stamina") }</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4 m-0">
|
|
<input class="form-check-input stackable-effect" type="checkbox" value="remove_old_effects">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:remove_old_effects") }</label>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="text-start fs-3 fw-bold mb-0">${ getLocalizedText("menu:effects:cumulative_effect") }</p>
|
|
|
|
<div class="d-flex row-cols-3 flex-wrap">
|
|
<div class="d-flex align-items-center gap-3 my-1 cumulative-effect" data-cumulative-name="armor">
|
|
<div class="form-check form-check-inline fs-4 me-3 my-auto">
|
|
<input class="form-check-input cumulative-effect-checkbox" type="checkbox" value="">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:armor") }</label>
|
|
</div>
|
|
<div class="form-check my-auto">
|
|
<input class="form-check-input" type="radio" name="drug-cumulative-armor-${itemName}" value="increase">
|
|
<label class="form-check-label">${getLocalizedText("menu:effects:increase")}</label>
|
|
</div>
|
|
<div class="form-check my-auto">
|
|
<input class="form-check-input" type="radio" name="drug-cumulative-armor-${itemName}" value="decrease">
|
|
<label class="form-check-label">${getLocalizedText("menu:effects:decrease")}</label>
|
|
</div>
|
|
<div class="form-floating col-3">
|
|
<input type="number" min=1 class="form-control cumulative-effect-amount" placeholder="..." value="20">
|
|
<label>${ getLocalizedText("menu:amount") }</label>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-3 my-1 cumulative-effect" data-cumulative-name="health">
|
|
<div class="form-check form-check-inline fs-4 me-3 my-auto">
|
|
<input class="form-check-input cumulative-effect-checkbox" type="checkbox" value="">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:health") }</label>
|
|
</div>
|
|
<div class="form-check my-auto">
|
|
<input class="form-check-input" type="radio" name="drug-cumulative-health-${itemName}" value="increase">
|
|
<label class="form-check-label">${getLocalizedText("menu:effects:increase")}</label>
|
|
</div>
|
|
<div class="form-check my-auto">
|
|
<input class="form-check-input" type="radio" name="drug-cumulative-health-${itemName}" value="decrease">
|
|
<label class="form-check-label">${getLocalizedText("menu:effects:decrease")}</label>
|
|
</div>
|
|
<div class="form-floating col-3">
|
|
<input type="number" min=1 class="form-control cumulative-effect-amount" placeholder="..." value="20">
|
|
<label>${ getLocalizedText("menu:amount") }</label>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-3 my-1 cumulative-effect" data-cumulative-name="stress">
|
|
<div class="form-check form-check-inline fs-4 me-3 my-auto">
|
|
<input class="form-check-input cumulative-effect-checkbox" type="checkbox" value="">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:stress") }</label>
|
|
</div>
|
|
<div class="form-check my-auto">
|
|
<input class="form-check-input" type="radio" name="drug-cumulative-stress-${itemName}" value="increase">
|
|
<label class="form-check-label">${getLocalizedText("menu:effects:increase")}</label>
|
|
</div>
|
|
<div class="form-check my-auto">
|
|
<input class="form-check-input" type="radio" name="drug-cumulative-stress-${itemName}" value="decrease">
|
|
<label class="form-check-label">${getLocalizedText("menu:effects:decrease")}</label>
|
|
</div>
|
|
<div class="form-floating col-3">
|
|
<input type="number" min=1 class="form-control cumulative-effect-amount" placeholder="..." value="20">
|
|
<label>${ getLocalizedText("menu:amount") }</label>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-3 my-1 cumulative-effect" data-cumulative-name="hunger">
|
|
<div class="form-check form-check-inline fs-4 me-3 my-auto">
|
|
<input class="form-check-input cumulative-effect-checkbox" type="checkbox" value="">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:hunger") }</label>
|
|
</div>
|
|
<div class="form-check my-auto">
|
|
<input class="form-check-input" type="radio" name="drug-cumulative-hunger-${itemName}" value="increase">
|
|
<label class="form-check-label">${getLocalizedText("menu:effects:increase")}</label>
|
|
</div>
|
|
<div class="form-check my-auto">
|
|
<input class="form-check-input" type="radio" name="drug-cumulative-hunger-${itemName}" value="decrease">
|
|
<label class="form-check-label">${getLocalizedText("menu:effects:decrease")}</label>
|
|
</div>
|
|
<div class="form-floating col-3">
|
|
<input type="number" min=1 class="form-control cumulative-effect-amount" placeholder="..." value="20">
|
|
<label>${ getLocalizedText("menu:amount") }</label>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-3 my-1 cumulative-effect" data-cumulative-name="thirst">
|
|
<div class="form-check form-check-inline fs-4 me-3 my-auto">
|
|
<input class="form-check-input cumulative-effect-checkbox" type="checkbox" value="">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:thirst") }</label>
|
|
</div>
|
|
<div class="form-check my-auto">
|
|
<input class="form-check-input" type="radio" name="drug-cumulative-thirst-${itemName}" value="increase">
|
|
<label class="form-check-label">${getLocalizedText("menu:effects:increase")}</label>
|
|
</div>
|
|
<div class="form-check my-auto">
|
|
<input class="form-check-input" type="radio" name="drug-cumulative-thirst-${itemName}" value="decrease">
|
|
<label class="form-check-label">${getLocalizedText("menu:effects:decrease")}</label>
|
|
</div>
|
|
<div class="form-floating col-3">
|
|
<input type="number" min=1 class="form-control cumulative-effect-amount" placeholder="..." value="20">
|
|
<label>${ getLocalizedText("menu:amount") }</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="text-start fs-3 fw-bold mb-2">${ getLocalizedText("menu:effects:special_effect") }</p>
|
|
|
|
<div class="d-flex mb-4">
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input" type="radio" name="drug-special-effect-${itemName}" value="none" checked>
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:none") }</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input" type="radio" name="drug-special-effect-${itemName}" value="vehicle_stalker">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:vehicle_stalker") }</label>
|
|
</div>
|
|
|
|
<div class="form-check form-check-inline fs-4">
|
|
<input class="form-check-input" type="radio" name="drug-special-effect-${itemName}" value="ghost">
|
|
<label class="form-check-label">${ getLocalizedText("menu:effects:ghost") }</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-floating mb-3 text-body">
|
|
<input type="number" min=1 class="form-control effect-duration" placeholder="Effects duration" required value="120">
|
|
<label>${ getLocalizedText("menu:effects:duration") }</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-inline-block col-12">
|
|
<button type="button" class="btn btn-danger float-end remove-effect-btn">${ getLocalizedText("menu:effects:remove_this_effect") }</button>
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
`);
|
|
|
|
if(itemData.takingMethod) {
|
|
itemEffectDiv.find(`input[name=drug-assume-type-${itemName}][value=${itemData.takingMethod}]`).prop("checked", true);
|
|
}
|
|
|
|
if(itemData.effectsDuration) {
|
|
itemEffectDiv.find(".effect-duration").val(itemData.effectsDuration);
|
|
}
|
|
|
|
if(itemData.effects) {
|
|
for(let effect of itemData.effects) {
|
|
itemEffectDiv.find(`input[value=${effect}]`).prop("checked", true);
|
|
}
|
|
}
|
|
|
|
if(itemData.cumulativeEffects) {
|
|
for(let cumulativeEffect of itemData.cumulativeEffects) {
|
|
itemEffectDiv.find(`.cumulative-effect[data-cumulative-name=${cumulativeEffect.type}]`).find(".cumulative-effect-checkbox").prop("checked", true);
|
|
itemEffectDiv.find(`input[name=drug-cumulative-${cumulativeEffect.type}-${itemName}][value=${cumulativeEffect.action}]`).prop("checked", true);
|
|
itemEffectDiv.find(`.cumulative-effect[data-cumulative-name=${cumulativeEffect.type}]`).find(".cumulative-effect-amount").val(cumulativeEffect.amount);
|
|
}
|
|
}
|
|
|
|
if(isNew) {
|
|
itemEffectDiv.find(".title").append(`<span class="fs-5 fw-lighter fst-italic text-danger"> - ${ getLocalizedText("menu:effects:may_need_restart") }</span>`)
|
|
}
|
|
|
|
itemEffectDiv.data("itemName", itemName);
|
|
|
|
itemEffectDiv.find(".remove-effect-btn").click(function() {
|
|
itemEffectDiv.remove();
|
|
})
|
|
|
|
$("#drugs-effects").append(itemEffectDiv);
|
|
}
|
|
|
|
function getEffectsDataFromDiv(effectDiv, itemName) {
|
|
let effectData = {
|
|
takingMethod: $(`input[name=drug-assume-type-${itemName}]:checked`).val(),
|
|
effects: [],
|
|
effectsDuration: parseInt( effectDiv.find(".effect-duration").val() ),
|
|
cumulativeEffects: []
|
|
};
|
|
|
|
let visualColorEffect = $(`input[name=camera-color-effects-${itemName}]:checked`).val();
|
|
|
|
if(visualColorEffect != "none") {
|
|
effectData.effects.push(visualColorEffect)
|
|
}
|
|
|
|
const specialEffect = $(`input[name=drug-special-effect-${itemName}]:checked`).val();
|
|
if(specialEffect != "none") {
|
|
effectData.effects.push(specialEffect)
|
|
}
|
|
|
|
effectDiv.find(".cumulative-effect").each(function() {
|
|
if (!$(this).find(".cumulative-effect-checkbox").prop("checked")) return;
|
|
|
|
const type = $(this).data("cumulativeName");
|
|
const action = $(this).find("input[type=radio]:checked").val() || "increase"; // In case people don't select any
|
|
const amount = parseInt( $(this).find(".cumulative-effect-amount").val() );
|
|
|
|
effectData.cumulativeEffects.push({type, action, amount});
|
|
});
|
|
|
|
effectDiv.find(".stackable-effect:checked").each(function() {
|
|
effectData.effects.push( $(this).val() );
|
|
});
|
|
|
|
return effectData;
|
|
}
|
|
|
|
function getAllEffectsData() {
|
|
let effectsData = {};
|
|
|
|
$("#drugs-effects").find(".drug-effect").each(function() {
|
|
let itemName = $(this).data("itemName");
|
|
|
|
effectsData[itemName] = getEffectsDataFromDiv($(this), itemName);
|
|
});
|
|
|
|
return effectsData;
|
|
}
|
|
|
|
$("#create-new-item-effect-btn").click(async function() {
|
|
const itemName = await itemsDialog();
|
|
addItemEffect(itemName, {}, true);
|
|
});
|
|
|
|
// Saves the new effects
|
|
$("#effects").submit(async function(event) {
|
|
if(isThereAnyErrorInForm(event)) return;
|
|
|
|
let serverSettings = {
|
|
drugsEffects: getAllEffectsData(),
|
|
}
|
|
|
|
let clientSettings = {}
|
|
let sharedSettings = {}
|
|
|
|
const response = await $.post(`https://${resName}/saveSettings`, JSON.stringify({
|
|
clientSettings: clientSettings,
|
|
serverSettings: serverSettings,
|
|
sharedSettings: sharedSettings
|
|
}));
|
|
showServerResponse(response);
|
|
});
|
|
|
|
/* AUCTIONS */
|
|
let auctionsDatatable = $("#auctions-container").DataTable( {
|
|
"lengthMenu": [10, 15, 20],
|
|
"columnDefs": [
|
|
{
|
|
"targets": -1, // Ultima colonna
|
|
"data": null,
|
|
"className": "col-1",
|
|
"defaultContent": `
|
|
<div class="d-inline-flex gap-3">
|
|
<button class='btn btn-success py-0 px-2 force-start-auction-btn' data-bs-toggle="tooltip" data-bs-placement="top" title="Start now"><i class=\"bi bi-play-fill\"></i></button>
|
|
</div>
|
|
`
|
|
}
|
|
],
|
|
"createdRow": function ( row, data, index ) {
|
|
$(row).addClass("clickable");
|
|
|
|
$(row).click(function() {
|
|
let auctionId = parseInt(data[0]);
|
|
|
|
editAuction(auctionId);
|
|
});
|
|
|
|
$(row).find(".force-start-auction-btn").click(async function(event) {
|
|
event.stopPropagation();
|
|
|
|
const auctionId = parseInt(data[0]);
|
|
$.post(`https://${resName}/forceStartAuction`, JSON.stringify({auctionId}));
|
|
}).tooltip().prop("title", getLocalizedText("menu:start_now"));
|
|
},
|
|
} );
|
|
|
|
let auctions = {};
|
|
async function loadAuctions() {
|
|
const rawAuctions = await $.post(`https://${resName}/getAllAuctions`);
|
|
|
|
// Manually create the table to avoid incompatibilities due table indexing
|
|
auctions = {};
|
|
|
|
for(const[k, auction] of Object.entries(rawAuctions)) {
|
|
auctions[auction.id] = auction;
|
|
}
|
|
|
|
auctionsDatatable.clear();
|
|
|
|
for(const[auctionId, auction] of Object.entries(auctions)) {
|
|
auctionsDatatable.row.add([
|
|
auctionId || "???",
|
|
auction.label || "???",
|
|
])
|
|
}
|
|
|
|
auctionsDatatable.draw()
|
|
}
|
|
|
|
function setAuctionConfig(auction) {
|
|
const config = auction ? auction.config : {};
|
|
$("#auction-label").val(auction?.label || "Default");
|
|
$("#auction-min-players-online").val(config.minPlayersForAuction ?? 3);
|
|
$("#auction-min-police-online").val(config.minPoliceOnline ?? 0);
|
|
$("#auction-randomize-items").prop("checked", config.randomizeItems ?? false);
|
|
$("#auction-multiplier").val(config.multiplier ?? 1);
|
|
$("#auction-max-rounds").val(config.maxRounds ?? 3);
|
|
$("#auction-alert-police-after-round").val(config.alertPoliceAfterRound ?? 2);
|
|
$("#auction-time-between-rounds").val(config.timeBetweenRoundsSeconds ?? 30);
|
|
$("#auction-warning-before-start").val(config.warningBeforeAuctionStartMinutes ?? 15);
|
|
$("#auction-no-bid-timeout").val(config.noBidTimeoutSeconds ?? 30);
|
|
$("#auction-min-bid-increment").val(config.minBidIncrement ?? 100);
|
|
$("#auction-blip-btn").data("blipData", config.blipData ?? getDefaultBlipCustomization());
|
|
|
|
if(config.accountToPayWith) {
|
|
setRewardDivData("#auction-account-to-pay-with", config.accountToPayWith);
|
|
}
|
|
|
|
// Locations
|
|
$("#auction-locations-list").empty();
|
|
if(config.locations) {
|
|
config.locations.forEach(location => {
|
|
addAuctionLocation(location);
|
|
});
|
|
}
|
|
|
|
// Items
|
|
$("#auction-items-list").empty();
|
|
if(config.objectsRewards) {
|
|
config.objectsRewards.forEach(item => {
|
|
addAuctionItem(item);
|
|
});
|
|
}
|
|
}
|
|
|
|
$("#new-auction-btn").click(function() {
|
|
let auctionModal = $("#auction-modal");
|
|
|
|
auctionModal.modal("show");
|
|
auctionModal.data("action", "create");
|
|
|
|
setAuctionConfig();
|
|
|
|
// Adapts the modal from edit mode to create mode
|
|
$("#delete-auction-btn").hide();
|
|
$("#auction-modal-confirm-btn").text( getLocalizedText("menu:create") );
|
|
})
|
|
|
|
function editAuction(auctionId) {
|
|
let auction = auctions[auctionId];
|
|
|
|
let auctionModal = $("#auction-modal");
|
|
|
|
auctionModal.modal("show");
|
|
|
|
auctionModal.data("action", "edit");
|
|
auctionModal.data("auctionId", auctionId);
|
|
|
|
setAuctionConfig(auction);
|
|
|
|
// Adapts the modal from create mode to edit mode
|
|
$("#delete-auction-btn").show();
|
|
$("#auction-modal-confirm-btn").text( getLocalizedText("menu:save") );
|
|
}
|
|
|
|
function getAuctionSetup() {
|
|
return {
|
|
label: $("#auction-label").val(),
|
|
config: {
|
|
minPlayersForAuction: parseInt( $("#auction-min-players-online").val() ),
|
|
minPoliceOnline: parseInt( $("#auction-min-police-online").val() ),
|
|
blipData: $("#auction-blip-btn").data("blipData"),
|
|
randomizeItems: $("#auction-randomize-items").prop("checked"),
|
|
multiplier: parseFloat( $("#auction-multiplier").val() ),
|
|
maxRounds: parseInt( $("#auction-max-rounds").val() ),
|
|
alertPoliceAfterRound: parseInt( $("#auction-alert-police-after-round").val() ),
|
|
accountToPayWith: getRewardDivData("#auction-account-to-pay-with"),
|
|
timeBetweenRoundsSeconds: parseInt( $("#auction-time-between-rounds").val() ),
|
|
warningBeforeAuctionStartMinutes: parseInt( $("#auction-warning-before-start").val() ),
|
|
noBidTimeoutSeconds: parseInt( $("#auction-no-bid-timeout").val() ),
|
|
minBidIncrement: parseInt( $("#auction-min-bid-increment").val() ),
|
|
objectsRewards: getAuctionItems(),
|
|
locations: getAuctionLocations()
|
|
}
|
|
}
|
|
}
|
|
|
|
$("#auction-form").submit(async function(event) {
|
|
if(isThereAnyErrorInForm(event)) return;
|
|
|
|
let auction = getAuctionSetup();
|
|
|
|
let modal = $("#auction-modal");
|
|
let action = $(modal).data("action");
|
|
|
|
let response = null;
|
|
|
|
switch(action) {
|
|
case "create": {
|
|
response = await $.post(`https://${resName}/createNewAuction`, JSON.stringify(auction));
|
|
break;
|
|
}
|
|
|
|
case "edit": {
|
|
let auctionId = modal.data("auctionId");
|
|
|
|
response = await $.post(`https://${resName}/updateAuction`, JSON.stringify({
|
|
auctionId,
|
|
auction
|
|
}));
|
|
break;
|
|
}
|
|
}
|
|
|
|
modal.modal("hide");
|
|
loadAuctions();
|
|
|
|
showServerResponse(response);
|
|
});
|
|
|
|
$("#delete-auction-btn").click(async function() {
|
|
if(!await confirmDeletion()) return;
|
|
|
|
let auctionId = $("#auction-modal").data("auctionId");
|
|
|
|
const response = await $.post(`https://${resName}/deleteAuction`, JSON.stringify({auctionId}));
|
|
$("#auction-modal").modal("hide");
|
|
|
|
loadAuctions();
|
|
showServerResponse(response);
|
|
})
|
|
|
|
function addAuctionLocation(location = {}) {
|
|
const div = $(`
|
|
<div class="auction-location">
|
|
<h5 class="text-center mb-1 mt-4">${getLocalizedText("menu:broker_ped")}</h5>
|
|
<div class="d-flex justify-content-center align-items-center gap-3">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control broker-model" placeholder="...">
|
|
<label>${getLocalizedText("menu:model")}</label>
|
|
</div>
|
|
|
|
<a class="btn btn-secondary px-4" href="https://docs.fivem.net/docs/game-references/ped-models/" target="_blank" onclick='window.invokeNative("openUrl", "https://docs.fivem.net/docs/game-references/ped-models/")'><i class="bi bi-box-arrow-up-right"></i></a>
|
|
|
|
<div class="form-floating col-1">
|
|
<input type="text" class="form-control broker-coordinates-x" placeholder="...">
|
|
<label>${getLocalizedText("menu:x")}</label>
|
|
</div>
|
|
<div class="form-floating col-1">
|
|
<input type="text" class="form-control broker-coordinates-y" placeholder="...">
|
|
<label>${getLocalizedText("menu:y")}</label>
|
|
</div>
|
|
<div class="form-floating col-1">
|
|
<input type="text" class="form-control broker-coordinates-z" placeholder="...">
|
|
<label>${getLocalizedText("menu:z")}</label>
|
|
</div>
|
|
<div class="form-floating col-auto">
|
|
<input type="text" class="form-control broker-heading" placeholder="...">
|
|
<label>${getLocalizedText("menu:heading")}</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-secondary px-4 choose-broker-coordinates-btn"><i class="bi bi-geo"></i></button>
|
|
</div>
|
|
|
|
<h5 class="text-center mb-1 mt-4">${getLocalizedText("menu:broker_vehicle")}</h5>
|
|
<div class="d-flex justify-content-center align-items-center gap-3">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control vehicle-model" placeholder="...">
|
|
<label>${getLocalizedText("menu:model")}</label>
|
|
</div>
|
|
|
|
<a class="btn btn-secondary px-4" href="https://docs.fivem.net/docs/game-references/vehicle-models/" target="_blank" onclick='window.invokeNative("openUrl", "https://docs.fivem.net/docs/game-references/vehicle-models/")'><i class="bi bi-box-arrow-up-right"></i></a>
|
|
|
|
<div class="form-floating col-1">
|
|
<input type="text" class="form-control vehicle-coordinates-x" placeholder="...">
|
|
<label>${getLocalizedText("menu:x")}</label>
|
|
</div>
|
|
<div class="form-floating col-1">
|
|
<input type="text" class="form-control vehicle-coordinates-y" placeholder="...">
|
|
<label>${getLocalizedText("menu:y")}</label>
|
|
</div>
|
|
<div class="form-floating col-1">
|
|
<input type="text" class="form-control vehicle-coordinates-z" placeholder="...">
|
|
<label>${getLocalizedText("menu:z")}</label>
|
|
</div>
|
|
<div class="form-floating col-auto">
|
|
<input type="text" class="form-control vehicle-heading" placeholder="...">
|
|
<label>${getLocalizedText("menu:heading")}</label>
|
|
</div>
|
|
<button type="button" class="btn btn-secondary px-4 choose-vehicle-coordinates-btn"><i class="bi bi-geo"></i></button>
|
|
</div>
|
|
|
|
<h5 class="text-center mb-1 mt-4">${getLocalizedText("menu:text")}</h5>
|
|
<div class="d-flex justify-content-center align-items-center gap-3">
|
|
<div class="form-floating col-1">
|
|
<input type="text" class="form-control text-coordinates-x" placeholder="...">
|
|
<label>${getLocalizedText("menu:x")}</label>
|
|
</div>
|
|
<div class="form-floating col-1">
|
|
<input type="text" class="form-control text-coordinates-y" placeholder="...">
|
|
<label>${getLocalizedText("menu:y")}</label>
|
|
</div>
|
|
<div class="form-floating col-1">
|
|
<input type="text" class="form-control text-coordinates-z" placeholder="...">
|
|
<label>${getLocalizedText("menu:z")}</label>
|
|
</div>
|
|
<button type="button" class="btn btn-secondary px-4 choose-text-coordinates-btn"><i class="bi bi-geo"></i></button>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-danger px-4 remove-location-btn">${getLocalizedText("menu:remove_location")}</button>
|
|
|
|
<hr class="thick-hr">
|
|
</div>
|
|
`);
|
|
|
|
div.find(".remove-location-btn").click(function() {
|
|
div.remove();
|
|
});
|
|
|
|
// Broker
|
|
div.find(".broker-model").val(location.broker?.model || "s_m_m_highsec_01");
|
|
div.find(".broker-coordinates-x").val(location.broker?.coordinates?.x || "");
|
|
div.find(".broker-coordinates-y").val(location.broker?.coordinates?.y || "");
|
|
div.find(".broker-coordinates-z").val(location.broker?.coordinates?.z || "");
|
|
div.find(".broker-heading").val(location.broker?.heading || "");
|
|
div.find(".choose-broker-coordinates-btn").click(async function() {
|
|
const model = div.find(".broker-model").val();
|
|
const data = await placeEntity(model, "ped");
|
|
if(!data) return;
|
|
|
|
div.find(".broker-coordinates-x").val(data.coords.x);
|
|
div.find(".broker-coordinates-y").val(data.coords.y);
|
|
div.find(".broker-coordinates-z").val(data.coords.z);
|
|
div.find(".broker-heading").val(data.heading);
|
|
});
|
|
|
|
// Broker vehicle
|
|
div.find(".vehicle-model").val(location.vehicle?.model || "burrito3");
|
|
div.find(".vehicle-coordinates-x").val(location.vehicle?.coordinates?.x || "");
|
|
div.find(".vehicle-coordinates-y").val(location.vehicle?.coordinates?.y || "");
|
|
div.find(".vehicle-coordinates-z").val(location.vehicle?.coordinates?.z || "");
|
|
div.find(".vehicle-heading").val(location.vehicle?.heading || "");
|
|
div.find(".choose-vehicle-coordinates-btn").click(async function() {
|
|
const model = div.find(".vehicle-model").val();
|
|
const data = await placeEntity(model, "vehicle");
|
|
if(!data) return;
|
|
|
|
div.find(".vehicle-coordinates-x").val(data.coords.x);
|
|
div.find(".vehicle-coordinates-y").val(data.coords.y);
|
|
div.find(".vehicle-coordinates-z").val(data.coords.z);
|
|
div.find(".vehicle-heading").val(data.heading);
|
|
});
|
|
|
|
// Text
|
|
div.find(".text-coordinates-x").val(location.text?.coordinates?.x || "");
|
|
div.find(".text-coordinates-y").val(location.text?.coordinates?.y || "");
|
|
div.find(".text-coordinates-z").val(location.text?.coordinates?.z || "");
|
|
div.find(".choose-text-coordinates-btn").click(async function() {
|
|
const data = await placeEntity();
|
|
if(!data) return;
|
|
|
|
div.find(".text-coordinates-x").val(data.x);
|
|
div.find(".text-coordinates-y").val(data.y);
|
|
div.find(".text-coordinates-z").val(data.z);
|
|
});
|
|
|
|
$("#auction-locations-list").append(div);
|
|
}
|
|
|
|
$("#add-auction-location-btn").click(function() {
|
|
addAuctionLocation();
|
|
})
|
|
|
|
function getAuctionLocations() {
|
|
let locations = [];
|
|
|
|
$("#auction-locations-list").find(".auction-location").each(function() {
|
|
let location = {
|
|
broker: {
|
|
model: $(this).find(".broker-model").val(),
|
|
coordinates: {
|
|
x: parseFloat( $(this).find(".broker-coordinates-x").val() ),
|
|
y: parseFloat( $(this).find(".broker-coordinates-y").val() ),
|
|
z: parseFloat( $(this).find(".broker-coordinates-z").val() ),
|
|
},
|
|
heading: parseFloat( $(this).find(".broker-heading").val() ),
|
|
},
|
|
vehicle: {
|
|
model: $(this).find(".vehicle-model").val(),
|
|
coordinates: {
|
|
x: parseFloat( $(this).find(".vehicle-coordinates-x").val() ),
|
|
y: parseFloat( $(this).find(".vehicle-coordinates-y").val() ),
|
|
z: parseFloat( $(this).find(".vehicle-coordinates-z").val() ),
|
|
},
|
|
heading: parseFloat( $(this).find(".vehicle-heading").val() ),
|
|
},
|
|
text: {
|
|
coordinates: {
|
|
x: parseFloat( $(this).find(".text-coordinates-x").val() ),
|
|
y: parseFloat( $(this).find(".text-coordinates-y").val() ),
|
|
z: parseFloat( $(this).find(".text-coordinates-z").val() ),
|
|
}
|
|
}
|
|
}
|
|
|
|
locations.push(location);
|
|
});
|
|
|
|
return locations;
|
|
}
|
|
|
|
function addAuctionItem(item={}) {
|
|
const div = $(`
|
|
<div class="auction-item d-flex justify-content-center align-items-center gap-3 my-3">
|
|
<button type="button" class="btn py-0 btn-close my-auto me-2"></button>
|
|
|
|
<div class="form-floating">
|
|
<input type="number" class="form-control item-quantity" placeholder="...">
|
|
<label>${getLocalizedText("menu:quantity")}</label>
|
|
</div>
|
|
|
|
<div class="form-floating" data-bs-toggle="tooltip" data-bs-placement="top" title="${getLocalizedText("menu:base_price:info")}">
|
|
<input type="number" class="form-control item-base-price" placeholder="...">
|
|
<label>${getLocalizedText("menu:base_price")}</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`);
|
|
|
|
const objectDiv = createDivForObjectChoose(item);
|
|
div.find(".btn-close").after(objectDiv);
|
|
|
|
div.find(".btn-close").click(function() {
|
|
div.remove();
|
|
});
|
|
|
|
div.find("[data-bs-toggle=tooltip]").tooltip();
|
|
|
|
div.find(".item-quantity").val(item.quantity || 10);
|
|
div.find(".item-base-price").val(item.basePrice || 1000);
|
|
setChoosenObject(objectDiv, item);
|
|
|
|
$("#auction-items-list").append(div);
|
|
}
|
|
|
|
$("#add-auction-item-btn").click(function() {
|
|
addAuctionItem();
|
|
});
|
|
|
|
function getAuctionItems() {
|
|
let items = [];
|
|
|
|
$("#auction-items-list").find(".auction-item").each(function() {
|
|
const object = getChoosenObject($(this));
|
|
|
|
let item = {
|
|
quantity: parseInt( $(this).find(".item-quantity").val() ),
|
|
basePrice: parseInt( $(this).find(".item-base-price").val() ),
|
|
name: object.name,
|
|
label: object.label,
|
|
type: object.type,
|
|
}
|
|
|
|
items.push(item);
|
|
});
|
|
|
|
return items;
|
|
}
|
|
|
|
$("#auction-blip-btn").click(async function() {
|
|
const oldBlipData = $(this).data("blipData");
|
|
const blipData = await blipDialog(oldBlipData);
|
|
|
|
$(this).data("blipData", blipData);
|
|
});
|
|
|
|
// [[ NEXUS ]]
|
|
const voteInstanceRater = raterJs({
|
|
starSize: 35,
|
|
element: document.querySelector("#vote-instance-rater"),
|
|
rateCallback: async function rateCallback(rating, done) {
|
|
const instanceId = $("#nexus-modal").data("instance").id;
|
|
const success = await $.post(`https://${resName}/nexus/rateInstance`, JSON.stringify({rating, instanceId}));
|
|
if(success) voteInstanceRater.setRating(rating);
|
|
|
|
done();
|
|
}
|
|
});
|
|
|
|
const averageInstanceVotes = raterJs({
|
|
starSize: 20,
|
|
readOnly: true,
|
|
element: document.querySelector("#nexus-modal-instance-average-rating"),
|
|
});
|
|
|
|
$("#nexus-import-instance-btn").click(async function() {
|
|
const instance = $("#nexus-modal").data("instance");
|
|
const id = instance.id;
|
|
|
|
const mappedItemsNames = await itemsMapperDialog(instance.requiredItemsNames);
|
|
if(!mappedItemsNames) return;
|
|
|
|
const response = await $.post(`https://${resName}/nexus/importInstance`, JSON.stringify({id, mappedItemsNames}));
|
|
$("#nexus-modal").modal("hide");
|
|
|
|
if(response === true) reloadAllData();
|
|
|
|
showServerResponse(response);
|
|
});
|
|
|
|
let nexusDataTable = $("#nexus-table").DataTable({
|
|
"lengthMenu": [5, 10, 15, 20],
|
|
"pageLength": 10,
|
|
"order": [[4, 'desc'], [5, 'desc']],
|
|
"createdRow": function (row, data, index) {
|
|
$(row).addClass("clickable");
|
|
$(row).click(function () {
|
|
const instance = $(this).data("instance");
|
|
showInstance(instance);
|
|
$("#nexus-modal").modal("show");
|
|
});
|
|
},
|
|
"columnDefs": [{ "defaultContent": "???", "targets": "_all" }]
|
|
});
|
|
|
|
function showInstance(instance) {
|
|
$("#nexus-modal").data("instance", instance);
|
|
|
|
$("#nexus-modal-instance-listing-label").text(instance.label);
|
|
$("#nexus-instance-content-type").text(instance.type);
|
|
$("#nexus-instance-content-amount").text(instance.content.length);
|
|
$("#nexus-modal-instance-description").text(instance.description || getLocalizedText("menu:nexus:no_description"));
|
|
$("#nexus-modal-instance-author").text(instance.author);
|
|
|
|
// Content names and labels
|
|
|
|
$("#nexus-modal-instance-content").empty();
|
|
instance.content.forEach(content => {
|
|
$("#nexus-modal-instance-content").append(`
|
|
<li class="list-group-item">${content.label || content.name}</li>
|
|
`);
|
|
});
|
|
|
|
// Votes
|
|
if(instance?.votes?.total > 0) {
|
|
averageInstanceVotes.setRating(instance?.votes.averageRating);
|
|
} else {
|
|
averageInstanceVotes.setRating(0);
|
|
}
|
|
|
|
$("#nexus-modal-instance-total-votes").text(instance.votes?.total || 0);
|
|
|
|
// This server vote
|
|
voteInstanceRater.setRating(0);
|
|
}
|
|
|
|
$("#upload-to-nexus-btn").click(async function() {
|
|
const type = await listDialog(getLocalizedText("menu:nexus:data_to_share"), getLocalizedText("menu:search"), [
|
|
{value: "harvestable_item", label: getLocalizedText("menu:harvestable_item")},
|
|
{value: "drug_field", label: getLocalizedText("menu:drug_field")},
|
|
{value: "crafting_recipe", label: getLocalizedText("menu:crafting_recipe")},
|
|
{value: "laboratory", label: getLocalizedText("menu:laboratory")},
|
|
]);
|
|
if(!type) return;
|
|
|
|
let dataToChooseFrom = [];
|
|
|
|
// Depending on the type, we retrieve the possible data and create a multiSelectDialog
|
|
switch(type) {
|
|
case "harvestable_item":
|
|
dataToChooseFrom = await $.post(`https://${resName}/getAllHarvestableItems`);
|
|
break;
|
|
case "drug_field":
|
|
dataToChooseFrom = await $.post(`https://${resName}/getAllDrugsFields`);
|
|
break;
|
|
case "crafting_recipe":
|
|
dataToChooseFrom = await $.post(`https://${resName}/getAllCraftingRecipes`);
|
|
break;
|
|
case "laboratory":
|
|
dataToChooseFrom = await $.post(`https://${resName}/getAllLaboratories`);
|
|
break;
|
|
}
|
|
if(!dataToChooseFrom) return;
|
|
|
|
let elements = [];
|
|
|
|
Object.values(dataToChooseFrom).forEach(data => {
|
|
elements.push({
|
|
value: data.id,
|
|
label: data.id + " - " + (data.label || data.name)
|
|
});
|
|
})
|
|
|
|
const selectedData = await multiSelectDialog(getLocalizedText("menu:nexus:data_to_share"), getLocalizedText("menu:search"), elements);
|
|
if(!selectedData) return;
|
|
|
|
$("#nexus-modal-upload").data("selectedData", selectedData);
|
|
$("#nexus-modal-upload").data("dataType", type);
|
|
|
|
$("#nexus-upload-label").val("");
|
|
$("#nexus-upload-description").val("");
|
|
|
|
$("#nexus-upload-accept-tos").prop("checked", false);
|
|
|
|
$("#nexus-modal-upload").modal("show");
|
|
});
|
|
|
|
$("#nexus-upload-form").submit(async function(event) {
|
|
if(isThereAnyErrorInForm(event)) return;
|
|
|
|
const dataToUpload = {
|
|
type: $("#nexus-modal-upload").data("dataType"),
|
|
ids: $("#nexus-modal-upload").data("selectedData"),
|
|
label: $("#nexus-upload-label").val(),
|
|
description: $("#nexus-upload-description").val(),
|
|
}
|
|
|
|
const result = await $.post(`https://${resName}/nexus/uploadData`, JSON.stringify(dataToUpload));
|
|
|
|
if(result == true) {
|
|
swal("Success", getLocalizedText("menu:nexus:upload_success"), "success");
|
|
resetNexus();
|
|
} else {
|
|
swal("Error", result, "error");
|
|
}
|
|
|
|
$("#nexus-modal-upload").modal("hide");
|
|
});
|
|
|
|
$("#enter-in-nexus-btn").click(async function() {
|
|
$("#nexus-login").find(".spinner-border").show();
|
|
$("#enter-in-nexus-label").text("...");
|
|
|
|
const sharedData = await $.get(`https://${resName}/nexus/getSharedData`);
|
|
if(!sharedData) {
|
|
swal("Error", getLocalizedText("menu:nexus:not_available"), "error");
|
|
resetNexus();
|
|
return;
|
|
}
|
|
|
|
nexusDataTable.clear()
|
|
|
|
Object.values(sharedData).forEach(instance => {
|
|
const roundedAverageRating = instance?.votes?.averageRating ? Math.round(instance.votes.averageRating) : 0;
|
|
const ratingStars = instance?.votes?.total ? "⭐".repeat(roundedAverageRating) : getLocalizedText("menu:nexus:not_rated");
|
|
const limitedDescription = instance.description?.length > 30 ? instance.description.substring(0, 30) + "..." : instance.description;
|
|
const amount = instance.content.length;
|
|
|
|
const rawRow = nexusDataTable.row.add( [instance.label, limitedDescription, instance.type, amount, ratingStars, instance.votes?.total || 0, instance.author] );
|
|
|
|
const rowDiv = $(rawRow.node());
|
|
$(rowDiv).data("instance", instance);
|
|
})
|
|
|
|
nexusDataTable.draw();
|
|
|
|
$("#nexus-login").hide();
|
|
$("#nexus-container").show();
|
|
})
|
|
|
|
function resetNexus() {
|
|
$("#nexus-login").show();
|
|
$("#nexus-login").find(".spinner-border").hide();
|
|
$("#enter-in-nexus-label").text("Enter in Nexus");
|
|
|
|
$("#nexus-container").hide();
|
|
}
|
|
|
|
function reloadAllData() {
|
|
resetNexus();
|
|
|
|
loadHarvestableItems();
|
|
loadDrugsFields();
|
|
loadCraftingRecipes();
|
|
loadLaboratories();
|
|
loadAuctions();
|
|
}
|
|
|
|
// Open/Close menu
|
|
function openMenu(version, fullConfig) {
|
|
$("#advanced-drugs-creator-version").text(version);
|
|
|
|
$("#drugs_creator").show();
|
|
|
|
reloadAllData();
|
|
|
|
window.dispatchEvent(new Event('menuOpened'));
|
|
|
|
loadSettings(fullConfig);
|
|
}
|
|
|
|
function closeMenu() {
|
|
$("#drugs_creator").hide();
|
|
|
|
$.post(`https://${resName}/close`, {})
|
|
}
|
|
$("#close-main-btn").click(closeMenu);
|
|
|
|
function playSound(name, speed = 1.0, volume = 1.0) {
|
|
// Create an AudioContext
|
|
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
|
|
|
// Create the audio element
|
|
var audio = new Audio(`./assets/audio/${name}.mp3`);
|
|
audio.playbackRate = speed;
|
|
|
|
// Create a media element source
|
|
var source = audioCtx.createMediaElementSource(audio);
|
|
|
|
// Create a gain node
|
|
var gainNode = audioCtx.createGain();
|
|
gainNode.gain.value = volume; // Set the volume
|
|
|
|
// Connect the source to the gain node and the gain node to the destination
|
|
source.connect(gainNode);
|
|
gainNode.connect(audioCtx.destination);
|
|
|
|
// Play the audio
|
|
audio.play();
|
|
}
|
|
|
|
// Messages received by client
|
|
window.addEventListener('message', (event) => {
|
|
let data = event.data;
|
|
let action = data.action;
|
|
|
|
if (action == 'openMenu') {
|
|
openMenu(data.version, data.fullConfig);
|
|
} else if (action == "playSound") {
|
|
playSound(data.name, data.speed, data.volume);
|
|
}
|
|
}) |