const resName = GetParentResourceName();
let hasDoorsCreator = null; // editing this is useless, don't do it
/* Utils */
async function placeObjectAndReturnCoords(object) {
$("html").hide();
return new Promise((resolve, reject) => {
$.post(`https://${resName}/placeObjectAndReturnCoords`, JSON.stringify({object: object}), function(coords) {
$("html").show();
resolve(coords);
})
})
}
async function placeGasPoints() {
$("html").hide();
return new Promise((resolve, reject) => {
$.post(`https://${resName}/placeGasPoints`, JSON.stringify(), function(gasPoints) {
$("html").show();
resolve(gasPoints);
})
})
}
// Open/Close menu
function openMenu(version, fullConfig) {
$("#robbery-creator-version").text(version);
loadSettings(fullConfig);
loadHeists();
loadCargoRobberies();
loadPlannings();
$("#robbery-creator").show()
}
function closeMenu() {
// Resets current active tab
$("#robbery-creator").find(".nav-link, .tab-pane").each(function() {
if($(this).data("isDefault") == "1") {
$(this).addClass(["active", "show"])
} else {
$(this).removeClass(["active", "show"])
}
})
$("#robbery-creator").hide();
$.post(`https://${resName}/close`, {})
}
$("#close-main-btn").click(closeMenu);
// Messages received by client
window.addEventListener('message', (event) => {
let data = event.data;
let action = data.action;
switch(action) {
case "openMenu": {
openMenu(data.version, data.fullConfig);
break;
}
case "hasDoorsCreator": {
hasDoorsCreator = data.hasDoorsCreator;
break;
}
}
})
function loadSettings(fullConfig) {
// Generic
setTomSelectValue("#settings_locale", fullConfig.locale)
$("#settings_acePermission").val(fullConfig.acePermission);
$("#settings_can_always_carry").prop("checked", fullConfig.canAlwaysCarryItem);
$("#settings_can_receive_multiple_same_item").prop("checked", fullConfig.canReceiveMultipleTimesTheSameItem);
$("#settings-global-timeout").val(fullConfig.globalMinutesTimeout);
// Targeting
setTomSelectValue("#settings-targeting-script", fullConfig.targetingScript)
// Help notification
setTomSelectValue("#settings-help-notification-script", fullConfig.helpNotification)
// NPC Mugging - Shared
$("#enable-npc-mugging").prop("checked", fullConfig.isNpcMuggingEnabled).change();
$("#seconds-to-mug").val(fullConfig.secondsToMug);
// NPC Mugging - Client
$("#can-make-npc-to-follow").prop("checked", fullConfig.clNpcMugging.canMakeFollow);
$("#npc-mugging-cooldown-on-mugging").val(fullConfig.clNpcMugging.cooldownOnMugging);
// NPC Mugging - Keys
$("#npc-mugging-key-to-mug").val(fullConfig.npcMuggingKeys.keyToMug);
$("#npc-mugging-key-to-follow").val(fullConfig.npcMuggingKeys.keyToFollow);
$("#npc-mugging-key-to-go-away").val(fullConfig.npcMuggingKeys.keyToGoAway);
// NPC Mugging - Server
setNpcMuggingRobbableObjects(fullConfig.svNpcMugging.robbableObjects)
$("#npc-mugging-minimum-police").val(fullConfig.svNpcMugging.minimumPolice)
$("#npc-mugging-probability-police-alert").val(fullConfig.svNpcMugging.probabilityPoliceAlert)
$("#npc-mugging-minimum-objects-amount").val(fullConfig.svNpcMugging.minimumObjectsAmount)
$("#npc-mugging-maximum-objects-amount").val(fullConfig.svNpcMugging.maximumObjectsAmount)
$("#minutes-before-automatically-run-away").val(fullConfig.svNpcMugging.minutesAfterAutomaticallyRunAway);
$("#npc-mugging-max-npcs-following").val(fullConfig.svNpcMugging.maxNPCsFollowing);
// Gas mask
$("#gas-mask-item-name").val(fullConfig.gasMask.name);
$("#gas-mask-item-duration").val(fullConfig.gasMask.duration);
$("#gas-mask-lose-on-use").prop("checked", fullConfig.gasMask.loseOnUse);
$("#gas-mask-cloth-id").val(fullConfig.gasMaskClothId);
// Drill
$("#drill-required-for-cargo-robbery").prop("checked", fullConfig.drill.requiredForCargoRobbery);
$("#drill-item-name").val(fullConfig.drill.name);
$("#drill-lose-on-use").prop("checked", fullConfig.drill.loseOnUse);
// Cargo robbery
$("#cargo-robbery-minutes-delete-after-robbed").val(fullConfig.cargoRobbery.minutesBeforeDeleteAfterRobbed);
$("#cargo-robbery-minutes-delete-after-arrived").val(fullConfig.cargoRobbery.minutesBeforeDeleteAfterArrived);
$("#cargo-robbery-max-minutes-difference-from-defined-date").val(fullConfig.cargoRobbery.maxMinutesDifferenceFromDefinedDate);
$("#cargo-robbery-probability-police-alert").val(fullConfig.cargoRobbery.probabilityPoliceAlert);
// Discord logs
$("#settings_isDiscordLogActive").prop("checked", fullConfig.areDiscordLogsActive);
toggleDiscordLogsInSettings(fullConfig.areDiscordLogsActive);
$("#settings_discordWebhook").val(fullConfig.mainDiscordWebhook);
for(const[logType, webhook] of Object.entries(fullConfig.specificWebhooks)) {
$("#settings_specific_webhooks").find(`[data-log-type="${logType}"]`).val(webhook);
}
}
/* Discord logs */
function toggleDiscordLogsInSettings(enable) {
$("#settings_discordWebhook").prop("disabled", !enable);
$("#settings_discordWebhook").prop("required", enable);
$("#settings_specific_webhooks").find(`.form-control`).prop("disabled", !enable);
}
$("#settings_isDiscordLogActive").change(function() {
let enabled = $(this).prop("checked");
toggleDiscordLogsInSettings(enabled);
})
function getSeparatedDiscordWebhooks() {
let webhooks = {};
$("#settings_specific_webhooks").find(".form-control").each(function(index, element) {
let logType = $(element).data("logType");
let webhook = $(element).val();
if(webhook) {
webhooks[logType] = webhook;
}
});
return webhooks;
}
/* Discord logs END */
$("#settings").submit(async function(event) {
if(isThereAnyErrorInForm(event)) return;
let clientSettings = {
gasMaskClothId: parseInt( $("#gas-mask-cloth-id").val() ),
targetingScript:$("#settings-targeting-script").val(),
helpNotification: $("#settings-help-notification-script").val(),
// NPC Mugging - Keys
npcMuggingKeys: {
keyToMug: parseInt($("#npc-mugging-key-to-mug").val()),
keyToFollow: parseInt($("#npc-mugging-key-to-follow").val()),
keyToGoAway: parseInt($("#npc-mugging-key-to-go-away").val()),
}
}
let sharedSettings = {
locale: $("#settings_locale").val(),
}
let serverSettings = {
// Generic
canAlwaysCarryItem: $("#settings_can_always_carry").prop("checked"),
canReceiveMultipleTimesTheSameItem: $("#settings_can_receive_multiple_same_item").prop("checked"),
acePermission: $("#settings_acePermission").val(),
globalMinutesTimeout: parseInt( $("#settings-global-timeout").val() ),
gasMask: {
name: $("#gas-mask-item-name").val(),
duration: parseInt( $("#gas-mask-item-duration").val() ),
loseOnUse: $("#gas-mask-lose-on-use").prop("checked"),
},
drill: {
requiredForCargoRobbery: $("#drill-required-for-cargo-robbery").prop("checked"),
name: $("#drill-item-name").val(),
loseOnUse: $("#drill-lose-on-use").prop("checked")
},
cargoRobbery: {
minutesBeforeDeleteAfterRobbed: parseInt( $("#cargo-robbery-minutes-delete-after-robbed").val() ),
minutesBeforeDeleteAfterArrived: parseInt( $("#cargo-robbery-minutes-delete-after-arrived").val() ),
maxMinutesDifferenceFromDefinedDate: parseInt( $("#cargo-robbery-max-minutes-difference-from-defined-date").val() ),
probabilityPoliceAlert: parseInt( $("#cargo-robbery-probability-police-alert").val() ),
},
// Discord logs
areDiscordLogsActive: $("#settings_isDiscordLogActive").prop("checked"),
mainDiscordWebhook: $("#settings_discordWebhook").val(),
specificWebhooks: getSeparatedDiscordWebhooks(),
}
const response = await $.post(`https://${resName}/saveSettings`, JSON.stringify({
clientSettings: clientSettings,
sharedSettings: sharedSettings,
serverSettings: serverSettings,
}));
showServerResponse(response)
refreshTranslations(sharedSettings.locale);
});
/*
██ ██ ███████ ██ ███████ ████████ ███████
██ ██ ██ ██ ██ ██ ██
███████ █████ ██ ███████ ██ ███████
██ ██ ██ ██ ██ ██ ██
██ ██ ███████ ██ ███████ ██ ███████
*/
let heistsDatatable = $("#heists-container").DataTable( {
"lengthMenu": [10, 15, 20],
"createdRow": function ( row, data, index ) {
$(row).addClass("clickable");
$(row).click(function() {
let id = parseInt( data[0] );
editHeist(id);
})
},
});
let heists = {};
function loadHeists() {
$.post(`https://${resName}/getAllHeists`, {}, async function(rawHeists) {
// Manually create the table to avoid incompatibilities due table indexing
heists = {};
for(const[k, heistData] of Object.entries(rawHeists)) {
heists[heistData.id] = heistData;
}
heistsDatatable.clear();
for(const[id, heistData] of Object.entries(heists)) {
heistsDatatable.row.add([
id,
heistData.label,
heistData.stages.length
]);
}
heistsDatatable.draw();
})
}
function setDefaultDataOfHeistModal() {
$("#heist-label").val("Default");
$("#heist-minimum-police").val(0);
$("#heist-timeout").val(120);
$("#heist-reset").val(60);
$("#heist-time-limit-minutes").val(0);
$("#heist-stages").empty();
}
$("#new-heist-btn").click(function() {
let heistModal = $("#heist-modal");
// Converts from edit modal to create modal
heistModal.data("action", "create");
$("#delete-heist-btn").hide();
$("#save-heist-btn").text( getLocalizedText("menu:create") );
setDefaultDataOfHeistModal();
heistModal.modal("show");
})
function editHeist(id) {
let heistData = heists[id];
let heistModal = $("#heist-modal");
heistModal.data("action", "edit");
heistModal.data("heistId", id);
$("#delete-heist-btn").show();
$("#save-heist-btn").text( getLocalizedText("menu:save") );
$("#heist-label").val(heistData.label);
$("#heist-minimum-police").val(heistData.minimumPolice);
$("#heist-timeout").val(heistData.timeoutMinutes);
$("#heist-reset").val(heistData.minutesBeforeReset);
$("#heist-time-limit-minutes").val(heistData.timeLimitMinutes);
$("#heist-stages").empty();
heistData.stages.forEach(stage => {
addHeistStage(stage);
})
heistModal.modal("show");
}
/*
██████ ██████ ██████ ██████ █████ ██████ ██ ███████ ██████ ██████ ██ ███████ ██████ ████████ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ██ ██ ██████ ██████ ███████ ██████ ██ █████ ██ ██ ██████ ██ █████ ██ ██ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██████ ██████ ██████ ██ ██ ██████ ███████ ███████ ██████ ██████ █████ ███████ ██████ ██ ███████
*/
function getAllRobbableObjectsFromStageDiv(stageDiv) {
let robbableObjects = [];
const stepMethod = "ROBBABLE_OBJECT"
stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
let robbableObjectDiv = $(this);
let robbableObject = {
method: stepMethod,
type: robbableObjectDiv.find(".object-type").val(),
coords: {
x: parseFloat( robbableObjectDiv.find(".coords-x").val() ),
y: parseFloat( robbableObjectDiv.find(".coords-y").val() ),
z: parseFloat( robbableObjectDiv.find(".coords-z").val() ),
},
heading: parseFloat( robbableObjectDiv.find(".heading").val() ),
blipData : robbableObjectDiv.data("blipData") || getDefaultBlipCustomization(),
item: {
type: robbableObjectDiv.find(".item-type").val(),
name: robbableObjectDiv.find(".item-name").val(),
minQuantity: parseInt( robbableObjectDiv.find(".item-min-quantity").val() ),
maxQuantity: parseInt( robbableObjectDiv.find(".item-max-quantity").val() ),
},
isOptional: robbableObjectDiv.find(".is-optional-checkbox").prop("checked"),
isInvisible: robbableObjectDiv.find(".is-invisible-checkbox").prop("checked"),
}
robbableObjects.push(robbableObject);
});
return robbableObjects
}
async function addRobbableObjectHeist(stageDiv, robbableObject) {
let robbableObjectDiv = $(`
${getLocalizedText("menu:robbable_object")}
`);
robbableObjectDiv.find(".choose-coords-and-heading-btn").click(async function() {
let objectType = robbableObjectDiv.find(".object-type").val();
let data = await placeObjectAndReturnCoords(objectType);
if(data) {
robbableObjectDiv.find(".coords-x").val(data.coords.x);
robbableObjectDiv.find(".coords-y").val(data.coords.y);
robbableObjectDiv.find(".coords-z").val(data.coords.z);
robbableObjectDiv.find(".heading").val(data.heading);
}
}).tooltip();
robbableObjectDiv.find(".customize-blip-btn").click(async function() {
let oldBlipData = robbableObjectDiv.data("blipData");
let blipData = await blipDialog(oldBlipData)
robbableObjectDiv.data("blipData", blipData);
})
robbableObjectDiv.find(".delete-step-btn").click(function() {
robbableObjectDiv.remove();
})
robbableObjectDiv.find(".choose-robbable-item-btn").click(async function() {
let objectType = robbableObjectDiv.find(".robbable-item").find(".item-type").val();
let objectName = await objectDialog(objectType);
robbableObjectDiv.find(".item-name").val(objectName);
}).tooltip();
// Sets value if the object is not new
if(robbableObject) {
robbableObjectDiv.find(".object-type").val(robbableObject.type);
robbableObjectDiv.find(".coords-x").val(robbableObject.coords.x);
robbableObjectDiv.find(".coords-y").val(robbableObject.coords.y);
robbableObjectDiv.find(".coords-z").val(robbableObject.coords.z);
robbableObjectDiv.find(".heading").val(robbableObject.heading);
robbableObjectDiv.data("blipData", robbableObject.blipData);
robbableObjectDiv.find(".item-type").val(robbableObject.item.type);
robbableObjectDiv.find(".item-name").val(robbableObject.item.name);
robbableObjectDiv.find(".item-min-quantity").val(robbableObject.item.minQuantity);
robbableObjectDiv.find(".item-max-quantity").val(robbableObject.item.maxQuantity);
robbableObjectDiv.find(".is-optional-checkbox").prop("checked", robbableObject.isOptional);
robbableObjectDiv.find(".is-invisible-checkbox").prop("checked", robbableObject.isInvisible);
}
stageDiv.find(".steps-list").append(robbableObjectDiv);
}
/*
██████ █████ ██ ███ ██ ████████ ██ ███ ██ ██████ ███████
██ ██ ██ ██ ██ ████ ██ ██ ██ ████ ██ ██ ██
██████ ███████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ████ ██ ██ ██ ████ ██████ ███████
*/
function getAllPaintingsFromStageDiv(stageDiv) {
let paintings = [];
const stepMethod = "PAINTING"
stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
let paintingDiv = $(this);
let painting = {
method: stepMethod,
type: paintingDiv.find(".object-type").val(),
coords: {
x: parseFloat( paintingDiv.find(".coords-x").val() ),
y: parseFloat( paintingDiv.find(".coords-y").val() ),
z: parseFloat( paintingDiv.find(".coords-z").val() ),
},
heading: parseFloat( paintingDiv.find(".heading").val() ),
blipData : paintingDiv.data("blipData") || getDefaultBlipCustomization(),
item: {
type: paintingDiv.find(".item-type").val(),
name: paintingDiv.find(".item-name").val(),
minQuantity: parseInt( paintingDiv.find(".item-min-quantity").val() ),
maxQuantity: parseInt( paintingDiv.find(".item-max-quantity").val() ),
},
isOptional: paintingDiv.find(".is-optional-checkbox").prop("checked"),
isInvisible: paintingDiv.find(".is-invisible-checkbox").prop("checked"),
}
paintings.push(painting);
});
return paintings
}
async function addPaintingHeist(stageDiv, painting) {
let paintingDiv = $(`
${getLocalizedText("menu:painting")}
`);
paintingDiv.find(".choose-coords-and-heading-btn").click(async function() {
let objectType = paintingDiv.find(".object-type").val();
let data = await placeObjectAndReturnCoords(objectType);
if(data) {
paintingDiv.find(".coords-x").val(data.coords.x);
paintingDiv.find(".coords-y").val(data.coords.y);
paintingDiv.find(".coords-z").val(data.coords.z);
paintingDiv.find(".heading").val(data.heading);
}
}).tooltip();
paintingDiv.find(".customize-blip-btn").click(async function() {
let oldBlipData = paintingDiv.data("blipData");
let blipData = await blipDialog(oldBlipData)
paintingDiv.data("blipData", blipData);
})
paintingDiv.find(".delete-robbable-object-btn").click(function() {
paintingDiv.remove();
})
paintingDiv.find(".choose-robbable-item-btn").click(async function() {
let objectType = paintingDiv.find(".robbable-item").find(".item-type").val();
let objectName = await objectDialog(objectType);
paintingDiv.find(".item-name").val(objectName);
}).tooltip();
// Sets value if the object is not new
if(painting) {
paintingDiv.find(".object-type").val(painting.type);
paintingDiv.find(".coords-x").val(painting.coords.x);
paintingDiv.find(".coords-y").val(painting.coords.y);
paintingDiv.find(".coords-z").val(painting.coords.z);
paintingDiv.find(".heading").val(painting.heading);
paintingDiv.data("blipData", painting.blipData);
paintingDiv.find(".item-type").val(painting.item.type);
paintingDiv.find(".item-name").val(painting.item.name);
paintingDiv.find(".item-min-quantity").val(painting.item.minQuantity);
paintingDiv.find(".item-max-quantity").val(painting.item.maxQuantity);
paintingDiv.find(".is-optional-checkbox").prop("checked", painting.isOptional);
paintingDiv.find(".is-invisible-checkbox").prop("checked", painting.isInvisible);
}
stageDiv.find(".steps-list").append(paintingDiv);
}
/*
██████ ██ █████ ███████ ███████ ██████ ██ ███████ ██████ ██ █████ ██ ██ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ███ ██ ███████ ███████ ███████ ██ ██ ██ ███████ ██████ ██ ███████ ████ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ███████ ██ ██ ███████ ███████ ██████ ██ ███████ ██ ███████ ██ ██ ██ ███████
*/
function getAllGlassDisplaysFromStageDiv(stageDiv) {
let glassDisplays = [];
const stepMethod = "GLASS_DISPLAY"
stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
let glassDisplayDiv = $(this);
let glassDisplay = {
method: stepMethod,
type: glassDisplayDiv.find(".object-type").val(),
coords: {
x: parseFloat( glassDisplayDiv.find(".coords-x").val() ),
y: parseFloat( glassDisplayDiv.find(".coords-y").val() ),
z: parseFloat( glassDisplayDiv.find(".coords-z").val() ),
},
heading: parseFloat( glassDisplayDiv.find(".heading").val() ),
blipData : glassDisplayDiv.data("blipData") || getDefaultBlipCustomization(),
isOptional: glassDisplayDiv.find(".is-optional-checkbox").prop("checked"),
requiredItem: getRequiredItemFromDiv(glassDisplayDiv),
item: {
type: glassDisplayDiv.find(".item-type").val(),
name: glassDisplayDiv.find(".item-name").val(),
minQuantity: parseInt( glassDisplayDiv.find(".item-min-quantity").val() ),
maxQuantity: parseInt( glassDisplayDiv.find(".item-max-quantity").val() ),
},
}
glassDisplays.push(glassDisplay);
});
return glassDisplays
}
async function addGlassDisplayHeist(stageDiv, glassDisplay) {
let stepDiv = $(`
${getLocalizedText("menu:glass_display")}
`);
stepDiv.find(".choose-coords-and-heading-btn").click(async function() {
let objectType = stepDiv.find(".object-type").val();
let data = await placeObjectAndReturnCoords(objectType);
if(data) {
stepDiv.find(".coords-x").val(data.coords.x);
stepDiv.find(".coords-y").val(data.coords.y);
stepDiv.find(".coords-z").val(data.coords.z);
stepDiv.find(".heading").val(data.heading);
}
}).tooltip();
stepDiv.find(".customize-blip-btn").click(async function() {
let oldBlipData = stepDiv.data("blipData");
let blipData = await blipDialog(oldBlipData)
stepDiv.data("blipData", blipData);
})
stepDiv.find(".delete-step-btn").click(function() {
stepDiv.remove();
})
stepDiv.find(".choose-robbable-item-btn").click(async function() {
let objectType = stepDiv.find(".robbable-item").find(".item-type").val();
let objectName = await objectDialog(objectType);
stepDiv.find(".item-name").val(objectName);
}).tooltip();
stepDiv.find(".choose-item-btn").click(async function() {
let itemName = await itemsDialog();
stepDiv.find(".required-item-name").val(itemName);
})
stepDiv.find(".requires-an-item-checkbox").change(function() {
toggleRequiredItem(stepDiv.find(".required-item-div"));
});
// Sets value if the object is not new
if(glassDisplay) {
stepDiv.find(".object-type").val(glassDisplay.type);
stepDiv.find(".coords-x").val(glassDisplay.coords.x);
stepDiv.find(".coords-y").val(glassDisplay.coords.y);
stepDiv.find(".coords-z").val(glassDisplay.coords.z);
stepDiv.find(".heading").val(glassDisplay.heading);
stepDiv.data("blipData", glassDisplay.blipData);
stepDiv.find(".is-optional-checkbox").prop("checked", glassDisplay.isOptional);
if(glassDisplay.requiredItem) {
stepDiv.find(".required-item-name").val(glassDisplay.requiredItem.name);
stepDiv.find(".required-item-quantity").val(glassDisplay.requiredItem.quantity);
stepDiv.find(".required-item-lose-on-use-checkbox").prop("checked", glassDisplay.requiredItem.loseOnUse);
}
stepDiv.find(".item-type").val(glassDisplay.item.type);
stepDiv.find(".item-name").val(glassDisplay.item.name);
stepDiv.find(".item-min-quantity").val(glassDisplay.item.minQuantity);
stepDiv.find(".item-max-quantity").val(glassDisplay.item.maxQuantity);
}
stepDiv.find(".requires-an-item-checkbox").prop("checked", glassDisplay?.requiredItem ? true : false).change();
stageDiv.find(".steps-list").append(stepDiv);
}
/*
██ █████ ███████ ███████ ██████ ██████ ██████ ██ ██ ██ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ███████ ███████ █████ ██████ ██ ██ ██████ ██ ██ ██ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
███████ ██ ██ ███████ ███████ ██ ██ ██████ ██ ██ ██ ███████ ███████ ███████
*/
function getAllLaserDrillsFromStageDiv(stageDiv) {
let laserDrills = [];
const stepMethod = "LASER_DRILL"
stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
let laserDrillDiv = $(this);
let stepData = {
type: "laser_drill", // for the object model
method: stepMethod,
coords: {
x: parseFloat( laserDrillDiv.find(".coords-x").val() ),
y: parseFloat( laserDrillDiv.find(".coords-y").val() ),
z: parseFloat( laserDrillDiv.find(".coords-z").val() ),
},
blipData : laserDrillDiv.data("blipData") || getDefaultBlipCustomization(),
isOptional: laserDrillDiv.find(".is-optional-checkbox").prop("checked"),
duration: parseInt( laserDrillDiv.find(".duration").val() ),
requiredItem: getRequiredItemFromDiv( laserDrillDiv ),
minObjectsAmount: parseInt( laserDrillDiv.find(".min-objects-amount").val() ),
maxObjectsAmount: parseInt( laserDrillDiv.find(".max-objects-amount").val() ),
items: getAllItemsFromDiv(laserDrillDiv),
}
laserDrills.push(stepData);
});
return laserDrills
}
async function addLaserDrillHeist(stageDiv, laserDrill) {
let stepDiv = $(`
${getLocalizedText("menu:laser_drill")}
`);
stepDiv.find(".delete-step-btn").click(function() { stepDiv.remove(); })
stepDiv.find(".choose-coords-and-heading-btn").click(async function() {
let objectType = stepDiv.find(".object-type").val();
let data = await placeObjectAndReturnCoords(objectType);
if(data) {
stepDiv.find(".coords-x").val(data.coords.x);
stepDiv.find(".coords-y").val(data.coords.y);
stepDiv.find(".coords-z").val(data.coords.z);
stepDiv.find(".heading").val(data.heading);
}
}).tooltip();
stepDiv.find(".customize-blip-btn").click(async function() {
let oldBlipData = stepDiv.data("blipData");
let blipData = await blipDialog(oldBlipData)
stepDiv.data("blipData", blipData);
})
stepDiv.find(".requires-an-item-checkbox").change(function() {
toggleRequiredItem(stepDiv.find(".required-item-div"));
});
stepDiv.find(".choose-item-btn").click(async function() {
let itemName = await itemsDialog();
stepDiv.find(".required-item-name").val(itemName);
})
stepDiv.find(".add-item-btn").click(function() {
addItemInStep(stepDiv);
})
// Sets value if the object is not new
if(laserDrill) {
stepDiv.find(".coords-x").val(laserDrill.coords.x);
stepDiv.find(".coords-y").val(laserDrill.coords.y);
stepDiv.find(".coords-z").val(laserDrill.coords.z);
stepDiv.data("blipData", laserDrill.blipData);
stepDiv.find(".is-optional-checkbox").prop("checked", laserDrill.isOptional);
stepDiv.find(".duration").val(laserDrill.duration);
if(laserDrill.requiredItem) {
stepDiv.find(".required-item-name").val(laserDrill.requiredItem.name);
stepDiv.find(".required-item-quantity").val(laserDrill.requiredItem.quantity);
stepDiv.find(".required-item-lose-on-use-checkbox").prop("checked", laserDrill.requiredItem.loseOnUse);
}
stepDiv.find(".min-objects-amount").val(laserDrill.minObjectsAmount);
stepDiv.find(".max-objects-amount").val(laserDrill.maxObjectsAmount);
if(laserDrill.items) {
laserDrill.items.forEach(item => {
addItemInStep(stepDiv, item);
});
}
} else {
stepDiv.find(".min-objects-amount").val(1);
stepDiv.find(".max-objects-amount").val(1);
addItemInStep(stepDiv);
}
stepDiv.find(".requires-an-item-checkbox").prop("checked", laserDrill?.requiredItem ? true : false).change();
stageDiv.find(".steps-list").append(stepDiv);
}
/*
██████ ██ ██ █████ ██████ ██████ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ███ ██ ██ ███████ ██████ ██ ██ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ██████ ██ ██ ██ ██ ██████ ███████
*/
function getGuardsFromGuardsDiv(guardsDiv) {
let guards = [];
guardsDiv.find(".guard").each(function() {
let guardDiv = $(this);
let guard = {
model: guardDiv.find(".guard-model").val(),
weapon: guardDiv.find(".guard-weapon").val(),
coords: {
x: parseFloat( guardDiv.find(".coords-x").val() ),
y: parseFloat( guardDiv.find(".coords-y").val() ),
z: parseFloat( guardDiv.find(".coords-z").val() ),
},
heading: parseFloat( guardDiv.find(".heading").val() ),
}
guards.push(guard);
});
return guards;
}
function getAllGuardsFromStageDiv(stageDiv) {
let guards = [];
const stepMethod = "GUARDS"
stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
let guardsDiv = $(this);
let currentGuards = {
method: stepMethod,
difficulty: parseInt( guardsDiv.find(".difficulty").val() ),
headshotOneshot: guardsDiv.find(".headshot-oneshot-checkbox").prop("checked"),
hideBlip: guardsDiv.find(".hide-blip-checkbox").prop("checked"),
guardsList: getGuardsFromGuardsDiv(guardsDiv),
}
guards.push(currentGuards);
});
return guards
}
function addGuardToGuardsDiv(guardsDiv, guard) {
let guardDiv = $(`
`);
guardDiv.find(".delete-guard-btn").click(function() {
guardDiv.remove();
});
guardDiv.find(".choose-coords-and-heading-btn").click(async function() {
let data = await placeObjectAndReturnCoords();
if(data) {
guardDiv.find(".coords-x").val(data.coords.x);
guardDiv.find(".coords-y").val(data.coords.y);
guardDiv.find(".coords-z").val(data.coords.z);
guardDiv.find(".heading").val(data.heading);
}
}).tooltip();
if(guard) {
guardDiv.find(".guard-model").val(guard.model);
guardDiv.find(".guard-weapon").val(guard.weapon);
guardDiv.find(".coords-x").val(guard.coords.x);
guardDiv.find(".coords-y").val(guard.coords.y);
guardDiv.find(".coords-z").val(guard.coords.z);
guardDiv.find(".heading").val(guard.heading);
}
guardsDiv.find(".guards-list").append(guardDiv);
}
async function addGuardsHeist(stageDiv, guards) {
let guardsDiv = $(`
${getLocalizedText("menu:guards")}
${ getLocalizedText("menu:guards_list") }
`);
guardsDiv.find(".difficulty").tooltip();
guardsDiv.find(".delete-step-btn").click(function() {
guardsDiv.remove();
});
guardsDiv.find(".add-guard-btn").click(function() {
addGuardToGuardsDiv(guardsDiv);
});
if(guards) {
guardsDiv.find(".difficulty").val(guards.difficulty);
guardsDiv.find(".headshot-oneshot-checkbox").prop("checked", guards.headshotOneshot);
guardsDiv.find(".hide-blip-checkbox").prop("checked", guards.hideBlip);
if(guards.guardsList) {
guards.guardsList.forEach(guard => {
addGuardToGuardsDiv(guardsDiv, guard);
});
}
}
stageDiv.find(".steps-list").append(guardsDiv);
}
/*
███████ █████ ███████ ███████ ███████
██ ██ ██ ██ ██ ██
███████ ███████ █████ █████ ███████
██ ██ ██ ██ ██ ██
███████ ██ ██ ██ ███████ ███████
*/
function getAllSafesFromStageDiv(stageDiv) {
let safes = [];
const stepMethod = "SAFE"
stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
let safeDiv = $(this);
let safe = {
method: stepMethod,
type: safeDiv.find(".object-type").val(),
coords: {
x: parseFloat( safeDiv.find(".coords-x").val() ),
y: parseFloat( safeDiv.find(".coords-y").val() ),
z: parseFloat( safeDiv.find(".coords-z").val() ),
},
heading: parseFloat( safeDiv.find(".heading").val() ),
blipData : safeDiv.data("blipData") || getDefaultBlipCustomization(),
items: getAllItemsFromDiv(safeDiv),
difficulty: parseInt( safeDiv.find(".difficulty").val() ),
minObjectsAmount: parseInt( safeDiv.find(".min-objects-amount").val() ),
maxObjectsAmount: parseInt( safeDiv.find(".max-objects-amount").val() ),
isOptional: safeDiv.find(".is-optional-checkbox").prop("checked"),
isInvisible: safeDiv.find(".is-invisible-checkbox").prop("checked"),
requiredItem: getRequiredItemFromDiv( safeDiv ),
alarms: safeDiv.data("alarms")
}
safes.push(safe);
});
return safes
}
function addSafeHeist(stageDiv, safe) {
let safeDiv = $(`
${getLocalizedText("menu:safe")}
`);
safeDiv.find(".setup-alarm-btn").click(async function() {
let currentAlarms = safeDiv.data("alarms");
let alarms = await alarmsDialog(currentAlarms);
safeDiv.data("alarms", alarms);
}).tooltip();
safeDiv.find(".requires-an-item-checkbox").change(function() {
toggleRequiredItem(safeDiv.find(".required-item-div"));
});
safeDiv.find(".choose-item-btn").click(async function() {
let itemName = await itemsDialog();
safeDiv.find(".required-item-name").val(itemName);
})
safeDiv.find(".add-item-btn").click(function() {
addItemInStep(safeDiv);
})
safeDiv.find(".choose-coords-and-heading-btn").click(async function() {
let objectType = safeDiv.find(".object-type").val();
let data = await placeObjectAndReturnCoords(objectType);
if(data) {
safeDiv.find(".coords-x").val(data.coords.x);
safeDiv.find(".coords-y").val(data.coords.y);
safeDiv.find(".coords-z").val(data.coords.z);
safeDiv.find(".heading").val(data.heading);
}
}).tooltip();
safeDiv.find(".customize-blip-btn").click(async function() {
let oldBlipData = safeDiv.data("blipData");
let blipData = await blipDialog(oldBlipData)
safeDiv.data("blipData", blipData);
})
safeDiv.find(".delete-step-btn").click(function() {
safeDiv.remove();
})
// Sets value if the object is not new
if(safe) {
safeDiv.find(".object-type").val(safe.type);
safeDiv.find(".coords-x").val(safe.coords.x);
safeDiv.find(".coords-y").val(safe.coords.y);
safeDiv.find(".coords-z").val(safe.coords.z);
safeDiv.find(".heading").val(safe.heading);
safeDiv.find(".difficulty").val(safe.difficulty);
safeDiv.find(".min-objects-amount").val(safe.minObjectsAmount);
safeDiv.find(".max-objects-amount").val(safe.maxObjectsAmount);
safeDiv.find(".is-optional-checkbox").prop("checked", safe.isOptional);
safeDiv.find(".is-invisible-checkbox").prop("checked", safe.isInvisible);
safeDiv.data("blipData", safe.blipData);
safeDiv.data("alarms", safe.alarms);
safe.items.forEach(item => {
addItemInStep(safeDiv, item);
})
if(safe.requiredItem) {
safeDiv.find(".required-item-name").val(safe.requiredItem.name);
safeDiv.find(".required-item-quantity").val(safe.requiredItem.quantity);
safeDiv.find(".required-item-lose-on-use-checkbox").prop("checked", safe.requiredItem.loseOnUse);
}
} else {
// If safe didn't exist, it will also add 1 empty item to it
addItemInStep(safeDiv);
}
safeDiv.find(".requires-an-item-checkbox").prop("checked", safe?.requiredItem ? true : false).change();
stageDiv.find(".steps-list").append(safeDiv);
}
/*
██ ██ █████ ██████ ██ ██ █████ ██████ ██ ███████ ██████ █████ ███ ██ ███████ ██ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██
███████ ███████ ██ █████ ███████ ██████ ██ █████ ██████ ███████ ██ ██ ██ █████ ██ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██████ ██ ██ ██ ██ ██████ ███████ ███████ ██ ██ ██ ██ ████ ███████ ███████ ███████
*/
function getAllHackablePanelsFromStageDiv(stageDiv) {
let hackablePanels = [];
const stepMethod = "HACKABLE_PANEL"
stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
let hackablePanelDiv = $(this);
let hackablePanel = {
type: "hackable_panel", // for the object model
method: stepMethod,
coords: {
x: parseFloat( hackablePanelDiv.find(".coords-x").val() ),
y: parseFloat( hackablePanelDiv.find(".coords-y").val() ),
z: parseFloat( hackablePanelDiv.find(".coords-z").val() ),
},
heading: parseFloat( hackablePanelDiv.find(".heading").val() ),
blipData : hackablePanelDiv.data("blipData") || getDefaultBlipCustomization(),
isOptional: hackablePanelDiv.find(".is-optional-checkbox").prop("checked"),
difficulty: parseInt( hackablePanelDiv.find(".difficulty").val() ),
isInvisible: hackablePanelDiv.find(".is-invisible-checkbox").prop("checked"),
doorsToOpen: hackablePanelDiv.data("doorsToOpen"),
alarms: hackablePanelDiv.data("alarms"),
requiredItem: getRequiredItemFromDiv( hackablePanelDiv ),
minigameToUse: hackablePanelDiv.find(".minigame-to-use").val()
}
hackablePanels.push(hackablePanel);
});
return hackablePanels
}
function addHackablePanelHeist(stageDiv, hackablePanel) {
let hackablePanelDiv = $(`
${getLocalizedText("menu:hackable_panel")}
`);
hackablePanelDiv.find(".delete-step-btn").click(function() {
hackablePanelDiv.remove();
})
hackablePanelDiv.find(".difficulty").tooltip();
hackablePanelDiv.find(".requires-an-item-checkbox").change(function() {
toggleRequiredItem(hackablePanelDiv.find(".required-item-div"));
});
hackablePanelDiv.find(".choose-item-btn").click(async function() {
let itemName = await itemsDialog();
hackablePanelDiv.find(".required-item-name").val(itemName);
})
hackablePanelDiv.find(".choose-coords-and-heading-btn").click(async function() {
let data = await placeObjectAndReturnCoords("hackable_panel");
if(data) {
hackablePanelDiv.find(".coords-x").val(data.coords.x);
hackablePanelDiv.find(".coords-y").val(data.coords.y);
hackablePanelDiv.find(".coords-z").val(data.coords.z);
hackablePanelDiv.find(".heading").val(data.heading);
}
}).tooltip();
hackablePanelDiv.find(".setup-alarm-btn").click(async function() {
let currentAlarms = hackablePanelDiv.data("alarms");
let alarms = await alarmsDialog(currentAlarms);
hackablePanelDiv.data("alarms", alarms);
}).tooltip();
hackablePanelDiv.find(".customize-blip-btn").click(async function() {
let oldBlipData = hackablePanelDiv.data("blipData");
let blipData = await blipDialog(oldBlipData)
hackablePanelDiv.data("blipData", blipData);
})
hackablePanelDiv.find(".choose-doors-btn").click(async function() {
let doors = await doorsDialog( hackablePanelDiv.data("doorsToOpen") );
hackablePanelDiv.data("doorsToOpen", doors);
})
// Sets value if the object is not new
if(hackablePanel) {
hackablePanelDiv.find(".coords-x").val(hackablePanel.coords.x);
hackablePanelDiv.find(".coords-y").val(hackablePanel.coords.y);
hackablePanelDiv.find(".coords-z").val(hackablePanel.coords.z);
hackablePanelDiv.find(".heading").val(hackablePanel.heading);
hackablePanelDiv.find(".difficulty").val(hackablePanel.difficulty);
hackablePanelDiv.find(".is-optional-checkbox").prop("checked", hackablePanel.isOptional);
hackablePanelDiv.find(".is-invisible-checkbox").prop("checked", hackablePanel.isInvisible);
hackablePanelDiv.data("blipData", hackablePanel.blipData);
hackablePanelDiv.data("doorsToOpen", hackablePanel.doorsToOpen);
hackablePanelDiv.data("alarms", hackablePanel.alarms);
hackablePanelDiv.find(".minigame-to-use").val(hackablePanel.minigameToUse || "bruteforce");
if(hackablePanel.requiredItem) {
hackablePanelDiv.find(".required-item-name").val(hackablePanel.requiredItem.name);
hackablePanelDiv.find(".required-item-quantity").val(hackablePanel.requiredItem.quantity);
hackablePanelDiv.find(".required-item-lose-on-use-checkbox").prop("checked", hackablePanel.requiredItem.loseOnUse);
}
}
hackablePanelDiv.find(".requires-an-item-checkbox").prop("checked", hackablePanel?.requiredItem ? true : false).change();
stageDiv.find(".steps-list").append(hackablePanelDiv);
}
/*
████████ ██ ██ ███████ ██████ ███ ███ █████ ██ ██████ ██ ██ █████ ██████ ██████ ███████ ███████
██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ███████ █████ ██████ ██ ████ ██ ███████ ██ ██ ███████ ███████ ██████ ██ ███ █████ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ███████ ██████ ██ ██ ██ ██ ██ ██ ██████ ███████ ███████
*/
function getAllThermalChargesFromStageDiv(stageDiv) {
let thermalCharges = [];
const stepMethod = "THERMAL_CHARGE"
stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
let thermalChargeDiv = $(this);
let thermalCharge = {
type: "thermal_charge", // for the object model
method: stepMethod,
coords: {
x: parseFloat( thermalChargeDiv.find(".coords-x").val() ),
y: parseFloat( thermalChargeDiv.find(".coords-y").val() ),
z: parseFloat( thermalChargeDiv.find(".coords-z").val() ),
},
heading: parseFloat( thermalChargeDiv.find(".heading").val() ),
blipData : thermalChargeDiv.data("blipData") || getDefaultBlipCustomization(),
isOptional: thermalChargeDiv.find(".is-optional-checkbox").prop("checked"),
doorsToOpen: thermalChargeDiv.data("doorsToOpen"),
duration: parseInt( thermalChargeDiv.find(".duration").val() ),
requiredItem: getRequiredItemFromDiv(thermalChargeDiv),
alarms: thermalChargeDiv.data("alarms"),
}
thermalCharges.push(thermalCharge);
});
return thermalCharges
}
function addThermalChargeHeist(stageDiv, thermalCharge) {
let thermalChargeHeistDiv = $(`
${getLocalizedText("menu:thermal_charge")}
`);
thermalChargeHeistDiv.find(".setup-alarm-btn").click(async function() {
let currentAlarms = thermalChargeHeistDiv.data("alarms");
let alarms = await alarmsDialog(currentAlarms);
thermalChargeHeistDiv.data("alarms", alarms);
}).tooltip();
thermalChargeHeistDiv.find(".requires-an-item-checkbox").change(function() {
toggleRequiredItem(thermalChargeHeistDiv.find(".required-item-div"));
});
thermalChargeHeistDiv.find(".choose-item-btn").click(async function() {
let itemName = await itemsDialog();
thermalChargeHeistDiv.find(".required-item-name").val(itemName);
})
thermalChargeHeistDiv.find(".choose-coords-and-heading-btn").click(async function() {
let data = await placeObjectAndReturnCoords("thermal_charge");
if(data) {
thermalChargeHeistDiv.find(".coords-x").val(data.coords.x);
thermalChargeHeistDiv.find(".coords-y").val(data.coords.y);
thermalChargeHeistDiv.find(".coords-z").val(data.coords.z);
thermalChargeHeistDiv.find(".heading").val(data.heading);
}
}).tooltip();
thermalChargeHeistDiv.find(".customize-blip-btn").click(async function() {
let oldBlipData = thermalChargeHeistDiv.data("blipData");
let blipData = await blipDialog(oldBlipData);
thermalChargeHeistDiv.data("blipData", blipData);
});
thermalChargeHeistDiv.find(".choose-doors-btn").click(async function() {
let doors = await doorsDialog( thermalChargeHeistDiv.data("doorsToOpen") );
thermalChargeHeistDiv.data("doorsToOpen", doors);
});
thermalChargeHeistDiv.find(".delete-step-btn").click(function() {
thermalChargeHeistDiv.remove();
});
if(thermalCharge) {
thermalChargeHeistDiv.find(".coords-x").val(thermalCharge.coords.x);
thermalChargeHeistDiv.find(".coords-y").val(thermalCharge.coords.y);
thermalChargeHeistDiv.find(".coords-z").val(thermalCharge.coords.z);
thermalChargeHeistDiv.find(".heading").val(thermalCharge.heading);
thermalChargeHeistDiv.find(".is-optional-checkbox").prop("checked", thermalCharge.isOptional);
thermalChargeHeistDiv.find(".duration").val(thermalCharge.duration);
thermalChargeHeistDiv.data("blipData", thermalCharge.blipData);
thermalChargeHeistDiv.data("doorsToOpen", thermalCharge.doorsToOpen);
thermalChargeHeistDiv.data("alarms", thermalCharge.alarms);
if(thermalCharge.requiredItem) {
thermalChargeHeistDiv.find(".required-item-name").val(thermalCharge.requiredItem.name);
thermalChargeHeistDiv.find(".required-item-quantity").val(thermalCharge.requiredItem.quantity);
thermalChargeHeistDiv.find(".required-item-lose-on-use-checkbox").prop("checked", thermalCharge.requiredItem.loseOnUse);
}
}
thermalChargeHeistDiv.find(".requires-an-item-checkbox").prop("checked", thermalCharge?.requiredItem ? true : false).change();
stageDiv.find(".steps-list").append(thermalChargeHeistDiv);
}
/*
██ ██████ ██████ ██ ██ ██████ ██ ██████ ██ ██ █████ ██████ ██ ███████ ██████ ██████ ██████ ██████ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ █████ ██████ ██ ██ █████ ███████ ██████ ██ █████ ██ ██ ██ ██ ██ ██ ██████ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
███████ ██████ ██████ ██ ██ ██ ██ ██████ ██ ██ ██ ██ ██████ ███████ ███████ ██████ ██████ ██████ ██ ██ ███████
*/
function getAllLockpickableDoorsFromStageDiv(stageDiv) {
let lockpickableDoors = [];
const stepMethod = "LOCKPICKABLE_DOOR"
stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
let lockpickableDoorDiv = $(this);
let lockpickableDoor = {
type: "lockpickable_door", // for the object model
method: stepMethod,
coords: {
x: parseFloat( lockpickableDoorDiv.find(".coords-x").val() ),
y: parseFloat( lockpickableDoorDiv.find(".coords-y").val() ),
z: parseFloat( lockpickableDoorDiv.find(".coords-z").val() ),
},
blipData : lockpickableDoorDiv.data("blipData") || getDefaultBlipCustomization(),
isOptional: lockpickableDoorDiv.find(".is-optional-checkbox").prop("checked"),
doorsToOpen: lockpickableDoorDiv.data("doorsToOpen"),
difficulty: parseInt( lockpickableDoorDiv.find(".difficulty").val() ),
requiredItem: getRequiredItemFromDiv(lockpickableDoorDiv),
alarms: lockpickableDoorDiv.data("alarms"),
}
lockpickableDoors.push(lockpickableDoor);
});
return lockpickableDoors
}
function addLockpickableDoorHeist(stageDiv, lockpickableDoor) {
let lockpickableDoorHeistDiv = $(`
${getLocalizedText("menu:lockpickable_door")}
`);
lockpickableDoorHeistDiv.find(".setup-alarm-btn").click(async function() {
let currentAlarms = lockpickableDoorHeistDiv.data("alarms");
let alarms = await alarmsDialog(currentAlarms);
lockpickableDoorHeistDiv.data("alarms", alarms);
}).tooltip();
lockpickableDoorHeistDiv.find(".requires-an-item-checkbox").change(function() {
toggleRequiredItem(lockpickableDoorHeistDiv.find(".required-item-div"));
});
lockpickableDoorHeistDiv.find(".choose-item-btn").click(async function() {
let itemName = await itemsDialog();
lockpickableDoorHeistDiv.find(".required-item-name").val(itemName);
})
lockpickableDoorHeistDiv.find(".choose-coords-and-heading-btn").click(async function() {
let data = await placeObjectAndReturnCoords();
if(data) {
lockpickableDoorHeistDiv.find(".coords-x").val(data.coords.x);
lockpickableDoorHeistDiv.find(".coords-y").val(data.coords.y);
lockpickableDoorHeistDiv.find(".coords-z").val(data.coords.z);
}
}).tooltip();
lockpickableDoorHeistDiv.find(".customize-blip-btn").click(async function() {
let oldBlipData = lockpickableDoorHeistDiv.data("blipData");
let blipData = await blipDialog(oldBlipData)
lockpickableDoorHeistDiv.data("blipData", blipData);
});
lockpickableDoorHeistDiv.find(".choose-doors-btn").click(async function() {
let doors = await doorsDialog( lockpickableDoorHeistDiv.data("doorsToOpen") );
lockpickableDoorHeistDiv.data("doorsToOpen", doors);
});
lockpickableDoorHeistDiv.find(".delete-step-btn").click(function() {
lockpickableDoorHeistDiv.remove();
});
if(lockpickableDoor) {
lockpickableDoorHeistDiv.find(".coords-x").val(lockpickableDoor.coords.x);
lockpickableDoorHeistDiv.find(".coords-y").val(lockpickableDoor.coords.y);
lockpickableDoorHeistDiv.find(".coords-z").val(lockpickableDoor.coords.z);
lockpickableDoorHeistDiv.find(".is-optional-checkbox").prop("checked", lockpickableDoor.isOptional);
lockpickableDoorHeistDiv.find(".difficulty").val(lockpickableDoor.difficulty);
lockpickableDoorHeistDiv.data("blipData", lockpickableDoor.blipData);
lockpickableDoorHeistDiv.data("doorsToOpen", lockpickableDoor.doorsToOpen);
lockpickableDoorHeistDiv.data("alarms", lockpickableDoor.alarms);
if(lockpickableDoor.requiredItem) {
lockpickableDoorHeistDiv.find(".required-item-name").val(lockpickableDoor.requiredItem.name);
lockpickableDoorHeistDiv.find(".required-item-quantity").val(lockpickableDoor.requiredItem.quantity);
lockpickableDoorHeistDiv.find(".required-item-lose-on-use-checkbox").prop("checked", lockpickableDoor.requiredItem.loseOnUse);
}
}
lockpickableDoorHeistDiv.find(".requires-an-item-checkbox").prop("checked", lockpickableDoor?.requiredItem ? true : false).change();
stageDiv.find(".steps-list").append(lockpickableDoorHeistDiv);
}
/*
██ ███████ ████████ ██ ██ █████ ██ ██████ █████ ███████ ███████ ███████ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ █████ ██ ███████ ███████ ██ ██ ███ ███████ ███████ ███████ █████ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
███████ ███████ ██ ██ ██ ██ ██ ███████ ██████ ██ ██ ███████ ███████ ███████ ███████
*/
function getAllLethalGassesFromStageDiv(stageDiv) {
let lethalGases = [];
const stepMethod = "LETHAL_GAS"
stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
let lethalGasDiv = $(this);
let lethalGas = {
type: "lethal_gas", // for the object model
method: stepMethod,
gasPoints: lethalGasDiv.data("gasPoints"),
duration: parseInt( lethalGasDiv.find(".duration").val() ),
damage: parseInt( lethalGasDiv.find(".damage").val() ),
secondsBeforeStart: parseInt( lethalGasDiv.find(".seconds-before-start").val() ),
color: hexToRgb( lethalGasDiv.find(".gas-color").val() ),
}
lethalGases.push(lethalGas);
});
return lethalGases
}
function addLethalGasHeist(stageDiv, lethalGas) {
let lethalGasHeistDiv = $(`
${getLocalizedText("menu:lethal_gas")}
`);
lethalGasHeistDiv.find(".seconds-before-start").tooltip()
lethalGasHeistDiv.find(".gas-color").tooltip()
lethalGasHeistDiv.find(".choose-gas-points").click(async function() {
let gasPoints = await placeGasPoints();
lethalGasHeistDiv.data("gasPoints", gasPoints);
}).tooltip();
lethalGasHeistDiv.find(".delete-step-btn").click(function() {
lethalGasHeistDiv.remove();
});
if(lethalGas) {
lethalGasHeistDiv.find(".duration").val(lethalGas.duration);
lethalGasHeistDiv.data("gasPoints", lethalGas.gasPoints);
lethalGasHeistDiv.find(".damage").val(lethalGas.damage);
lethalGasHeistDiv.find(".seconds-before-start").val(lethalGas.secondsBeforeStart);
lethalGasHeistDiv.find(".gas-color").val( rgbToHex(lethalGas.color.r, lethalGas.color.g, lethalGas.color.b) );
}
stageDiv.find(".steps-list").append(lethalGasHeistDiv);
}
/*
███████ ███████ █████ ██████ ██████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██
███████ █████ ███████ ██████ ██ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██
███████ ███████ ██ ██ ██ ██ ██████ ██ ██
*/
function getPossiblePointsFromSearchPointsDiv(searchPointsDiv) {
let possiblePoints = [];
searchPointsDiv.find(".point").each(function() {
let pointDiv = $(this);
let possiblePoint = {
coords: {
x: parseFloat( pointDiv.find(".coords-x").val() ),
y: parseFloat( pointDiv.find(".coords-y").val() ),
z: parseFloat( pointDiv.find(".coords-z").val() ),
},
}
possiblePoints.push(possiblePoint);
});
return possiblePoints;
}
function getAllSearchPointsFromStageDiv(stageDiv) {
let searchPoints = [];
const stepMethod = "SEARCH_POINTS"
stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
let searchPointslDiv = $(this);
let stepData = {
type: "search_points", // for the object model
method: stepMethod,
possiblePoints: getPossiblePointsFromSearchPointsDiv(searchPointslDiv),
blipData : searchPointslDiv.data("blipData") || getDefaultBlipCustomization(),
isOptional: searchPointslDiv.find(".is-optional-checkbox").prop("checked"),
minObjectsAmount: parseInt( searchPointslDiv.find(".min-objects-amount").val() ),
maxObjectsAmount: parseInt( searchPointslDiv.find(".max-objects-amount").val() ),
items: getAllItemsFromDiv(searchPointslDiv),
}
searchPoints.push(stepData);
});
return searchPoints
}
function addPossiblePointToSearchPointsDiv(searchPointsDiv, possiblePoint) {
let possiblePointDiv = $(`
`);
possiblePointDiv.find(".delete-point-btn").click(function() {
possiblePointDiv.remove();
});
possiblePointDiv.find(".choose-coords-and-heading-btn").click(async function() {
let coords = await chooseCoords();
if(coords) {
possiblePointDiv.find(".coords-x").val(coords.x);
possiblePointDiv.find(".coords-y").val(coords.y);
possiblePointDiv.find(".coords-z").val(coords.z);
}
}).tooltip();
if(possiblePoint) {
possiblePointDiv.find(".coords-x").val(possiblePoint.coords.x);
possiblePointDiv.find(".coords-y").val(possiblePoint.coords.y);
possiblePointDiv.find(".coords-z").val(possiblePoint.coords.z);
}
searchPointsDiv.find(".possible-points-list").append(possiblePointDiv);
}
async function addSearchPointsHeist(stageDiv, searchPoints) {
let stepDiv = $(`
${getLocalizedText("menu:search_points")}
${getLocalizedText("menu:possible_points")}
`);
stepDiv.find(".delete-step-btn").click(function() { stepDiv.remove(); })
stepDiv.find(".choose-coords-and-heading-btn").click(async function() {
let objectType = stepDiv.find(".object-type").val();
let data = await placeObjectAndReturnCoords(objectType);
if(data) {
stepDiv.find(".coords-x").val(data.coords.x);
stepDiv.find(".coords-y").val(data.coords.y);
stepDiv.find(".coords-z").val(data.coords.z);
}
}).tooltip();
stepDiv.find(".customize-blip-btn").click(async function() {
let oldBlipData = stepDiv.data("blipData");
let blipData = await blipDialog(oldBlipData)
stepDiv.data("blipData", blipData);
})
stepDiv.find(".add-item-btn").click(function() {
addItemInStep(stepDiv);
})
stepDiv.find(".add-point-btn").click(function() {
addPossiblePointToSearchPointsDiv(stepDiv);
})
// Sets value if the object is not new
if(searchPoints) {
stepDiv.data("blipData", searchPoints.blipData);
stepDiv.find(".is-optional-checkbox").prop("checked", searchPoints.isOptional);
stepDiv.find(".min-objects-amount").val(searchPoints.minObjectsAmount);
stepDiv.find(".max-objects-amount").val(searchPoints.maxObjectsAmount);
if(searchPoints.items) {
searchPoints.items.forEach(item => {
addItemInStep(stepDiv, item);
});
}
if(searchPoints.possiblePoints) {
searchPoints.possiblePoints.forEach(possiblePoint => {
addPossiblePointToSearchPointsDiv(stepDiv, possiblePoint);
});
}
} else {
stepDiv.find(".min-objects-amount").val(1);
stepDiv.find(".max-objects-amount").val(1);
addItemInStep(stepDiv);
addPossiblePointToSearchPointsDiv(stepDiv);
}
stageDiv.find(".steps-list").append(stepDiv);
}
/*
██ ██ ███████ ██ ███████ ████████ ███████ ███ ███ ██ ███████ ██████
██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██
███████ █████ ██ ███████ ██ ███████ ██ ████ ██ ██ ███████ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ███████ ██ ███████ ██ ███████ ██ ██ ██ ███████ ██████
*/
function getAllItemsFromDiv(stepDiv) {
let items = [];
stepDiv.find(".item").each(function() {
let itemDiv = $(this);
let item = {
type: itemDiv.find(".item-type").val(),
name: itemDiv.find(".item-name").val(),
minQuantity: parseInt( itemDiv.find(".item-min-quantity").val() ),
maxQuantity: parseInt( itemDiv.find(".item-max-quantity").val() ),
chances: parseInt( itemDiv.find(".item-chances").val() ),
}
items.push(item);
});
return items
}
function getRequiredItemFromDiv(stepDiv) {
const requiredItemDiv = stepDiv.find(".required-item-div");
let requiresItem = requiredItemDiv.find(".requires-an-item-checkbox").prop("checked");
if(requiresItem) {
return {
name: requiredItemDiv.find(".required-item-name").val(),
quantity: parseInt( requiredItemDiv.find(".required-item-quantity").val() ),
loseOnUse: requiredItemDiv.find(".required-item-lose-on-use-checkbox").prop("checked"),
}
} else {
return null;
}
}
function getHeistStages() {
let stages = [];
$("#heist-stages").find(".stage").each(function(index) {
let stageDiv = $(this);
let stage = {
steps: [
...getAllRobbableObjectsFromStageDiv(stageDiv),
...getAllSafesFromStageDiv(stageDiv),
...getAllPaintingsFromStageDiv(stageDiv),
...getAllGlassDisplaysFromStageDiv(stageDiv),
...getAllLaserDrillsFromStageDiv(stageDiv),
...getAllGuardsFromStageDiv(stageDiv),
...getAllHackablePanelsFromStageDiv(stageDiv),
...getAllThermalChargesFromStageDiv(stageDiv),
...getAllLockpickableDoorsFromStageDiv(stageDiv),
...getAllLethalGassesFromStageDiv(stageDiv),
...getAllSearchPointsFromStageDiv(stageDiv),
],
}
stages.push(stage);
})
return stages
}
$("#heist-form").submit(async function(event) {
if(isThereAnyErrorInForm(event)) return;
let heistModal = $("#heist-modal");
let action = heistModal.data("action");
let heistData = {
label: $("#heist-label").val(),
minimumPolice: parseInt( $("#heist-minimum-police").val() ),
timeoutMinutes: parseInt( $("#heist-timeout").val() ),
minutesBeforeReset: parseInt( $("#heist-reset").val() ),
timeLimitMinutes: parseInt( $("#heist-time-limit-minutes").val() ),
stages: getHeistStages()
}
switch(action) {
case "create": {
const response = await $.post(`https://${resName}/createHeist`, JSON.stringify(heistData));
showServerResponse(response)
if(response === true) {
heistModal.modal("hide");
loadHeists();
}
break;
}
case "edit": {
const response = await $.post(`https://${resName}/updateHeist`, JSON.stringify({heistId: heistModal.data("heistId"), heistData: heistData}));
showServerResponse(response)
if(response === true) {
heistModal.modal("hide");
loadHeists();
}
break;
}
}
});
$("#delete-heist-btn").click(async function() {
if(!await confirmDeletion()) return;
let heistModal = $("#heist-modal");
let heistId = heistModal.data("heistId");
const response = await $.post(`https://${resName}/deleteHeist`, JSON.stringify({heistId: heistId}));
showServerResponse(response);
if(response === true) {
heistModal.modal("hide");
loadHeists();
}
})
async function addItemInStep(stepDiv, item) {
let itemDiv = $(`
`);
itemDiv.find(".delete-item-btn").click(function() {
itemDiv.remove();
})
itemDiv.find(".choose-item-btn").click(async function() {
let objectType = itemDiv.find(".item-type").val();
let objectName = await objectDialog(objectType);
itemDiv.find(".item-name").val(objectName);
}).tooltip();
if(item) {
itemDiv.find(".item-type").val(item.type);
itemDiv.find(".item-name").val(item.name);
itemDiv.find(".item-min-quantity").val(item.minQuantity);
itemDiv.find(".item-max-quantity").val(item.maxQuantity);
itemDiv.find(".item-chances").val(item.chances);
}
stepDiv.find(".items-list").append(itemDiv);
}
function toggleRequiredItem(requiredItemDiv) {
let enabled = requiredItemDiv.find(".requires-an-item-checkbox").prop("checked");
requiredItemDiv.find(".required-item-name").prop("disabled", !enabled).prop("required", enabled);
requiredItemDiv.find(".required-item-quantity").prop("disabled", !enabled).prop("required", enabled);
requiredItemDiv.find(".choose-item-btn").prop("disabled", !enabled);
requiredItemDiv.find(".required-item-lose-on-use-checkbox").prop("disabled", !enabled);
if(!enabled) {
requiredItemDiv.find(".required-item-lose-on-use-checkbox").prop("checked", false);
}
}
function renameAllStagesByTheirOrder() {
$("#heist-stages").find(".stage-title").each(function(index) {
let stageNumber = index + 1;
$(this).prop("innerHTML", `${ getLocalizedText("menu:stage") } ${stageNumber}`)
})
$("#heist-stages").find(".delete-stage-btn").each(function(index) {
let stageNumber = index + 1;
$(this).prop("innerHTML", `${ getLocalizedText("menu:delete_stage") } - ${stageNumber}`)
})
}
function setDoorsCreatorStatusOnStageDiv(stageDiv) {
if(hasDoorsCreator) return;
let stepsRequiringDoorsCreator = {
"HACKABLE_PANEL": true,
"THERMAL_CHARGE": true,
"LOCKPICKABLE_DOOR": true,
}
stageDiv.find("[data-step-method]").each(function() {
let stepMethod = $(this).data("stepMethod");
if(stepsRequiringDoorsCreator[stepMethod]) {
$(this)
.addClass("disabled")
.parent()
.data("bs-toggle", "tooltip")
.data("bs-placement", "top")
.prop("title", getLocalizedText("menu:requires_doors_creator"))
.tooltip();
}
});
}
// If it's the first stage, the steps guards and lethal gas won't be usable
function setAvailableStepsForEachStage() {
$("#heist-stages").find(".stage").each(function() {
let stageNumber = $(this).index() + 1;
const isFirstStage = stageNumber == 1;
const guardDiv = $(this).find(".steps-methods-list").find("[data-step-method='GUARDS']");
const lethalGasDiv = $(this).find(".steps-methods-list").find("[data-step-method='LETHAL_GAS']");
guardDiv.toggleClass("disabled", isFirstStage);
lethalGasDiv.toggleClass("disabled", isFirstStage);
// Enable tooltip only if it's first stage
if(isFirstStage) {
$(this).find(".not-first-stage-list-element").tooltip("enable");
} else {
$(this).find(".not-first-stage-list-element").tooltip("disable");
}
})
}
function addStepDiv(stepMethod, stageDiv, stepData) {
switch(stepMethod) {
case "ROBBABLE_OBJECT":
addRobbableObjectHeist(stageDiv, stepData);
break;
case "SAFE":
addSafeHeist(stageDiv, stepData);
break;
case "PAINTING":
addPaintingHeist(stageDiv, stepData);
break;
case "GLASS_DISPLAY":
addGlassDisplayHeist(stageDiv, stepData);
break;
case "LASER_DRILL":
addLaserDrillHeist(stageDiv, stepData);
break;
case "GUARDS":
addGuardsHeist(stageDiv, stepData);
break;
case "HACKABLE_PANEL":
addHackablePanelHeist(stageDiv, stepData);
break;
case "THERMAL_CHARGE":
addThermalChargeHeist(stageDiv, stepData);
break;
case "LOCKPICKABLE_DOOR":
addLockpickableDoorHeist(stageDiv, stepData);
break;
case "LETHAL_GAS":
addLethalGasHeist(stageDiv, stepData);
break;
case "SEARCH_POINTS": {
addSearchPointsHeist(stageDiv, stepData);
break;
}
default:
console.log("Unknown step method: " + stepMethod);
break;
}
}
function addHeistStage(stage) {
let stageDiv = $(`
${ getLocalizedText("menu:stage") } ${$("#heist-stages").children(".stage").length + 1}
`)
stageDiv.find(".delete-stage-btn").click(function() {
stageDiv.remove();
renameAllStagesByTheirOrder();
setAvailableStepsForEachStage();
})
stageDiv.find(".steps-methods-list a").click(function() {
let stepMethod = $(this).data("stepMethod");
addStepDiv(stepMethod, stageDiv);
})
if(stage) {
stage.steps.forEach(stepData => {
addStepDiv(stepData.method, stageDiv, stepData);
});
}
setDoorsCreatorStatusOnStageDiv(stageDiv);
$("#heist-stages").append(stageDiv);
setAvailableStepsForEachStage();
}
$("#heist-add-stage-btn").click(function() {
addHeistStage()
})
$("#heist-import-stages-from-heist-btn").click(async function() {
let heistId = await heistsDialog()
if(heistId) {
heists[heistId].stages.forEach(stage => {
addHeistStage(stage);
})
}
})
/*
██████ █████ ██████ ██████ ██████ ██████ ██████ ██████ ██████ ███████ ██████ ██ ███████ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ███████ ██████ ██ ███ ██ ██ ██████ ██ ██ ██████ ██████ █████ ██████ ██ █████ ███████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ██ ██ ██ ██ ██████ ██████ ██ ██ ██████ ██████ ██████ ███████ ██ ██ ██ ███████ ███████
*/
let cargoRobberiesDatatable = $("#cargo-robberies-container").DataTable( {
"lengthMenu": [10, 15, 20],
"createdRow": function ( row, data, index ) {
$(row).addClass("clickable");
$(row).click(function() {
let id = parseInt( data[0] );
editCargoRobbery(id);
})
},
});
let cargoRobberies = {};
function loadCargoRobberies() {
$.post(`https://${resName}/getAllCargoRobberies`, {}, async function(rawCargoRobberies) {
// Manually create the table to avoid incompatibilities due table indexing
cargoRobberies = {};
for(const[k, heistData] of Object.entries(rawCargoRobberies)) {
cargoRobberies[heistData.id] = heistData;
}
cargoRobberiesDatatable.clear();
for(const[id, heistData] of Object.entries(cargoRobberies)) {
cargoRobberiesDatatable.row.add([
id,
heistData.label,
]);
}
cargoRobberiesDatatable.draw();
})
}
function getDefaultBlipCargoRobbery() {
return {
isEnabled: true,
sprite: 67,
label: getLocalizedText("menu:armored_truck"),
scale: 0.8,
color: 1,
display: 2,
}
}
function setDefaultDataOfCargoRobberyModal() {
$("#cargo-robbery-label").val("Default");
$("#cargo-robbery-vehicle-model").val("stockade");
$("#cargo-robbery-driver-model").val("mp_m_securoguard_01");
$("#cargo-robbery-max-speed").val(50.0);
$("#cargo-robbery-seconds-to-drill").val(20);
$(`input[name=cargo-drive-type][value='stops']`).prop("checked", true).change();
$("#cargo-robbery-random-drive-coords-x").val("");
$("#cargo-robbery-random-drive-coords-y").val("");
$("#cargo-robbery-random-drive-coords-z").val("");
$("#cargo-robbery-random-drive-heading").val("");
$("#cargo-robbery-random-drive-minutes-before-despawn").val(30);
$("#cargo-robbery-min-objects-quantity").val(1);
$("#cargo-robbery-max-objects-quantity").val(2);
$("#cargo-items-list").empty();
$("#cargo-robbery-stops").empty();
$("#cargo-robbery-modal").data("blipData", getDefaultBlipCargoRobbery());
}
function renameAllStopsByTheirOrder() {
$("#cargo-robbery-stops").find(".stop-title").each(function(index) {
let stopNumber = index + 1;
$(this).prop("innerHTML", `${ getLocalizedText("menu:stop") } ${stopNumber}`)
})
$("#cargo-robbery-stops").find(".delete-stop-btn").each(function(index) {
let stopNumber = index + 1;
$(this).prop("innerHTML", `${ getLocalizedText("menu:delete_stop") } - ${stopNumber}`)
})
}
$("#new-cargo-robbery-btn").click(function() {
let cargoRobberyModal = $("#cargo-robbery-modal");
// Converts from edit modal to create modal
cargoRobberyModal.data("action", "create");
$("#delete-cargo-robbery-btn").hide();
$("#save-cargo-robbery-btn").text( getLocalizedText("menu:create") );
setDefaultDataOfCargoRobberyModal();
cargoRobberyModal.modal("show");
})
function editCargoRobbery(id) {
let cargoRobberyData = cargoRobberies[id];
let cargoRobberyModal = $("#cargo-robbery-modal");
cargoRobberyModal.data("action", "edit");
cargoRobberyModal.data("cargoRobberyId", id);
cargoRobberyModal.data("blipData", cargoRobberyData.blipData);
$("#delete-cargo-robbery-btn").show();
$("#save-cargo-robbery-btn").text( getLocalizedText("menu:save") );
$("#cargo-robbery-label").val(cargoRobberyData.label);
$("#cargo-robbery-vehicle-model").val(cargoRobberyData.data.vehicleModel);
$("#cargo-robbery-driver-model").val(cargoRobberyData.data.driverModel);
$("#cargo-robbery-bulletproof-wheels-checkbox").prop("checked", cargoRobberyData.data.hasBulletproofTires);
$("#cargo-robbery-max-speed").val(cargoRobberyData.data.maxSpeed);
$("#cargo-robbery-seconds-to-drill").val(cargoRobberyData.data.secondsToDrill)
$(`input[name=cargo-drive-type][value='${cargoRobberyData.data.driveType}']`).prop("checked", true).change();
$("#cargo-robbery-random-drive-coords-x").val(cargoRobberyData.data.startCoords?.x);
$("#cargo-robbery-random-drive-coords-y").val(cargoRobberyData.data.startCoords?.y);
$("#cargo-robbery-random-drive-coords-z").val(cargoRobberyData.data.startCoords?.z);
$("#cargo-robbery-random-drive-heading").val(cargoRobberyData.data.startCoords?.heading);
$("#cargo-robbery-random-drive-minutes-before-despawn").val(cargoRobberyData.data.minutesBeforeDespawn);
$("#cargo-robbery-min-objects-quantity").val(cargoRobberyData.data.minObjectsAmount);
$("#cargo-robbery-max-objects-quantity").val(cargoRobberyData.data.maxObjectsAmount);
$("#cargo-items-list").empty();
if(cargoRobberyData.data.rewardObjects) {
cargoRobberyData.data.rewardObjects.forEach(rewardObject => {
addObjectInCargoRobbery(rewardObject);
})
}
$("#cargo-robbery-stops").empty();
if(cargoRobberyData.data.stops) {
cargoRobberyData.data.stops.forEach(stop => {
addCargoRobberyStop(stop);
})
}
cargoRobberyModal.modal("show");
}
function addCargoRobberyStop(stopData) {
let stopIndex = $("#cargo-robbery-stops").children(".stop").length + 1;
let stopDiv = $(`
${ getLocalizedText("menu:stop") } ${stopIndex}
${ getLocalizedText("menu:destination") }
`)
stopDiv.find(".current-coords-btn").click(async function() {
let data = await getCurrentCoordsAndHeading();
stopDiv.find(".coords-x").val(data.coords.x);
stopDiv.find(".coords-y").val(data.coords.y);
stopDiv.find(".coords-z").val(data.coords.z);
stopDiv.find(".heading").val(data.heading);
}).tooltip();
stopDiv.find(".delete-stop-btn").click(function() {
stopDiv.remove();
renameAllStopsByTheirOrder();
});
if(stopData) {
stopDiv.find(".coords-x").val(stopData.coords.x);
stopDiv.find(".coords-y").val(stopData.coords.y);
stopDiv.find(".coords-z").val(stopData.coords.z);
stopDiv.find(".heading").val(stopData.heading);
stopDiv.find(".minutes-to-wait").val(stopData.minutesToWait);
}
$("#cargo-robbery-stops").append(stopDiv);
}
$("#cargo-robbery-add-stop-btn").click(function() {
addCargoRobberyStop()
})
$('input[name=cargo-drive-type]').change(function() {
let type = $(this).val();
switch(type) {
case "stops": {
$("#cargo-robbery-stops-container").show().find(".form-control").prop("required", true);
$("#cargo-robbery-random-drive-container").hide().find(".form-control").prop("required", false);
break;
}
case "random": {
$("#cargo-robbery-stops-container").hide().find(".form-control").prop("required", false);
$("#cargo-robbery-random-drive-container").show().find(".form-control").prop("required", true);
break;
}
}
});
$("#cargo-robbery-random-drive-current-coords-btn").click(async function() {
let data = await getCurrentCoordsAndHeading();
$("#cargo-robbery-random-drive-coords-x").val(data.coords.x);
$("#cargo-robbery-random-drive-coords-y").val(data.coords.y);
$("#cargo-robbery-random-drive-coords-z").val(data.coords.z);
$("#cargo-robbery-random-drive-heading").val(data.heading);
})
function getCargoRobberyStops() {
let stops = [];
$("#cargo-robbery-stops").find(".stop").each(function() {
let stop = {
coords: {
x: parseFloat( $(this).find(".coords-x").val() ),
y: parseFloat( $(this).find(".coords-y").val() ),
z: parseFloat( $(this).find(".coords-z").val() ),
},
heading: parseFloat( $(this).find(".heading").val() ),
minutesToWait: parseInt( $(this).find(".minutes-to-wait").val() ),
};
stops.push(stop);
});
return stops;
}
function getCargoRobberyRewards() {
let rewards = [];
$("#cargo-items-list").find(".cargo-robbery-item").each(function() {
let currentObject = $(this);
let reward = {
type: currentObject.find(".object-type").val(),
name: currentObject.find(".object-name").val(),
minQuantity: parseInt(currentObject.find(".min-quantity").val()),
maxQuantity: parseInt(currentObject.find(".max-quantity").val()),
chances: parseInt(currentObject.find(".chances").val())
}
rewards.push(reward);
})
return rewards;
}
$("#cargo-robbery-form").submit(async function(event) {
if(isThereAnyErrorInForm(event)) return;
let cargoRobberyModal = $("#cargo-robbery-modal");
let action = cargoRobberyModal.data("action");
let cargoRobberyData = {
label: $("#cargo-robbery-label").val(),
blipData: cargoRobberyModal.data("blipData"),
data: {
vehicleModel: $("#cargo-robbery-vehicle-model").val(),
driverModel: $("#cargo-robbery-driver-model").val(),
hasBulletproofTires: $("#cargo-robbery-bulletproof-wheels-checkbox").prop("checked"),
maxSpeed: parseFloat( $("#cargo-robbery-max-speed").val() ),
secondsToDrill: parseInt( $("#cargo-robbery-seconds-to-drill").val() ),
minObjectsAmount: parseInt( $("#cargo-robbery-min-objects-quantity").val() ),
maxObjectsAmount: parseInt( $("#cargo-robbery-max-objects-quantity").val() ),
rewardObjects: getCargoRobberyRewards(),
stops: getCargoRobberyStops(),
driveType: $('input[name=cargo-drive-type]:checked').val(),
startCoords: {
x: parseFloat( $("#cargo-robbery-random-drive-coords-x").val() ),
y: parseFloat( $("#cargo-robbery-random-drive-coords-y").val() ),
z: parseFloat( $("#cargo-robbery-random-drive-coords-z").val() ),
heading: parseFloat( $("#cargo-robbery-random-drive-heading").val() ),
},
minutesBeforeDespawn: parseInt( $("#cargo-robbery-random-drive-minutes-before-despawn").val() )
}
}
switch(action) {
case "create": {
const response = await $.post(`https://${resName}/createCargoRobbery`, JSON.stringify(cargoRobberyData));
showServerResponse(response);
if(response === true) {
cargoRobberyModal.modal("hide");
loadCargoRobberies();
}
break;
}
case "edit": {
const response = await $.post(`https://${resName}/updateCargoRobbery`, JSON.stringify({cargoRobberyId: cargoRobberyModal.data("cargoRobberyId"), cargoRobberyData: cargoRobberyData}));
showServerResponse(response);
if(response === true) {
cargoRobberyModal.modal("hide");
loadCargoRobberies();
}
break;
}
}
});
$("#delete-cargo-robbery-btn").click(async function() {
if(!await confirmDeletion()) return;
let cargoRobberyModal = $("#cargo-robbery-modal");
let cargoRobberyId = cargoRobberyModal.data("cargoRobberyId");
const response = await $.post(`https://${resName}/deleteCargoRobbery`, JSON.stringify({cargoRobberyId: cargoRobberyId}));
showServerResponse(response);
if(response === true) {
cargoRobberyModal.modal("hide");
loadCargoRobberies();
}
})
$("#cargo-robbery-customize-blip-btn").click(async function() {
oldBlipData = $("#cargo-robbery-modal").data("blipData");
let blipData = await blipDialog(oldBlipData)
$("#cargo-robbery-modal").data("blipData", blipData);
});
async function addObjectInCargoRobbery(objectData) {
let objectDiv = $(`
`);
objectDiv.find(".delete-cargo-robbery-item-btn").click(function() {
objectDiv.remove();
})
objectDiv.find(".choose-item-btn").click(async function() {
let objectType = objectDiv.find(".object-type").val();
let objectName = await objectDialog(objectType);
objectDiv.find(".object-name").val(objectName);
}).tooltip();
if(objectData) {
objectDiv.find(".object-type").val(objectData.type);
objectDiv.find(".object-name").val(objectData.name);
objectDiv.find(".min-quantity").val(objectData.minQuantity);
objectDiv.find(".max-quantity").val(objectData.maxQuantity);
objectDiv.find(".chances").val(objectData.chances);
}
$("#cargo-items-list").append(objectDiv);
}
$("#cargo-robbery-add-item-btn").click(function() {
addObjectInCargoRobbery();
});
/*
██████ ██ █████ ███ ██ ███ ██ ██ ███ ██ ██████
██ ██ ██ ██ ██ ████ ██ ████ ██ ██ ████ ██ ██
██████ ██ ███████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ███████ ██ ██ ██ ████ ██ ████ ██ ██ ████ ██████
*/
let planningsDatatable = $("#plannings-container").DataTable( {
"lengthMenu": [10, 15, 20],
"createdRow": function ( row, data, index ) {
$(row).addClass("clickable");
$(row).click(function() {
let id = parseInt( data[0] );
editPlanning(id);
})
},
});
let plannings = {};
function getRemainingTimeForPlannings() {
return new Promise((resolve, reject) => {
$.post(`https://${resName}/getRemainingTimeForPlannings`, function(remainingTimes) {
resolve(remainingTimes)
});
})
}
async function loadPlannings() {
let remainingTimes = await getRemainingTimeForPlannings();
$.post(`https://${resName}/getAllPlannings`, {}, async function(rawPlannings) {
// Manually create the table to avoid incompatibilities due table indexing
plannings = {};
for(const[k, planningData] of Object.entries(rawPlannings)) {
plannings[planningData.id] = planningData;
}
planningsDatatable.clear();
for(const[id, planningData] of Object.entries(plannings)) {
planningsDatatable.row.add([
id,
planningData.label,
cargoRobberies[planningData.cargoRobberyId]?.label || "None",
remainingTimes[id] || "?",
]);
}
planningsDatatable.draw();
})
}
function planningSetTargetRobbery(robberyId = null) {
let planningModal = $("#planning-modal");
planningModal.data("cargoRobberyId", robberyId);
if(robberyId) {
let robberyLabel = cargoRobberies[robberyId].label;
$("#planning-target-robbery").val( robberyLabel );
} else {
$("#planning-target-robbery").val( getLocalizedText("menu:none") );
}
}
function setDefaultDataOfPlanningModal() {
$("#planning-label").val("Default");
$("#planning-min-police").val(0);
planningSetTargetRobbery(null);
$("#planning-date-type").val("now").change();
}
$("#new-planning-btn").click(function() {
let planningModal = $("#planning-modal");
// Converts from edit modal to create modal
planningModal.data("action", "create");
$("#delete-planning-btn").hide();
$("#save-planning-btn").text( getLocalizedText("menu:create") );
setDefaultDataOfPlanningModal();
planningModal.modal("show");
})
$("#planning-choose-robbery-btn").click(async function() {
let robberyId = await cargoRobberiesDialog();
planningSetTargetRobbery(robberyId);
});
function togglePlanningDateType() {
let val = $("#planning-date-type").val();
if(val == "date") {
$("#planning-date").show().prop("required", true);
} else {
$("#planning-date").hide().prop("required", false);
}
if(val == "interval") {
$("#planning-interval-div").show();
$("#planning-interval").prop("required", true);
} else {
$("#planning-interval-div").hide();
$("#planning-interval").prop("required", false);
}
}
$("#planning-date-type").change(togglePlanningDateType);
function getUnixFromDate(date) {
return Math.floor(new Date(date).getTime() / 1000 )
}
function getDateFromUnix(unix) {
const padL = (nr, len = 2, chr = `0`) => `${nr}`.padStart(2, chr);
let date = new Date(unix * 1000)
let formatted = `${
date.getFullYear()}-${
padL(date.getMonth()+1)}-${
padL(date.getDate())}T${
padL(date.getHours())}:${
padL(date.getMinutes())}`
return formatted;
}
$("#planning-form").submit(async function(event) {
if(isThereAnyErrorInForm(event)) return;
let planningModal = $("#planning-modal");
let action = planningModal.data("action");
let planningData = {
label: $("#planning-label").val(),
minimumPolice: parseInt($("#planning-min-police").val()),
cargoRobberyId: parseInt(planningModal.data("cargoRobberyId")),
data: {
dateType: $("#planning-date-type").val(),
date: getUnixFromDate( $("#planning-date").val() ),
interval: parseInt( $("#planning-interval").val() ),
}
}
switch(action) {
case "create": {
const response = await $.post(`https://${resName}/createPlanning`, JSON.stringify(planningData));
showServerResponse(response);
if(response === true) {
planningModal.modal("hide");
loadPlannings();
}
break;
}
case "edit": {
const response = await $.post(`https://${resName}/updatePlanning`, JSON.stringify({planningId: planningModal.data("planningId"), planningData: planningData}));
showServerResponse(response);
if(response === true) {
planningModal.modal("hide");
loadPlannings();
}
break;
}
}
})
function editPlanning(id) {
let planningData = plannings[id];
let planningsModal = $("#planning-modal");
planningsModal.data("action", "edit");
planningsModal.data("planningId", id);
$("#delete-planning-btn").show();
$("#save-planning-btn").text( getLocalizedText("menu:save") );
$("#planning-label").val(planningData.label);
$("#planning-min-police").val(planningData.minimumPolice);
planningSetTargetRobbery(planningData.cargoRobberyId);
$("#planning-date-type").val(planningData.data.dateType).change();
$("#planning-date").val( getDateFromUnix(planningData.data.date) );
$("#planning-interval").val(planningData.data.interval);
planningsModal.modal("show");
}
$("#delete-planning-btn").click(async function() {
if(!await confirmDeletion()) return;
let planningModal = $("#planning-modal");
let planningId = planningModal.data("planningId");
const response = await $.post(`https://${resName}/deletePlanning`, JSON.stringify({planningId: planningId}));
showServerResponse(response);
if(response === true) {
planningModal.modal("hide");
loadPlannings();
}
})
/*
███ ██ ██████ ██████ ███ ███ ██ ██ ██████ ██████ ██ ███ ██ ██████
████ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ████ ██ ██
██ ██ ██ ██████ ██ ██ ████ ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ████ ██ ██████ ██ ██ ██████ ██████ ██████ ██ ██ ████ ██████
*/
function toggleNpcMugging() {
let enableNpcMuggingDiv = $("#enable-npc-mugging")
let enable = enableNpcMuggingDiv.prop("checked");
$("#npc-mugging-options").find("input, button").not(enableNpcMuggingDiv).prop("disabled", !enable);
}
$("#enable-npc-mugging").change(toggleNpcMugging);
async function addRobbableObjectNpcMugging(object) {
let robbableObjectDiv = $(`
`)
robbableObjectDiv.find(".choose-btn").click(async function() {
let objectType = robbableObjectDiv.find(".object-type").val();
let objectName = await objectDialog(objectType);
robbableObjectDiv.find(".object-name").val(objectName);
}).tooltip();
robbableObjectDiv.find(".remove-btn").click(function() {
robbableObjectDiv.remove();
})
// Sets the object property if exist
if(object) {
robbableObjectDiv.find(".object-type").val(object.type);
robbableObjectDiv.find(".object-name").val(object.name);
robbableObjectDiv.find(".min-quantity").val(object.minQuantity);
robbableObjectDiv.find(".max-quantity").val(object.maxQuantity);
robbableObjectDiv.find(".chances").val(object.chances);
}
$("#npc-mugging-robbable-items").append(robbableObjectDiv);
}
$("#npc-mugging-add-object").click(function() {
addRobbableObjectNpcMugging();
});
function getNpcMuggingRobbableObjects() {
let robbableObjects = [];
$("#npc-mugging-robbable-items").find(".robbable-object").each(function() {
let currentObject = $(this);
let object = {
type: currentObject.find(".object-type").val(),
name: currentObject.find(".object-name").val(),
minQuantity: parseInt(currentObject.find(".min-quantity").val()),
maxQuantity: parseInt(currentObject.find(".max-quantity").val()),
chances: parseInt(currentObject.find(".chances").val())
}
robbableObjects.push(object);
})
return robbableObjects;
}
function setNpcMuggingRobbableObjects(objects) {
$("#npc-mugging-robbable-items").empty();
objects.forEach(object => {
addRobbableObjectNpcMugging(object);
});
}
$("#npc-mugging").submit(async function(event) {
if(isThereAnyErrorInForm(event)) return;
let clientSettings = {
clNpcMugging: {
canMakeFollow: $("#can-make-npc-to-follow").prop("checked"),
cooldownOnMugging: parseInt( $("#npc-mugging-cooldown-on-mugging").val() )
}
}
let sharedSettings = {
isNpcMuggingEnabled: $("#enable-npc-mugging").prop("checked"),
secondsToMug: parseInt( $("#seconds-to-mug").val() )
}
let serverSettings = {
svNpcMugging: {
robbableObjects: getNpcMuggingRobbableObjects(),
minimumPolice: parseInt( $("#npc-mugging-minimum-police").val() ),
probabilityPoliceAlert: parseInt( $("#npc-mugging-probability-police-alert").val() ),
minimumObjectsAmount: parseInt( $("#npc-mugging-minimum-objects-amount").val() ),
maximumObjectsAmount: parseInt( $("#npc-mugging-maximum-objects-amount").val() ),
minutesAfterAutomaticallyRunAway: parseInt( $("#minutes-before-automatically-run-away").val() ),
maxNPCsFollowing: parseInt( $("#npc-mugging-max-npcs-following").val() )
}
}
const response = await $.post(`https://${resName}/saveSettings`, JSON.stringify({
clientSettings: clientSettings,
sharedSettings: sharedSettings,
serverSettings: serverSettings,
}));
showServerResponse(response);
});