Files
The_Bitburner_Scripts/Mizzajl/home/test3.js

332 lines
13 KiB
JavaScript

/** @param {NS} ns */
export async function main(ns) {
ns.disableLog('sleep'); // Disable log spamming
const doc = eval("document");
const body = doc.body;
let isMinimized = false; // Track the minimize state
// Read crime stats from the CrimeStats.txt file
let crimeStatsData = await ns.read("CrimeStats.txt");
let crimes = JSON.parse(crimeStatsData); // Parse the JSON data from the file
// Add dynamic crime chance and calculate stats per second
for (let crime of crimes) {
crime.chance = ns.singularity.getCrimeChance(crime.name) * 100; // Get success chance as a number
let timeInSeconds = crime.time / 1000; // Convert ms to seconds for calculations
// Calculate stats per second
crime.hacking_exp_per_sec = crime.hacking_exp / timeInSeconds;
crime.strength_exp_per_sec = crime.strength_exp / timeInSeconds;
crime.defense_exp_per_sec = crime.defense_exp / timeInSeconds;
crime.dexterity_exp_per_sec = crime.dexterity_exp / timeInSeconds;
crime.agility_exp_per_sec = crime.agility_exp / timeInSeconds;
crime.charisma_exp_per_sec = crime.charisma_exp / timeInSeconds;
crime.intelligence_exp_per_sec = crime.intelligence_exp / timeInSeconds || 0; // Handle potential missing data
crime.money_per_sec = crime.money / timeInSeconds;
crime.karma_per_sec = crime.karma / timeInSeconds;
crime.money_per_chance = crime.money * (crime.chance / 100) / timeInSeconds; // Money per chance per second
}
// Function to map value to a color between green and red
function getGradientColor(value, min, max, isReversed = false) {
if (min === max) return "rgb(0,255,0)"; // Return green if all values are the same
let normalized = (value - min) / (max - min);
if (isReversed) normalized = 1 - normalized; // Reverse the color gradient for columns where lower values are better
let r = Math.round(255 * (1 - normalized)); // Red for lower values
let g = Math.round(255 * normalized); // Green for higher values
return `rgb(${r},${g},0)`; // Return color from green to red
}
// Create the CSS window container
let container = doc.createElement("div");
container.style = `
position: fixed;
top: 100px;
left: 100px;
background: black; /* Black background */
opacity: 0.9; /* 50% transparency */
color: #0c0; /* Green text */
z-index: 1000;
font-family: "Source Code Pro", monospace; /* Apply the terminal font */
font-size: 16px;
border: 2px solid #666;
border-radius: 5px;
width: 1400px; /* Increased width */
height: 400px; /* Increased height */
overflow-y: auto;
`;
body.appendChild(container);
// Create the title bar with minimize and close buttons
let titleBar = doc.createElement("div");
titleBar.style = `
display: flex;
justify-content: space-between;
align-items: center;
background: black; /* Black background for title bar */
color: #0c0; /* Green text */
border-bottom: 1px solid #666;
font-weight: bold;
`;
container.appendChild(titleBar);
// Title text
let title = doc.createElement("span");
title.innerText = "Crime Stats Monitor";
titleBar.appendChild(title);
// Create a container for the buttons and align them to the right
let buttonContainer = doc.createElement("div");
buttonContainer.style = `
display: flex;
justify-content: flex-end;
`;
titleBar.appendChild(buttonContainer);
// Minimize button (▲ for minimize, ▼ for restore)
let minimizeButton = doc.createElement("button");
minimizeButton.innerText = "▲"; // Start as minimize button
minimizeButton.style = `
background: none;
border: 1px solid #666;
color: #0c0; /* Green text */
cursor: pointer;
font-weight: bold;
padding: 5px;
width: 30px; /* Set same width for buttons */
`;
buttonContainer.appendChild(minimizeButton);
minimizeButton.addEventListener("click", () => {
if (isMinimized) {
minimizeButton.innerText = "▲"; // Minimize arrow
container.style.height = "400px"; // Restore height
} else {
minimizeButton.innerText = "▼"; // Restore arrow
container.style.height = "25px"; // Minimize height
}
isMinimized = !isMinimized;
});
// Close button (X)
let closeButton = doc.createElement("button");
closeButton.innerText = "X";
closeButton.style = `
background: none;
border: 1px solid #666;
color: #0c0; /* Green text */
cursor: pointer;
font-weight: bold;
padding: 5px;
width: 30px; /* Set same width for buttons */
`;
buttonContainer.appendChild(closeButton);
closeButton.addEventListener("click", () => {
container.remove(); // Close the window
});
// Table for displaying the crime stats
let table = doc.createElement("table");
table.style = `
width: 100%;
border-collapse: collapse;
color: #0c0; /* Green text */
`;
container.appendChild(table);
// Track the current sort direction for each column (null, 'asc', or 'desc')
let sortOrder = new Array(13).fill(null); // One for each column
// Add table headers with clickable sorting functionality and arrows
let headers = [
"Crime", "Time (s)", "Chance", "Hack Exp/s", "Str Exp/s", "Def Exp/s",
"Dex Exp/s", "Agi Exp/s", "Cha Exp/s", "Money/s", "Karma/s", "$/%/s", "IntXP/s"
];
let headerRow = doc.createElement("tr");
headers.forEach((header, index) => {
let th = doc.createElement("th");
th.innerHTML = `${header} <span></span>`; // Span for the arrow
th.style = `
padding: 5px;
background: #333;
border: 1px solid #666;
cursor: pointer;
white-space: nowrap; /* Prevent wrapping */
`;
// Add sorting functionality for each column
th.addEventListener("click", () => {
clearAllArrows(); // Remove all arrows
sortCrimesByColumn(index); // Sort by this column
toggleSortOrder(index); // Toggle sort order for this column
});
headerRow.appendChild(th);
});
table.appendChild(headerRow);
// Function to toggle the sort order for a column
function toggleSortOrder(columnIndex) {
// Toggle between 'asc' and 'desc'
if (sortOrder[columnIndex] === 'asc') {
sortOrder[columnIndex] = 'desc';
} else {
sortOrder[columnIndex] = 'asc';
}
// Update the arrows for this column
updateArrow(columnIndex);
}
// Function to update the arrow for a specific column
function updateArrow(columnIndex) {
let th = headerRow.querySelectorAll("th")[columnIndex];
let arrowSpan = th.querySelector("span");
if (sortOrder[columnIndex] === 'asc') {
arrowSpan.innerHTML = " ▲"; // Up arrow for ascending
} else if (sortOrder[columnIndex] === 'desc') {
arrowSpan.innerHTML = " ▼"; // Down arrow for descending
}
}
// Function to clear all arrows
function clearAllArrows() {
headerRow.querySelectorAll("th span").forEach(arrow => {
arrow.innerHTML = ""; // Remove all arrows
});
}
// Function to format numbers with 4 decimal places
function formatNumber(num) {
return parseFloat(num.toFixed(4)); // Convert to number with 4 decimal places
}
// Function to display crimes in the table
function displayCrimes(crimesList) {
// Clear any existing rows (except the header)
while (table.rows.length > 1) {
table.deleteRow(1);
}
// Find min and max for each column (for color gradient)
const columnsWithValues = [
{ key: "time", reverseGradient: true },
{ key: "chance", reverseGradient: false },
{ key: "hacking_exp_per_sec", reverseGradient: false },
{ key: "strength_exp_per_sec", reverseGradient: false },
{ key: "defense_exp_per_sec", reverseGradient: false },
{ key: "dexterity_exp_per_sec", reverseGradient: false },
{ key: "agility_exp_per_sec", reverseGradient: false },
{ key: "charisma_exp_per_sec", reverseGradient: false },
{ key: "money_per_sec", reverseGradient: false },
{ key: "karma_per_sec", reverseGradient: false },
{ key: "money_per_chance", reverseGradient: false },
{ key: "intelligence_exp_per_sec", reverseGradient: false }
];
let minMax = {};
// Get min and max values for each column, excluding non-data elements
columnsWithValues.forEach(column => {
let values = crimesList.map(crime => crime[column.key]).filter(v => !isNaN(v)); // Ensure we're working with numbers
minMax[column.key] = {
min: Math.min(...values),
max: Math.max(...values)
};
});
crimesList.forEach(crime => {
let row = doc.createElement("tr");
let columns = [
{ text: crime.name, value: crime.name, isName: true }, // Crime name (always green)
{ text: formatNumber(crime.time / 1000), value: crime.time / 1000, key: "time" }, // Time in seconds
{ text: formatNumber(crime.chance), value: crime.chance, key: "chance" }, // Success chance
{ text: formatNumber(crime.hacking_exp_per_sec), value: crime.hacking_exp_per_sec, key: "hacking_exp_per_sec" },
{ text: formatNumber(crime.strength_exp_per_sec), value: crime.strength_exp_per_sec, key: "strength_exp_per_sec" },
{ text: formatNumber(crime.defense_exp_per_sec), value: crime.defense_exp_per_sec, key: "defense_exp_per_sec" },
{ text: formatNumber(crime.dexterity_exp_per_sec), value: crime.dexterity_exp_per_sec, key: "dexterity_exp_per_sec" },
{ text: formatNumber(crime.agility_exp_per_sec), value: crime.agility_exp_per_sec, key: "agility_exp_per_sec" },
{ text: formatNumber(crime.charisma_exp_per_sec), value: crime.charisma_exp_per_sec, key: "charisma_exp_per_sec" },
{ text: formatNumber(crime.money_per_sec), value: crime.money_per_sec, key: "money_per_sec" },
{ text: formatNumber(crime.karma_per_sec), value: crime.karma_per_sec, key: "karma_per_sec" },
{ text: formatNumber(crime.money_per_chance), value: crime.money_per_chance, key: "money_per_chance" }, // Money per chance per second
{ text: formatNumber(crime.intelligence_exp_per_sec), value: crime.intelligence_exp_per_sec, key: "intelligence_exp_per_sec" } // Intelligence XP per second
];
columns.forEach((col, index) => {
let td = doc.createElement("td");
td.innerText = col.text; // Display formatted text
td.dataset.value = col.value; // Store the numeric value for sorting
// Apply the color gradient to each cell based on the column
let gradientColor = col.isName
? "#0c0" // Always green for names
: getGradientColor(col.value, minMax[col.key].min, minMax[col.key].max, columnsWithValues[index]?.reverseGradient);
td.style.color = gradientColor; // Apply color
td.style.padding = "5px";
td.style.borderBottom = "1px solid #666";
td.style.textAlign = "center";
td.style.whiteSpace = "nowrap"; // Prevent text wrapping
row.appendChild(td);
});
table.appendChild(row);
});
}
// Sorting function (high to low for numeric values)
function sortCrimesByColumn(columnIndex) {
let rows = Array.from(table.querySelectorAll('tr:nth-child(n+2)')); // Get all rows except header
let order = sortOrder[columnIndex]; // Ascending or descending
rows.sort((rowA, rowB) => {
let valA = rowA.cells[columnIndex].dataset.value;
let valB = rowB.cells[columnIndex].dataset.value;
// Check if values are numeric and sort accordingly
if (!isNaN(valA) && !isNaN(valB)) {
return order === 'asc' ? parseFloat(valA) - parseFloat(valB) : parseFloat(valB) - parseFloat(valA);
}
// If not numeric, sort alphabetically
return order === 'asc' ? String(valA).localeCompare(String(valB)) : String(valB).localeCompare(String(valA));
});
// Append rows in sorted order
rows.forEach(row => table.appendChild(row));
}
// Display the crimes initially
displayCrimes(crimes);
// Make the window draggable
let isDragging = false;
let offsetX, offsetY;
titleBar.addEventListener("mousedown", (e) => {
isDragging = true;
offsetX = e.clientX - container.getBoundingClientRect().left;
offsetY = e.clientY - container.getBoundingClientRect().top;
});
doc.addEventListener("mousemove", (e) => {
if (isDragging) {
container.style.left = `${e.clientX - offsetX}px`;
container.style.top = `${e.clientY - offsetY}px`;
}
});
doc.addEventListener("mouseup", () => {
isDragging = false;
});
}