// ==UserScript==
// @name Cartel Empire - Job & Expedition Progress Bars
// @namespace http://tampermonkey.net/
// @version 1.8.2
// @description Adds progress bars for jobs and expeditions
// @author Baccy
// @match https://cartelempire.online/*
// @icon https://cartelempire.online/images/icon-white.png
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
// Below are examples of how to enter colors in the user settings as well as a few formats
let exampleOption1 = 'white';
let exampleOption2 = '#fff';
let exampleOption3 = 'rgb(255, 255, 255)';
// USER SETTINGS
let disableExpeditionProgressBar = false; // Change to true to disable a type of progress bar
let disableJobProgressBar = false;
let flashColorOn100Percent = ''; // Enter a color for a bar to alternate between it and the fill color every second at 100%
let percentageDecimal = 0; // Change the number from 0 if you want the percentages to have decimals
let expeditionBarFillColor = ''; // The color of the completed width of a progress bar
let expeditionBarTextColor = ''; // The color for the completion % text
let expeditionBarBackgroundColor = ''; // The background color for the progress bar
let expeditionTextShadowColor = ''; // Slight white glow the default bars have. Enter 'none' to remove it or another color to change it
let jobBarFillColor = '';
let jobBarTextColor = '';
let jobBarBackgroundColor = '';
let jobTextShadowColor = '';
let progressBarsFirst = false; // Changing this to true will put the bars before the reputation bar. This may look better on smaller window sizes
// USER SETTINGS END
(function() {
'use strict';
let jobData = {};
let expeditionData = {};
let jobFlashing = false;
let expeditionFlashing = {};
init();
async function init() {
if (window.location.href.toLowerCase().includes('cartelempire.online/jobs')) {
setInterval(processJobs, 1000);
} else if (window.location.href.toLowerCase().includes('cartelempire.online/expedition')) {
setInterval(processExpeditions, 1000);
}
let barElement;
if (progressBarsFirst) barElement = document.querySelector('.col-4.col-lg-2');
else barElement = document.querySelector('.row.justify-content-center.mb-1.text-center.gy-2');
if (barElement) {
await loadData();
if (!disableJobProgressBar && Object.keys(jobData).length > 0) {
const fillColor = jobBarFillColor || '#7c4acf';
const textColor = jobBarTextColor || '#3a3a3a';
const backgroundColor = jobBarBackgroundColor || '#d7d7d7';
const textShadow = jobTextShadowColor || '#ffffff';
const jobProgressBar = buildProgressBar(jobData, fillColor, textColor, backgroundColor, textShadow, percentageDecimal, 'job-progress-bar', 'Your job will be ready at<br>', 'job-progress-bar-fill', 'job-progress-text', 'M6.5 1A1.5 1.5 0 0 0 5 2.5V3H1.5A1.5 1.5 0 0 0 0 4.5v1.384l7.614 2.03a1.5 1.5 0 0 0 .772 0L16 5.884V4.5A1.5 1.5 0 0 0 14.5 3H11v-.5A1.5 1.5 0 0 0 9.5 1h-3zm0 1h3a.5.5 0 0 1 .5.5V3H6v-.5a.5.5 0 0 1 .5-.5z', 'M0 12.5A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5V6.85L8.129 8.947a.5.5 0 0 1-.258 0L0 6.85v5.65z');
if (progressBarsFirst) barElement.parentNode.insertBefore(jobProgressBar, barElement);
else barElement.appendChild(jobProgressBar);
setInterval(() => {
updateProgressBar('job-progress-bar', 'job-progress-text', jobData, percentageDecimal, fillColor, '#7c4acf', 'job', flashColorOn100Percent);
}, 1000);
}
if (!disableExpeditionProgressBar && Object.keys(expeditionData).length > 0) {
const fillColor = expeditionBarFillColor || '#5692e4';
const textColor = expeditionBarTextColor || '#3a3a3a';
const backgroundColor = expeditionBarBackgroundColor || '#d7d7d7';
const textShadow = expeditionTextShadowColor || '#ffffff';
for (let i = 0; i < 3; i++) {
if (expeditionData[i]) {
const expeditionProgressBar = buildProgressBar(expeditionData[i], fillColor, textColor, backgroundColor, textShadow, percentageDecimal, `expedition-progress-bar-${i}`, 'Your sicarios will return at<br>', `expedition-progress-bar-${i}-fill`, `expedition-progress-text-${i}`, 'M2.52 3.515A2.5 2.5 0 0 1 4.82 2h6.362c1 0 1.904.596 2.298 1.515l.792 1.848c.075.175.21.319.38.404.5.25.855.715.965 1.262l.335 1.679c.033.161.049.325.049.49v.413c0 .814-.39 1.543-1 1.997V13.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-1.338c-1.292.048-2.745.088-4 .088s-2.708-.04-4-.088V13.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-1.892c-.61-.454-1-1.183-1-1.997v-.413a2.5 2.5 0 0 1 .049-.49l.335-1.68c.11-.546.465-1.012.964-1.261a.807.807 0 0 0 .381-.404l.792-1.848ZM3 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2Zm10 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM6 8a1 1 0 0 0 0 2h4a1 1 0 1 0 0-2H6ZM2.906 5.189a.51.51 0 0 0 .497.731c.91-.073 3.35-.17 4.597-.17 1.247 0 3.688.097 4.597.17a.51.51 0 0 0 .497-.731l-.956-1.913A.5.5 0 0 0 11.691 3H4.309a.5.5 0 0 0-.447.276L2.906 5.19Z');
if (progressBarsFirst) barElement.parentNode.insertBefore(expeditionProgressBar, barElement);
else barElement.appendChild(expeditionProgressBar);
setInterval(() => {
updateProgressBar(`expedition-progress-bar-${i}`, `expedition-progress-text-${i}`, expeditionData[i], percentageDecimal, fillColor, '#5692e4', 'expedition', flashColorOn100Percent, i);
}, 1000);
}
}
}
setInterval(loadData, 5000);
}
}
async function loadData() {
jobData = await GM_getValue('jobData', {});
expeditionData = await GM_getValue('expeditionData', {});
}
function processJobs() {
const jobCompletionElement = document.getElementById('progressMessage');
if (jobCompletionElement) {
const currentTime = Date.now();
let completionTime = parseInt(jobCompletionElement.getAttribute('data-bs-finishtime'), 10) * 1000;
if (!jobData || !jobData.completion || jobData.completion < currentTime || jobData.completion !== completionTime) {
const jobTypeElement = jobCompletionElement.closest('.row.g-0')?.querySelector('.card-title.text-center.mb-2');
const jobType = jobTypeElement ? jobTypeElement.childNodes[0].nodeValue.trim() : '';
const jobDuration = completionTime - currentTime;
const newJobData = {
completion: completionTime,
duration: jobDuration,
location: jobType
};
if (newJobData.completion > currentTime) {
jobData = newJobData;
GM_setValue('jobData', jobData);
}
}
}
}
function processExpeditions() {
function convertDuration(timeString) {
const timeParts = timeString.split(' ');
let hours = 0, minutes = 0;
timeParts.forEach(part => {
if (part.includes('H')) {
hours = parseInt(part.replace('H', ''), 10);
} else if (part.includes('m')) {
minutes = parseInt(part.replace('m', ''), 10);
}
});
return (hours * 60 * 60 * 1000) + (minutes * 60 * 1000);
}
const completionElements = document.querySelectorAll('.remainingTime');
const locationElements = document.querySelectorAll('.fs-6 > .fst-italic');
const expeditionDurationElements = document.querySelectorAll('.card-text.estimatedTime');
for (let i = 0; i < completionElements.length; i++) {
const completionTime = parseInt(completionElements[i].getAttribute('completion'), 10) * 1000;
const currentTime = Date.now();
if (completionTime > currentTime) {
const locationText = locationElements[i].innerText.trim();
const expeditionDurationString = expeditionDurationElements[i].innerText.trim();
const expeditionDuration = convertDuration(expeditionDurationString);
const newExpeditionData = {
completion: completionTime,
duration: expeditionDuration,
location: locationText
};
if (expeditionData[i]?.completion === newExpeditionData.completion) continue;
expeditionData[i] = newExpeditionData;
GM_setValue('expeditionData', expeditionData);
}
}
}
function buildProgressBar(data, fillColor, textColor, backgroundColor, textShadow, decimalPlace, anchorID, anchorBody, fillId, textId, svgPath1, svgPath2) {
function formatTimeToLPT(completionTimestamp) {
if (completionTimestamp) {
const completionDate = new Date(completionTimestamp);
let utcTime = completionDate.toUTCString().replace('GMT', 'LPT');
return utcTime;
}
}
const currentTimestamp = Date.now();
const remainingTime = data.completion - currentTimestamp;
const utcTime = formatTimeToLPT(data.completion);
const percentageRemaining = (remainingTime / data.duration) * 100;
const progressWidth = Math.min(100 - percentageRemaining, 100);
const progressBar = document.createElement('div');
progressBar.classList.add('col-6', 'col-lg-3');
const anchor = document.createElement('a');
anchor.id = anchorID;
anchor.setAttribute('tabindex', '0');
anchor.setAttribute('role', 'button');
anchor.setAttribute('data-bs-toggle', 'popover');
anchor.setAttribute('data-bs-html', 'true');
anchor.setAttribute('data-bs-trigger', 'hover focus');
anchor.setAttribute('data-bs-placement', 'bottom');
anchor.setAttribute('data-bs-sanitize', 'false');
anchor.setAttribute('data-bs-content', `${anchorBody}${utcTime}`);
anchor.setAttribute('data-bs-original-title', data.location);
const elementRow = document.createElement('div');
elementRow.classList.add('row', 'align-items-center', 'g-2');
const iconElement = document.createElement('div');
iconElement.classList.add('col-auto');
const svgLabel = document.createElement('span');
svgLabel.classList.add('lifeLabel', 'd-flex', 'align-items-center', 'mb-0');
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('class', 'bi bi-battery-full');
svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
svg.setAttribute('width', '16');
svg.setAttribute('height', '16');
svg.setAttribute('fill', 'currentColor');
svg.setAttribute('viewBox', '0 0 16 16');
const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path1.setAttribute('d', svgPath1);
svg.appendChild(path1);
if (svgPath2) {
const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path2.setAttribute('d', svgPath2);
svg.appendChild(path2);
}
svgLabel.appendChild(svg);
iconElement.appendChild(svgLabel);
const barParentElement = document.createElement('div');
barParentElement.classList.add('col');
const barElement = document.createElement('div');
barElement.classList.add('progress', 'progressBarStat');
barElement.style.backgroundColor = backgroundColor;
const barProgressElement = document.createElement('div');
barProgressElement.classList.add('progress-bar', 'progress-bar-striped');
barProgressElement.id = fillId;
barProgressElement.setAttribute('role', 'progressbar');
barProgressElement.style.width = `${progressWidth}%`;
barProgressElement.style.backgroundColor = fillColor;
const barTextParent = document.createElement('div');
barTextParent.classList.add('progress-bar-title', 'fw-bold');
const textLabel = document.createElement('span');
textLabel.textContent = `${progressWidth.toFixed(decimalPlace)}%`;
textLabel.id = textId;
textLabel.style.textShadow = textShadow;
textLabel.style.color = textColor;
barTextParent.appendChild(textLabel);
barElement.appendChild(barProgressElement);
barElement.appendChild(barTextParent);
barParentElement.appendChild(barElement);
elementRow.appendChild(iconElement);
elementRow.appendChild(barParentElement);
anchor.appendChild(elementRow);
progressBar.appendChild(anchor);
return progressBar;
}
function updateProgressBar(progressBarID, textId, data, decimal, fillColor, defaultColor, type, flashingColor, i) {
const progressBarElement = document.querySelector(`#${progressBarID}`);
const progressBar = progressBarElement.querySelector(`#${progressBarID}-fill`);
const progressText = progressBarElement.querySelector(`#${textId}`);
if (!progressBar) return;
const remainingTime = data.completion - Date.now();
const percentageRemaining = (remainingTime / data.duration) * 100;
const percentageComplete = Math.min(100 - percentageRemaining, 100);
if (percentageComplete >= 100 && flashingColor) {
if (type === 'job') {
progressBar.style.setProperty('background-color', jobFlashing ? flashingColor : fillColor || defaultColor, 'important');
jobFlashing = !jobFlashing;
} else {
progressBar.style.setProperty('background-color', expeditionFlashing[i] ? flashingColor : fillColor || defaultColor, 'important');
expeditionFlashing[i] = !expeditionFlashing[i];
}
} else {
progressBar.style.setProperty('background-color', fillColor || defaultColor, 'important');
}
progressBar.style.width = `${percentageComplete}%`;
progressText.textContent = `${percentageComplete.toFixed(decimal)}%`;
}
})();