Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 64 additions & 11 deletions sleeve.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getNsDataThroughFile, formatMoney, formatDuration, disableLogs } from './helpers.js'
import { getNsDataThroughFile, formatMoney, formatDuration, getActiveSourceFiles, disableLogs } from './helpers.js'

const interval = 5000; // Uodate (tick) this often
const minTaskWorkTime = 59000; // Sleeves assigned a new task should stick to it for at least this many milliseconds
Expand All @@ -14,6 +14,9 @@ const argsSchema = [
['aug-budget', 0.5], // Spend up to this much of current cash on augs per tick (Default is high, because these are permanent for the rest of the BN)
['buy-cooldown', 60 * 1000], // Must wait this may milliseconds before buying more augs for a sleeve
['min-aug-batch', 20], // Must be able to afford at least this many augs before we pull the trigger (or fewer if buying all remaining augs)
['mimic-player', true], // Whether sleeve 0 always attempts work instead of doing crime, prioritizing the same faction/company as the player
['work-chance', 0.5], // General chance of working instead of doing crime, if we still need more karma for a gang, or money
['money-threshold', 10000000], // Above this amount of money, don't worry about crime unless we're still trying to get a gang
];

export function autocomplete(data, _) {
Expand All @@ -26,7 +29,20 @@ export async function main(ns) {
options = ns.flags(argsSchema);
disableLogs(ns, ['getServerMoneyAvailable']);
if (!crimes.includes(options.crime)) crimes.push(options.crime);
let task = [], lastUpdate = [], lastPurchase = [], availableAugs = [], lastReassign = [];
let task = [], lastUpdate = [], lastPurchase = [], availableAugs = [], lastReassign = [], sleeveFactions = [], ownedSourceFiles = [];
let needGang, randomCrime, doWork, doFaction;
let crimeRoll, randomIndex;

ownedSourceFiles = await getActiveSourceFiles(ns);
const sf2Level = ownedSourceFiles[2] || 0;
if (sf2Level == 0) {
needGang = false;
log(ns, "Gangs not unlocked.");
} else {
needGang = true;
}

log(ns, `WARN: Do not go to Sleeves tab while sleeve.js is managing multiple sleeves, until https://github.com/danielyxie/bitburner/issues/2832 is resolved. It can trigger Recovery Mode, but is seemingly harmless.`, `warning`, true);

// Collect info that won't change or that we can track ourselves going forward
let numSleeves;
Expand All @@ -35,17 +51,29 @@ export async function main(ns) {
} catch {
return ns.print("User does not appear to have access to sleeves. Exiting...");
}
for (let i = 0; i < numSleeves; i++)
availableAugs[i] = null;
for (let i = 0; i < numSleeves; i++) {
availableAugs[i] = null;
await getNsDataThroughFile(ns, `ns.sleeve.setToShockRecovery(${i})`, tempFile); // Default everyone to non-faction work to avoid trouble before sleeveFactions are populated
}

while (true) {
let cash = ns.getServerMoneyAvailable("home") - Number(ns.read("reserve.txt"));
let budget = cash * options['aug-budget'];
let playerInfo = await getNsDataThroughFile(ns, 'ns.getPlayer()', '/Temp/player-info.txt')
let playerInfo = await getNsDataThroughFile(ns, 'ns.getPlayer()', '/Temp/player-info.txt');
const gangInfo = await getNsDataThroughFile(ns, 'ns.gang.inGang() ? ns.gang.getGangInformation() : false', '/Temp/gang-stats.txt');
let gangFaction = ' ';
if (gangInfo && gangInfo.faction) {
needGang = false;
gangFaction = gangInfo.faction;
}
randomCrime = (cash < options['money-threshold'] || needGang);
let numJoinedFactions = playerInfo.factions.length;
let factionIndex = 0;
for (let i = 0; i < numSleeves; i++) {
let sleeveStats = ns.sleeve.getSleeveStats(i);
let shock = sleeveStats.shock;
let sync = sleeveStats.sync;
let factionAssignedName = ' ';
// Manage Augmentations
if (shock == 0 && availableAugs[i] == null) // No augs are available augs until shock is 0
availableAugs[i] = (await getNsDataThroughFile(ns, `ns.sleeve.getSleevePurchasableAugs(${i})`, '/Temp/sleeve-augs.txt')).sort((a, b) => a.cost - b.cost); // list of { name, cost }
Expand All @@ -67,6 +95,24 @@ export async function main(ns) {
lastPurchase[i] = Date.now();
}
}

if (!randomCrime || Math.random() < options['work-chance']) {
doWork = true;
// Pick a valid faction that no one else is using
randomIndex = Math.floor(Math.random() * numJoinedFactions); // This will remove the bias for our earlier, less-useful factions
for (let j = 0; j < numJoinedFactions; j++) {
if ((!sleeveFactions.includes(playerInfo.factions[(j+randomIndex) % numJoinedFactions]) || sleeveFactions[i] == playerInfo.factions[(j+randomIndex) % numJoinedFactions]) && playerInfo.factions[(j+randomIndex) % numJoinedFactions] != gangFaction) {
factionIndex = (j+randomIndex) % numJoinedFactions;
doFaction = true
break;
} else if (j == numJoinedFactions - 1) {
doFaction = false
}
}
} else {
doWork = false;
doFaction = false;
}
// Manage what this sleeve should be doing
let command, designatedTask;
if (sync < 100) { // Synchronize
Expand All @@ -83,15 +129,21 @@ export async function main(ns) {
log(ns, `INFO: Sleeve ${i} is recovering from shock... ${shock.toFixed(2)}%`);
lastUpdate[i] = Date.now();
}
} else if (i == 0 && playerInfo.isWorking && playerInfo.workType == "Working for Faction") { // If player is currently working for faction rep, sleeves 0 shall help him out (only one sleeve can work for a faction)
} else if (i == 0 && playerInfo.isWorking && playerInfo.workType == "Working for Faction" && !sleeveFactions.includes(playerInfo.currentWorkFactionName) && (doWork || options['mimic-player'])) { // If player is currently working for faction rep, sleeves 0 shall help him out (only one sleeve can work for a faction)
// TODO: We should be able to borrow logic from work-for-factions.js to have more sleeves work for useful factions / companies
factionAssignedName = playerInfo.currentWorkFactionName;
let work = works[workByFaction[playerInfo.currentWorkFactionName] || 0];
designatedTask = `work for faction '${playerInfo.currentWorkFactionName}' (${work})`;
command = `ns.sleeve.setToFactionWork(${i}, '${playerInfo.currentWorkFactionName}', '${work}')`; // TODO: Auto-determine the most productive faction work to do?
} else if (i == 0 && playerInfo.isWorking && playerInfo.workType == "Working for Company") { // If player is currently working for a company rep, sleeves 0 shall help him out (only one sleeve can work for a company)
designatedTask = `work for faction '${factionAssignedName}' (${work})`;
command = `ns.sleeve.setToFactionWork(${i}, '${factionAssignedName}', '${work}')`; // TODO: Auto-determine the most productive faction work to do?
} else if (i == 0 && playerInfo.isWorking && playerInfo.workType == "Working for Company" && (doWork || options['mimic-player'])) { // If player is currently working for a company rep, sleeves 0 shall help him out (only one sleeve can work for a company)
designatedTask = `work for company '${playerInfo.companyName}'`;
command = `ns.sleeve.setToCompanyWork(${i}, '${playerInfo.companyName}')`;
} else { // Do something productive
} else if (doWork && doFaction) {
factionAssignedName = playerInfo.factions[factionIndex];
let work = works[workByFaction[playerInfo.factions[factionIndex]] || 0];
designatedTask = `work for faction '${factionAssignedName}' (${work})`;
command = `ns.sleeve.setToFactionWork(${i}, '${factionAssignedName}', '${work}')`;
} else { // Do something productive
let crime = options.crime || (sleeveStats.strength < 100 ? 'mug' : 'homicide');
designatedTask = `commit ${crime}`;
command = `ns.sleeve.setToCommitCrime(${i}, '${crime}')`;
Expand All @@ -102,13 +154,14 @@ export async function main(ns) {
let strAction = `Set sleeve ${i} to ${designatedTask}`;
if (await getNsDataThroughFile(ns, command, tempFile)) {
task[i] = designatedTask;
sleeveFactions[i] = factionAssignedName;
lastReassign[i] = Date.now();
log(ns, `SUCCESS: ${strAction}`);
} else {
// If working for faction / company, it's possible he current work isn't supported, so try the next one.
if (designatedTask.startsWith('work for faction')) {
log(ns, `WARN: Failed to ${strAction} - work type may not be supported.`, 'warning');
workByFaction[playerInfo.currentWorkFactionName] = (workByFaction[playerInfo.currentWorkFactionName] || 0) + 1;
workByFaction[factionAssignedName] = (workByFaction[factionAssignedName] || 0) + 1;
} else
log(ns, `ERROR: Failed to ${strAction}`, 'error');
}
Expand Down