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
121 changes: 118 additions & 3 deletions work-for-factions.js
Original file line number Diff line number Diff line change
Expand Up @@ -547,19 +547,21 @@ async function goToCity(ns, cityName) {
/** @param {NS} ns */
export async function crimeForKillsKarmaStats(ns, reqKills, reqKarma, reqStats, doFastCrimesOnly = false) {
const bestCrimesByDifficulty = ["heist", "assassinate", "homicide", "mug"]; // Will change crimes as our success rate improves
const bestGym = "powerhouse gym";
const chanceThresholds = [0.75, 0.9, 0.5, 0]; // Will change crimes once we reach this probability of success for better all-round gains
doFastCrimesOnly = doFastCrimesOnly || (options ? options['fast-crimes-only'] : false);
let player = await getPlayerInfo(ns);
let strRequirements = [];
let forever = reqKills >= Number.MAX_SAFE_INTEGER || reqKarma >= Number.MAX_SAFE_INTEGER || reqStats >= Number.MAX_SAFE_INTEGER;
let forever = reqKills >= Number.MAX_SAFE_INTEGER || reqKarma >= Number.MAX_SAFE_INTEGER;
let statForever = reqStats >= Number.MAX_SAFE_INTEGER;
if (reqKills) strRequirements.push(() => `${reqKills} kills (Have ${player.numPeopleKilled})`);
if (reqKarma) strRequirements.push(() => `-${reqKarma} Karma (Have ${Math.round(ns.heart.break()).toLocaleString('en')})`);
if (reqStats) strRequirements.push(() => `${reqStats} of each combat stat (Have ` +
`Str: ${player.skills.strength}, Def: ${player.skills.defense}, Dex: ${player.skills.dexterity}, Agi: ${player.skills.agility})`);
let anyStatsDeficient = (p) => p.skills.strength < reqStats || p.skills.defense < reqStats ||
/* */ p.skills.dexterity < reqStats || p.skills.agility < reqStats;
let crime, lastCrime, crimeTime, lastStatusUpdateTime, needStats;
while (forever || (needStats = anyStatsDeficient(player)) || player.numPeopleKilled < reqKills || -ns.heart.break() < reqKarma) {
while (forever || player.numPeopleKilled < reqKills || -ns.heart.break() < reqKarma) {
if (!forever && breakToMainLoop()) return ns.print('INFO: Interrupting crime to check on high-level priorities.');
let crimeChances = await getNsDataThroughFile(ns, `Object.fromEntries(ns.args.map(c => [c, ns.singularity.getCrimeChance(c)]))`, '/Temp/crime-chances.txt', bestCrimesByDifficulty);
let karma = -ns.heart.break();
Expand Down Expand Up @@ -592,10 +594,123 @@ export async function crimeForKillsKarmaStats(ns, reqKills, reqKarma, reqStats,
crimeCount++;
player = await getPlayerInfo(ns);
}

// Travels to Sector-12 since ns.singularity.gymWorkout(); requires that the location of the player is the same as the gym
await goToCity(ns, "Sector-12");
let isWorking = false;
let currentStat = 1;

while(statForever || (needStats = anyStatsDeficient(player))) {

player = await getPlayerInfo(ns);

let pStr = player.skills.strength;
let pDef = player.skills.defense;
let pDex = player.skills.dexterity;
let pAgi = player.skills.agility;

if (!statForever && breakToMainLoop()) return ns.print('INFO: Interrupting training to check on high-level priorities.');
switch (currentStat) {
case 1:
if (!isWorking) {
ns.singularity.gymWorkout(bestGym, "strength")
isWorking = true;
}
ns.print(`Currently at ${pStr} strength, out of ${reqStats}` + ` (ETA: ${formatDuration(await getSkillEta(ns, "strength", reqStats))})`);
if (pStr >= reqStats) {
currentStat = 2;
ns.singularity.stopAction();
isWorking = false;
ns.print('SUCCESS: Strength stat requirement completed.');
}
break;
case 2:
if (!isWorking) {
ns.singularity.gymWorkout(bestGym, "defense")
isWorking = true;
}
ns.print(`Currently at ${pDef} defense, out of ${reqStats}` + ` (ETA: ${formatDuration(await getSkillEta(ns, "defense", reqStats))})`);
if (pDef >= reqStats) {
currentStat = 3;
ns.singularity.stopAction();
isWorking = false;
ns.print('SUCCESS: Defense stat requirement completed.');
}
break;
case 3:
if (!isWorking) {
ns.singularity.gymWorkout(bestGym, "dexterity")
isWorking = true;
}
ns.print(`Currently at ${pDex} dexterity, out of ${reqStats}` + ` (ETA: ${formatDuration(await getSkillEta(ns, "dexterity", reqStats))})`);
if (pDex >= reqStats) {
currentStat = 4;
ns.singularity.stopAction();
isWorking = false;
ns.print('SUCCESS: Dexterity stat requirement completed.');
}
break;
case 4:
if (!isWorking) {
ns.singularity.gymWorkout(bestGym, "agility")
isWorking = true;
}
ns.print(`Currently at ${pAgi} agility, out of ${reqStats}` + ` (ETA: ${formatDuration(await getSkillEta(ns, "agility", reqStats))})`);
if (pAgi >= reqStats) {
currentStat = 1;
ns.singularity.stopAction();
isWorking = false;
ns.print('SUCCESS: Agility stat requirement completed.');
}
break;

}

// TODO: Maybe configure the loop update to match the ETA for the stat to not over train stat (it probably doesn't matter since it's a matter of seconds).
await ns.sleep(loopSleepInterval + 5000);
}
ns.print(`Done committing crimes. Reached ${strRequirements.map(r => r()).join(', ')}`);
return true;
}

// TODO: Finish cost check before working at the gym.
/** @param {NS} ns */
async function getGymCost(ns, etaMilli) {
let baseGymCost = 120;
let powerHouseGymCostMult = 20;
// TODO: Apparently, if the gym gets backdoored, a 10% discount will be applied.
return baseGymCost * powerHouseGymCostMult * (etaMilli / 1000);
}

// Gets the time to fill Exp requirements.
/** @param {NS} ns */
async function getSkillEta(ns, skill, reqStats) {
let player = await getPlayerInfo(ns);
let skill1 = skill + "_exp";
let statExpMult = player.mults[skill1];
let powerHouseGymSkillMult = 10;

let expPerMilli = ((Math.ceil(statExpMult * 10000) / 10000) * powerHouseGymSkillMult) / 1000;
// ^ While this seems unneccessairy, it actually makes the ETA more precise.

return (await getReqExp(ns, skill, reqStats) / expPerMilli);
}
// Gets the required exp to bring a stat level to a reuqired stat level.
/** @param {NS} ns */
async function getReqExp(ns, _wStat, reqStats) {
let player = await getPlayerInfo(ns);

let actExp = player.exp[_wStat];
let mult = player.mults[_wStat];
let reqExp = calculateExp(reqStats, mult);

return reqExp - actExp;
}
// Calculates the exp for a given stat level (game accurate).
export function calculateExp(skill, mult = 1) {
return Math.exp((skill / mult + 200) / 32) - 534.6;
}

/** @param {NS} ns */
async function studyForCharisma(ns, focus) {
await goToCity(ns, 'Volhaven');
Expand Down Expand Up @@ -1123,4 +1238,4 @@ export async function workForMegacorpFactionInvite(ns, factionName, waitForInvit
ns.print(`Stopped working for "${companyName}" repRequiredForFaction: ${repRequiredForFaction.toLocaleString('en')} ` +
`currentReputation: ${Math.round(currentReputation).toLocaleString('en')} inFaction: ${player.factions.includes(factionName)}`);
return false;
}
}