272 lines
9.5 KiB
JavaScript
272 lines
9.5 KiB
JavaScript
/** @param {NS} ns */
|
|
export async function main(ns) {
|
|
const doc = eval("document");
|
|
|
|
// File to store money data
|
|
const moneyRecordFile = "MoneyRecord.txt";
|
|
const recordInterval = 10000; // Record every 10 seconds
|
|
const graphDuration = 300000; // Last 5 minutes (in milliseconds)
|
|
let isClosed = false; // Track if the window is closed
|
|
|
|
// Function to format numbers as K (thousand), M (million), B (billion), etc.
|
|
function formatLargeNumber(number) {
|
|
if (number >= 1e12) {
|
|
return `$${(number / 1e12).toFixed(3)}t`;
|
|
} else if (number >= 1e9) {
|
|
return `$${(number / 1e9).toFixed(3)}b`;
|
|
} else if (number >= 1e6) {
|
|
return `$${(number / 1e6).toFixed(3)}m`;
|
|
} else if (number >= 1e3) {
|
|
return `$${(number / 1e3).toFixed(3)}k`;
|
|
} else {
|
|
return `$${number.toFixed(2)}`; // Less than 1000, show as a standard number
|
|
}
|
|
}
|
|
|
|
// Remove any existing container if it exists
|
|
const existingContainer = doc.getElementById("money-graph-container");
|
|
if (existingContainer) {
|
|
existingContainer.remove(); // Remove the existing container
|
|
ns.tprint("Previous graph closed.");
|
|
}
|
|
|
|
// Create the container for the graph
|
|
const container = doc.createElement("div");
|
|
container.id = "money-graph-container";
|
|
container.style = `
|
|
position: absolute;
|
|
top: 200px;
|
|
left: 200px;
|
|
width: 600px;
|
|
height: 300px;
|
|
background-color: rgba(0, 0, 0, 0.9); /* Black background with 90% opacity */
|
|
color: #0c0; /* Green text */
|
|
border: 2px solid #666;
|
|
border-radius: 8px;
|
|
padding: 10px;
|
|
font-family: "Source Code Pro", monospace;
|
|
z-index: 1000;
|
|
cursor: move;
|
|
box-shadow: 0px 0px 15px rgba(0, 255, 0, 0.5); /* Glow effect */
|
|
`;
|
|
|
|
// Create a header bar with a close button
|
|
const header = doc.createElement("div");
|
|
header.style = `
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-weight: bold;
|
|
color: #0c0; /* Green text */
|
|
padding: 5px;
|
|
background: rgba(17, 17, 17, 0.9); /* Dark background with 90% opacity */
|
|
border-bottom: 1px solid #666;
|
|
text-shadow: 0px 0px 10px #0f0;
|
|
`;
|
|
header.innerText = "Server Money Graph";
|
|
|
|
// Close button
|
|
const closeButton = doc.createElement("button");
|
|
closeButton.innerText = "X";
|
|
closeButton.style = `
|
|
background: none;
|
|
border: 1px solid #666;
|
|
color: #0c0; /* Green text */
|
|
cursor: pointer;
|
|
padding: 2px 8px;
|
|
font-weight: bold;
|
|
border-radius: 3px;
|
|
margin-left: 10px;
|
|
`;
|
|
closeButton.addEventListener("click", () => {
|
|
container.remove(); // Close the graph container
|
|
ns.tprint("Graph window closed.");
|
|
isClosed = true; // Mark the window as closed
|
|
});
|
|
|
|
header.appendChild(closeButton);
|
|
container.appendChild(header);
|
|
|
|
// Create the canvas for the graph
|
|
const canvas = doc.createElement("canvas");
|
|
canvas.id = "money-graph";
|
|
canvas.width = 580; // Slightly smaller than the container for padding
|
|
canvas.height = 200;
|
|
canvas.style = `
|
|
background-color: rgba(17, 17, 17, 0.9); /* Dark background with 90% opacity */
|
|
border: 1px solid #666;
|
|
border-radius: 4px;
|
|
margin-top: 10px;
|
|
`;
|
|
container.appendChild(canvas);
|
|
|
|
// Create the money display (on the right side of the graph)
|
|
const moneyDisplay = doc.createElement("div");
|
|
moneyDisplay.style = `
|
|
color: #ffd700; /* Yellow color for money display */
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
position: absolute;
|
|
right: 20px;
|
|
top: 100px;
|
|
`;
|
|
moneyDisplay.innerText = "Money: Loading...";
|
|
container.appendChild(moneyDisplay);
|
|
|
|
doc.body.appendChild(container);
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
|
|
// Movable functionality for the entire container
|
|
let isMoving = false;
|
|
let offsetX = 0, offsetY = 0;
|
|
|
|
// Start moving the container on mousedown
|
|
function startMove(e) {
|
|
isMoving = true;
|
|
offsetX = e.clientX - container.getBoundingClientRect().left;
|
|
offsetY = e.clientY - container.getBoundingClientRect().top;
|
|
container.style.cursor = "grabbing"; // Change cursor to grabbing while moving
|
|
}
|
|
|
|
// Stop moving on mouseup
|
|
function stopMove() {
|
|
if (isMoving) {
|
|
isMoving = false;
|
|
container.style.cursor = "move"; // Change cursor back to move
|
|
}
|
|
}
|
|
|
|
// Handle the movement of the window
|
|
function moveWindow(e) {
|
|
if (isMoving) {
|
|
container.style.left = `${e.clientX - offsetX}px`;
|
|
container.style.top = `${e.clientY - offsetY}px`;
|
|
}
|
|
}
|
|
|
|
// Attach event listeners for movement
|
|
container.addEventListener("mousedown", startMove);
|
|
doc.addEventListener("mousemove", moveWindow);
|
|
doc.addEventListener("mouseup", stopMove);
|
|
doc.addEventListener("mouseleave", stopMove); // Stop moving if mouse leaves the window
|
|
|
|
// Function to record money data to file with timestamps
|
|
function recordMoneyData() {
|
|
const currentMoney = ns.getServerMoneyAvailable("home");
|
|
const timestamp = Date.now();
|
|
const record = `${timestamp},${currentMoney}\n`;
|
|
ns.write(moneyRecordFile, record, "a"); // Append without await
|
|
moneyDisplay.innerText = `Money: ${formatLargeNumber(currentMoney)}`; // Update money display with formatted value
|
|
}
|
|
|
|
// Function to draw grid lines
|
|
function drawGridLines(maxMoney, minMoney) {
|
|
const numSteps = 5; // Number of steps on the axes
|
|
const stepY = (maxMoney - minMoney) / numSteps; // Step size for Y-axis
|
|
const stepX = graphDuration / numSteps; // Step size for X-axis (time)
|
|
|
|
// Draw vertical grid lines (X-axis steps)
|
|
for (let i = 1; i <= numSteps; i++) {
|
|
const x = 40 + (i * (canvas.width - 60)) / numSteps;
|
|
ctx.beginPath();
|
|
ctx.moveTo(x, 10);
|
|
ctx.lineTo(x, 180);
|
|
ctx.strokeStyle = 'rgba(0, 255, 0, 0.2)'; // Faint green grid line
|
|
ctx.stroke();
|
|
// Draw X-axis labels
|
|
const timeLabel = `${Math.round((i * stepX) / 1000)}s`;
|
|
ctx.fillStyle = '#0c0';
|
|
ctx.font = '12px "Source Code Pro"';
|
|
ctx.fillText(timeLabel, x - 10, 190);
|
|
}
|
|
|
|
// Draw horizontal grid lines (Y-axis steps)
|
|
for (let i = 1; i <= numSteps; i++) {
|
|
const y = 180 - (i * 170) / numSteps;
|
|
ctx.beginPath();
|
|
ctx.moveTo(40, y);
|
|
ctx.lineTo(550, y);
|
|
ctx.strokeStyle = 'rgba(0, 255, 0, 0.2)'; // Faint green grid line
|
|
ctx.stroke();
|
|
// Draw Y-axis labels with formatted money
|
|
const moneyLabel = formatLargeNumber(minMoney + i * stepY);
|
|
ctx.fillStyle = '#0c0';
|
|
ctx.font = '12px "Source Code Pro"';
|
|
ctx.fillText(moneyLabel, 5, y + 5);
|
|
}
|
|
}
|
|
|
|
// Function to draw the graph with dynamic scaling from file
|
|
async function drawGraph() {
|
|
const fileData = await ns.read(moneyRecordFile);
|
|
if (!fileData) return;
|
|
|
|
const records = fileData.trim().split("\n").map(line => {
|
|
const [timestamp, money] = line.split(",");
|
|
return { timestamp: parseInt(timestamp), money: parseFloat(money) };
|
|
});
|
|
|
|
const now = Date.now();
|
|
const recentRecords = records.filter(record => now - record.timestamp <= graphDuration);
|
|
|
|
// Clear the canvas
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
if (recentRecords.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const maxMoney = Math.max(...recentRecords.map(record => record.money));
|
|
const minMoney = Math.min(...recentRecords.map(record => record.money));
|
|
|
|
if (maxMoney === minMoney) {
|
|
return;
|
|
}
|
|
|
|
// Draw gridlines
|
|
drawGridLines(maxMoney, minMoney);
|
|
|
|
// Draw axes
|
|
ctx.beginPath();
|
|
ctx.moveTo(40, 10); // Left margin
|
|
ctx.lineTo(40, 180); // Bottom margin
|
|
ctx.lineTo(550, 180); // Right margin
|
|
ctx.strokeStyle = '#0c0'; // Green for axes
|
|
ctx.stroke();
|
|
|
|
// Plot the data points with yellow line
|
|
ctx.strokeStyle = '#ffd700'; // Yellow for the money line
|
|
ctx.beginPath();
|
|
const firstPoint = recentRecords[0];
|
|
const startTime = firstPoint.timestamp;
|
|
const initialY = 180 - ((firstPoint.money - minMoney) / (maxMoney - minMoney)) * 170; // Scale to fit graph height
|
|
ctx.moveTo(40, initialY);
|
|
|
|
recentRecords.forEach((record) => {
|
|
const timeDiff = record.timestamp - startTime;
|
|
const x = 40 + (timeDiff / graphDuration) * (canvas.width - 60); // Scaled x-axis
|
|
const y = 180 - ((record.money - minMoney) / (maxMoney - minMoney)) * 170; // Scaled y-axis
|
|
ctx.lineTo(x, y);
|
|
});
|
|
|
|
ctx.stroke();
|
|
}
|
|
|
|
// Main loop to record money data and update graph
|
|
try {
|
|
while (!isClosed) {
|
|
recordMoneyData(); // Record data without await
|
|
await drawGraph(); // Ensure graph is updated properly
|
|
await ns.sleep(recordInterval); // Wait for 10 seconds
|
|
}
|
|
} catch (e) {
|
|
ns.tprint(`Error: ${e.message}`);
|
|
} finally {
|
|
if (doc.body.contains(container)) {
|
|
container.remove(); // Clean up the container when the script ends
|
|
}
|
|
ns.tprint("Script stopped.");
|
|
}
|
|
}
|