work on sleeve new work system

This commit is contained in:
Olivier Gagnon
2022-07-27 20:37:32 -04:00
parent 315b2adf30
commit ebe953b498
19 changed files with 816 additions and 467 deletions
+130 -338
View File
@@ -37,8 +37,18 @@ import { BladeburnerConstants } from "../../Bladeburner/data/Constants";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { capitalizeFirstLetter, capitalizeEachWord } from "../../utils/StringHelperFunctions"; import { capitalizeFirstLetter, capitalizeEachWord } from "../../utils/StringHelperFunctions";
import { FactionWorkType } from "../../Work/data/FactionWorkType"; import { FactionWorkType } from "../../Work/data/FactionWorkType";
import { Work } from "./Work/Work";
import { SleeveClassWork } from "./Work/SleeveClassWork";
import { ClassType } from "../../Work/ClassWork";
import { SleeveSynchroWork } from "./Work/SleeveSynchroWork";
import { SleeveRecoveryWork } from "./Work/SleeveRecoveryWork";
import { SleeveFactionWork } from "./Work/SleeveFactionWork";
import { SleeveCompanyWork } from "./Work/SleeveCompanyWork";
import { SleeveBladeburnerGeneralWork } from "./Work/SleeveBladeburnerGeneralActionWork";
import { SleeveInfiltrateWork } from "./Work/SleeveInfiltrateWork";
export class Sleeve extends Person { export class Sleeve extends Person {
currentWork: Work | null = null;
/** /**
* Stores the name of the class that the player is currently taking * Stores the name of the class that the player is currently taking
*/ */
@@ -149,6 +159,13 @@ export class Sleeve extends Person {
} }
} }
shockBonus(): number {
return this.shock / 100;
}
syncBonus(): number {
return this.sync / 100;
}
/** /**
* Commit crimes * Commit crimes
*/ */
@@ -184,9 +201,8 @@ export class Sleeve extends Person {
/** /**
* Called to stop the current task * Called to stop the current task
*/ */
finishTask(p: IPlayer): ITaskTracker { finishTask(p: IPlayer): void {
let retValue: ITaskTracker = createTaskTracker(); // Amount of exp to be gained by other sleeves this.currentWork = null;
if (this.currentTask === SleeveTaskType.Crime) { if (this.currentTask === SleeveTaskType.Crime) {
// For crimes, all experience and money is gained at the end // For crimes, all experience and money is gained at the end
if (this.currentTaskTime >= this.currentTaskMaxTime) { if (this.currentTaskTime >= this.currentTaskMaxTime) {
@@ -194,7 +210,7 @@ export class Sleeve extends Person {
if (!crime) { if (!crime) {
console.error(`Invalid data stored in sleeve.crimeType: ${this.crimeType}`); console.error(`Invalid data stored in sleeve.crimeType: ${this.crimeType}`);
this.resetTaskStatus(p); this.resetTaskStatus(p);
return retValue; return;
} }
if (Math.random() < crime.successRate(this)) { if (Math.random() < crime.successRate(this)) {
// Success // Success
@@ -205,22 +221,22 @@ export class Sleeve extends Person {
const key = keysForIteration[i]; const key = keysForIteration[i];
successGainRates[key] = this.gainRatesForTask[key] * 2; successGainRates[key] = this.gainRatesForTask[key] * 2;
} }
retValue = this.gainExperience(p, successGainRates); this.gainExperience(p, successGainRates);
this.gainMoney(p, this.gainRatesForTask); this.gainMoney(p, this.gainRatesForTask);
p.karma -= crime.karma * (this.sync / 100); p.karma -= crime.karma * (this.sync / 100);
} else { } else {
retValue = this.gainExperience(p, this.gainRatesForTask); this.gainExperience(p, this.gainRatesForTask);
} }
// Do not reset task to IDLE // Do not reset task to IDLE
this.currentTaskTime = 0; this.currentTaskTime = 0;
return retValue; return;
} }
} else if (this.currentTask === SleeveTaskType.Bladeburner) { } else if (this.currentTask === SleeveTaskType.Bladeburner) {
if (this.currentTaskMaxTime === 0) { if (this.currentTaskMaxTime === 0) {
this.currentTaskTime = 0; this.currentTaskTime = 0;
return retValue; return;
} }
// For bladeburner, all experience and money is gained at the end // For bladeburner, all experience and money is gained at the end
const bb = p.bladeburner; const bb = p.bladeburner;
@@ -228,14 +244,14 @@ export class Sleeve extends Person {
const errorLogText = `bladeburner is null`; const errorLogText = `bladeburner is null`;
console.error(`Function: sleeves.finishTask; Message: '${errorLogText}'`); console.error(`Function: sleeves.finishTask; Message: '${errorLogText}'`);
this.resetTaskStatus(p); this.resetTaskStatus(p);
return retValue; return;
} }
if (this.currentTaskTime >= this.currentTaskMaxTime) { if (this.currentTaskTime >= this.currentTaskMaxTime) {
if (this.bbAction === "Infiltrate synthoids") { if (this.bbAction === "Infiltrate synthoids") {
bb.infiltrateSynthoidCommunities(p); bb.infiltrateSynthoidCommunities(p);
this.currentTaskTime = 0; this.currentTaskTime = 0;
return retValue; return;
} }
let type: string; let type: string;
let name: string; let name: string;
@@ -252,19 +268,19 @@ export class Sleeve extends Person {
const errorLogText = `Invalid action: type='${type}' name='${name}'`; const errorLogText = `Invalid action: type='${type}' name='${name}'`;
console.error(`Function: sleeves.finishTask; Message: '${errorLogText}'`); console.error(`Function: sleeves.finishTask; Message: '${errorLogText}'`);
this.resetTaskStatus(p); this.resetTaskStatus(p);
return retValue; return;
} }
const action = bb.getActionObject(actionIdent); const action = bb.getActionObject(actionIdent);
if ((action?.count ?? 0) > 0) { if ((action?.count ?? 0) > 0) {
const bbRetValue = bb.completeAction(p, this, actionIdent, false); const bbRetValue = bb.completeAction(p, this, actionIdent, false);
if (bbRetValue) { if (bbRetValue) {
retValue = this.gainExperience(p, bbRetValue); this.gainExperience(p, bbRetValue);
this.gainMoney(p, bbRetValue); this.gainMoney(p, bbRetValue);
// Do not reset task to IDLE // Do not reset task to IDLE
this.currentTaskTime = 0; this.currentTaskTime = 0;
return retValue; return;
} }
} }
} }
@@ -272,7 +288,7 @@ export class Sleeve extends Person {
this.resetTaskStatus(p); this.resetTaskStatus(p);
return retValue; return;
} }
/** /**
@@ -431,25 +447,7 @@ export class Sleeve extends Person {
* Only applicable when working for company or faction * Only applicable when working for company or faction
*/ */
getRepGain(p: IPlayer): number { getRepGain(p: IPlayer): number {
if (this.currentTask === SleeveTaskType.Faction) { if (this.currentTask === SleeveTaskType.Company) {
let favorMult = 1;
const fac: Faction | null = Factions[this.currentTaskLocation];
if (fac != null) {
favorMult = 1 + fac.favor / 100;
}
switch (this.factionWorkType) {
case FactionWorkType.HACKING:
return this.getFactionHackingWorkRepGain() * (this.shock / 100) * favorMult;
case FactionWorkType.FIELD:
return this.getFactionFieldWorkRepGain() * (this.shock / 100) * favorMult;
case FactionWorkType.SECURITY:
return this.getFactionSecurityWorkRepGain() * (this.shock / 100) * favorMult;
default:
console.warn(`Invalid Sleeve.factionWorkType property in Sleeve.getRepGain(): ${this.factionWorkType}`);
return 0;
}
} else if (this.currentTask === SleeveTaskType.Company) {
const companyName: string = this.currentTaskLocation; const companyName: string = this.currentTaskLocation;
const company: Company | null = Companies[companyName]; const company: Company | null = Companies[companyName];
if (company == null) { if (company == null) {
@@ -532,12 +530,16 @@ export class Sleeve extends Person {
// Only process once every second (5 cycles) // Only process once every second (5 cycles)
const CyclesPerSecond = 1000 / CONSTANTS.MilliPerCycle; const CyclesPerSecond = 1000 / CONSTANTS.MilliPerCycle;
this.storedCycles += numCycles; this.storedCycles += numCycles;
if (this.storedCycles < CyclesPerSecond) { if (this.storedCycles < CyclesPerSecond) return;
return;
}
let cyclesUsed = this.storedCycles; let cyclesUsed = this.storedCycles;
cyclesUsed = Math.min(cyclesUsed, 15); cyclesUsed = Math.min(cyclesUsed, 15);
if (this.currentWork) {
this.currentWork.process(p, this, cyclesUsed);
this.storedCycles -= cyclesUsed;
return;
}
let time = cyclesUsed * CONSTANTS.MilliPerCycle; let time = cyclesUsed * CONSTANTS.MilliPerCycle;
if (this.currentTaskMaxTime !== 0 && this.currentTaskTime + time > this.currentTaskMaxTime) { if (this.currentTaskMaxTime !== 0 && this.currentTaskTime + time > this.currentTaskMaxTime) {
time = this.currentTaskMaxTime - this.currentTaskTime; time = this.currentTaskMaxTime - this.currentTaskTime;
@@ -556,36 +558,6 @@ export class Sleeve extends Person {
this.shock = Math.min(100, this.shock + 0.0001 * cyclesUsed); this.shock = Math.min(100, this.shock + 0.0001 * cyclesUsed);
switch (this.currentTask) { switch (this.currentTask) {
case SleeveTaskType.Idle:
break;
case SleeveTaskType.Class:
case SleeveTaskType.Gym:
this.updateTaskGainRates(p);
this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
break;
case SleeveTaskType.Faction: {
this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
// Gain faction reputation
const fac: Faction = Factions[this.currentTaskLocation];
if (!(fac instanceof Faction)) {
console.error(`Invalid faction for Sleeve task: ${this.currentTaskLocation}`);
break;
}
// If the player has a gang with the faction the sleeve is working
// for, we need to reset the sleeve's task
if (p.gang) {
if (fac.name === p.gang.facName) {
this.resetTaskStatus(p);
}
}
fac.playerReputation += this.getRepGain(p) * cyclesUsed;
break;
}
case SleeveTaskType.Company: { case SleeveTaskType.Company: {
this.gainExperience(p, this.gainRatesForTask, cyclesUsed); this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
this.gainMoney(p, this.gainRatesForTask, cyclesUsed); this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
@@ -599,16 +571,6 @@ export class Sleeve extends Person {
company.playerReputation += this.getRepGain(p) * cyclesUsed; company.playerReputation += this.getRepGain(p) * cyclesUsed;
break; break;
} }
case SleeveTaskType.Recovery:
this.shock = Math.min(100, this.shock + 0.0002 * cyclesUsed);
if (this.shock >= 100) this.resetTaskStatus(p);
break;
case SleeveTaskType.Synchro:
this.sync = Math.min(100, this.sync + p.getIntelligenceBonus(0.5) * 0.0002 * cyclesUsed);
if (this.sync >= 100) this.resetTaskStatus(p);
break;
default:
break;
} }
if (this.currentTaskMaxTime !== 0 && this.currentTaskTime >= this.currentTaskMaxTime) { if (this.currentTaskMaxTime !== 0 && this.currentTaskTime >= this.currentTaskMaxTime) {
@@ -630,15 +592,10 @@ export class Sleeve extends Person {
* Resets all parameters used to keep information about the current task * Resets all parameters used to keep information about the current task
*/ */
resetTaskStatus(p: IPlayer): void { resetTaskStatus(p: IPlayer): void {
this.currentWork = null;
if (this.bbAction == "Support main sleeve") { if (this.bbAction == "Support main sleeve") {
p.bladeburner?.sleeveSupport(false); p.bladeburner?.sleeveSupport(false);
} }
if (this.currentTask == SleeveTaskType.Class) {
const retVal = createTaskTracker();
retVal.int = CONSTANTS.IntelligenceClassBaseExpGain * Math.round(this.currentTaskTime / 1000);
const r = this.gainExperience(p, retVal);
p.sleeves.filter((s) => s != this).forEach((s) => s.gainExperience(p, r, 1, true));
}
this.earningsForTask = createTaskTracker(); this.earningsForTask = createTaskTracker();
this.gainRatesForTask = createTaskTracker(); this.gainRatesForTask = createTaskTracker();
this.currentTask = SleeveTaskType.Idle; this.currentTask = SleeveTaskType.Idle;
@@ -654,24 +611,22 @@ export class Sleeve extends Person {
} }
shockRecovery(p: IPlayer): boolean { shockRecovery(p: IPlayer): boolean {
if (this.currentTask !== SleeveTaskType.Idle) { if (this.currentTask !== SleeveTaskType.Idle || this.currentWork === null) {
this.finishTask(p); this.finishTask(p);
} else { } else {
this.resetTaskStatus(p); this.resetTaskStatus(p);
} }
this.currentWork = new SleeveRecoveryWork();
this.currentTask = SleeveTaskType.Recovery;
return true; return true;
} }
synchronize(p: IPlayer): boolean { synchronize(p: IPlayer): boolean {
if (this.currentTask !== SleeveTaskType.Idle) { if (this.currentTask !== SleeveTaskType.Idle || this.currentWork !== null) {
this.finishTask(p); this.finishTask(p);
} else { } else {
this.resetTaskStatus(p); this.resetTaskStatus(p);
} }
this.currentWork = new SleeveSynchroWork();
this.currentTask = SleeveTaskType.Synchro;
return true; return true;
} }
@@ -679,7 +634,7 @@ export class Sleeve extends Person {
* Take a course at a university * Take a course at a university
*/ */
takeUniversityCourse(p: IPlayer, universityName: string, className: string): boolean { takeUniversityCourse(p: IPlayer, universityName: string, className: string): boolean {
if (this.currentTask !== SleeveTaskType.Idle) { if (this.currentTask !== SleeveTaskType.Idle || this.currentWork) {
this.finishTask(p); this.finishTask(p);
} else { } else {
this.resetTaskStatus(p); this.resetTaskStatus(p);
@@ -687,58 +642,54 @@ export class Sleeve extends Person {
// Set exp/money multipliers based on which university. // Set exp/money multipliers based on which university.
// Also check that the sleeve is in the right city // Also check that the sleeve is in the right city
let costMult = 1; let loc: LocationName | undefined;
switch (universityName.toLowerCase()) { switch (universityName.toLowerCase()) {
case LocationName.AevumSummitUniversity.toLowerCase(): case LocationName.AevumSummitUniversity.toLowerCase(): {
if (this.city !== CityName.Aevum) { if (this.city !== CityName.Aevum) return false;
return false; loc = LocationName.AevumSummitUniversity;
}
this.currentTaskLocation = LocationName.AevumSummitUniversity;
costMult = 4;
break; break;
case LocationName.Sector12RothmanUniversity.toLowerCase():
if (this.city !== CityName.Sector12) {
return false;
} }
this.currentTaskLocation = LocationName.Sector12RothmanUniversity; case LocationName.Sector12RothmanUniversity.toLowerCase(): {
costMult = 3; if (this.city !== CityName.Sector12) return false;
loc = LocationName.Sector12RothmanUniversity;
break; break;
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase():
if (this.city !== CityName.Volhaven) {
return false;
} }
this.currentTaskLocation = LocationName.VolhavenZBInstituteOfTechnology; case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase(): {
costMult = 5; if (this.city !== CityName.Volhaven) return false;
loc = LocationName.VolhavenZBInstituteOfTechnology;
break; break;
default:
return false;
} }
}
if (!loc) return false;
// Set experience/money gains based on class // Set experience/money gains based on class
let classType: ClassType | undefined;
switch (className.toLowerCase()) { switch (className.toLowerCase()) {
case "study computer science": case "study computer science":
classType = ClassType.StudyComputerScience;
break; break;
case "data structures": case "data structures":
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassDataStructuresBaseCost * costMult); classType = ClassType.DataStructures;
break; break;
case "networks": case "networks":
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassNetworksBaseCost * costMult); classType = ClassType.Networks;
break; break;
case "algorithms": case "algorithms":
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassAlgorithmsBaseCost * costMult); classType = ClassType.Algorithms;
break; break;
case "management": case "management":
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassManagementBaseCost * costMult); classType = ClassType.Management;
break; break;
case "leadership": case "leadership":
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassLeadershipBaseCost * costMult); classType = ClassType.Leadership;
break; break;
default:
return false;
} }
if (!classType) return false;
this.className = className; this.currentWork = new SleeveClassWork({
this.currentTask = SleeveTaskType.Class; classType: classType,
location: loc,
});
return true; return true;
} }
@@ -767,99 +718,6 @@ export class Sleeve extends Person {
return true; return true;
} }
updateTaskGainRates(p: IPlayer): void {
if (this.currentTask === SleeveTaskType.Class) {
let expMult = 1;
switch (this.currentTaskLocation.toLowerCase()) {
case LocationName.AevumSummitUniversity.toLowerCase():
expMult = 3;
break;
case LocationName.Sector12RothmanUniversity.toLowerCase():
expMult = 2;
break;
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase():
expMult = 4;
break;
default:
return;
}
const totalExpMult = expMult * p.hashManager.getStudyMult();
switch (this.className.toLowerCase()) {
case "study computer science":
this.gainRatesForTask.hack =
CONSTANTS.ClassStudyComputerScienceBaseExp * totalExpMult * this.mults.hacking_exp;
break;
case "data structures":
this.gainRatesForTask.hack = CONSTANTS.ClassDataStructuresBaseExp * totalExpMult * this.mults.hacking_exp;
break;
case "networks":
this.gainRatesForTask.hack = CONSTANTS.ClassNetworksBaseExp * totalExpMult * this.mults.hacking_exp;
break;
case "algorithms":
this.gainRatesForTask.hack = CONSTANTS.ClassAlgorithmsBaseExp * totalExpMult * this.mults.hacking_exp;
break;
case "management":
this.gainRatesForTask.cha = CONSTANTS.ClassManagementBaseExp * totalExpMult * this.mults.charisma_exp;
break;
case "leadership":
this.gainRatesForTask.cha = CONSTANTS.ClassLeadershipBaseExp * totalExpMult * this.mults.charisma_exp;
break;
default:
break;
}
return;
}
if (this.currentTask === SleeveTaskType.Gym) {
// Get gym exp multiplier
let expMult = 1;
switch (this.currentTaskLocation.toLowerCase()) {
case LocationName.AevumCrushFitnessGym.toLowerCase():
expMult = 2;
break;
case LocationName.AevumSnapFitnessGym.toLowerCase():
expMult = 5;
break;
case LocationName.Sector12IronGym.toLowerCase():
expMult = 1;
break;
case LocationName.Sector12PowerhouseGym.toLowerCase():
expMult = 10;
break;
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase():
expMult = 4;
break;
default:
return;
}
// Set stat gain rate
const baseGymExp = 1;
const totalExpMultiplier = p.hashManager.getTrainingMult() * expMult;
switch (this.gymStatType) {
case "none": // Note : due to the way Sleeve.workOutAtGym() is currently designed, this should never happend.
break;
case "str":
this.gainRatesForTask.str = baseGymExp * totalExpMultiplier * this.mults.strength_exp;
break;
case "def":
this.gainRatesForTask.def = baseGymExp * totalExpMultiplier * this.mults.defense_exp;
break;
case "dex":
this.gainRatesForTask.dex = baseGymExp * totalExpMultiplier * this.mults.dexterity_exp;
break;
case "agi":
this.gainRatesForTask.agi = baseGymExp * totalExpMultiplier * this.mults.agility_exp;
break;
}
return;
}
console.warn(`Sleeve.updateTaskGainRates() called for unexpected task type ${this.currentTask}`);
}
upgradeMemory(n: number): void { upgradeMemory(n: number): void {
if (n < 0) { if (n < 0) {
console.warn(`Sleeve.upgradeMemory() called with negative value: ${n}`); console.warn(`Sleeve.upgradeMemory() called with negative value: ${n}`);
@@ -878,7 +736,7 @@ export class Sleeve extends Person {
return false; return false;
} }
if (this.currentTask !== SleeveTaskType.Idle) { if (this.currentTask !== SleeveTaskType.Idle || this.currentWork !== null) {
this.finishTask(p); this.finishTask(p);
} else { } else {
this.resetTaskStatus(p); this.resetTaskStatus(p);
@@ -886,50 +744,10 @@ export class Sleeve extends Person {
const company: Company | null = Companies[companyName]; const company: Company | null = Companies[companyName];
const companyPosition: CompanyPosition | null = CompanyPositions[p.jobs[companyName]]; const companyPosition: CompanyPosition | null = CompanyPositions[p.jobs[companyName]];
if (company == null) { if (company == null) return false;
return false; if (companyPosition == null) return false;
}
if (companyPosition == null) {
return false;
}
this.gainRatesForTask.money =
companyPosition.baseSalary *
company.salaryMultiplier *
this.mults.work_money *
BitNodeMultipliers.CompanyWorkMoney;
this.gainRatesForTask.hack =
companyPosition.hackingExpGain *
company.expMultiplier *
this.mults.hacking_exp *
BitNodeMultipliers.CompanyWorkExpGain;
this.gainRatesForTask.str =
companyPosition.strengthExpGain *
company.expMultiplier *
this.mults.strength_exp *
BitNodeMultipliers.CompanyWorkExpGain;
this.gainRatesForTask.def =
companyPosition.defenseExpGain *
company.expMultiplier *
this.mults.defense_exp *
BitNodeMultipliers.CompanyWorkExpGain;
this.gainRatesForTask.dex =
companyPosition.dexterityExpGain *
company.expMultiplier *
this.mults.dexterity_exp *
BitNodeMultipliers.CompanyWorkExpGain;
this.gainRatesForTask.agi =
companyPosition.agilityExpGain *
company.expMultiplier *
this.mults.agility_exp *
BitNodeMultipliers.CompanyWorkExpGain;
this.gainRatesForTask.cha =
companyPosition.charismaExpGain *
company.expMultiplier *
this.mults.charisma_exp *
BitNodeMultipliers.CompanyWorkExpGain;
this.currentTaskLocation = companyName; this.currentWork = new SleeveCompanyWork({ companyName: companyName });
this.currentTask = SleeveTaskType.Company;
return true; return true;
} }
@@ -944,7 +762,7 @@ export class Sleeve extends Person {
return false; return false;
} }
if (this.currentTask !== SleeveTaskType.Idle) { if (this.currentTask !== SleeveTaskType.Idle || this.currentWork === null) {
this.finishTask(p); this.finishTask(p);
} else { } else {
this.resetTaskStatus(p); this.resetTaskStatus(p);
@@ -953,40 +771,25 @@ export class Sleeve extends Person {
const factionInfo = faction.getInfo(); const factionInfo = faction.getInfo();
// Set type of work (hacking/field/security), and the experience gains // Set type of work (hacking/field/security), and the experience gains
const sanitizedWorkType: string = workType.toLowerCase(); const sanitizedWorkType = workType.toLowerCase();
let factionWorkType: FactionWorkType;
if (sanitizedWorkType.includes("hack")) { if (sanitizedWorkType.includes("hack")) {
if (!factionInfo.offerHackingWork) { if (!factionInfo.offerHackingWork) return false;
return false; factionWorkType = FactionWorkType.HACKING;
}
this.factionWorkType = FactionWorkType.HACKING;
this.gainRatesForTask.hack = 0.15 * this.mults.hacking_exp * BitNodeMultipliers.FactionWorkExpGain;
} else if (sanitizedWorkType.includes("field")) { } else if (sanitizedWorkType.includes("field")) {
if (!factionInfo.offerFieldWork) { if (!factionInfo.offerFieldWork) return false;
return false; factionWorkType = FactionWorkType.FIELD;
}
this.factionWorkType = FactionWorkType.FIELD;
this.gainRatesForTask.hack = 0.1 * this.mults.hacking_exp * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.str = 0.1 * this.mults.strength_exp * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.def = 0.1 * this.mults.defense_exp * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.dex = 0.1 * this.mults.dexterity_exp * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.agi = 0.1 * this.mults.agility_exp * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.cha = 0.1 * this.mults.charisma_exp * BitNodeMultipliers.FactionWorkExpGain;
} else if (sanitizedWorkType.includes("security")) { } else if (sanitizedWorkType.includes("security")) {
if (!factionInfo.offerSecurityWork) { if (!factionInfo.offerSecurityWork) return false;
return false; factionWorkType = FactionWorkType.SECURITY;
}
this.factionWorkType = FactionWorkType.SECURITY;
this.gainRatesForTask.hack = 0.1 * this.mults.hacking_exp * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.str = 0.15 * this.mults.strength_exp * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.def = 0.15 * this.mults.defense_exp * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.dex = 0.15 * this.mults.dexterity_exp * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.agi = 0.15 * this.mults.agility_exp * BitNodeMultipliers.FactionWorkExpGain;
} else { } else {
return false; return false;
} }
this.currentTaskLocation = factionName; this.currentWork = new SleeveFactionWork({
this.currentTask = SleeveTaskType.Faction; factionWorkType: factionWorkType,
factionName: faction.name,
});
return true; return true;
} }
@@ -995,7 +798,7 @@ export class Sleeve extends Person {
* Begin a gym workout task * Begin a gym workout task
*/ */
workoutAtGym(p: IPlayer, gymName: string, stat: string): boolean { workoutAtGym(p: IPlayer, gymName: string, stat: string): boolean {
if (this.currentTask !== SleeveTaskType.Idle) { if (this.currentTask !== SleeveTaskType.Idle || this.currentWork) {
this.finishTask(p); this.finishTask(p);
} else { } else {
this.resetTaskStatus(p); this.resetTaskStatus(p);
@@ -1003,73 +806,60 @@ export class Sleeve extends Person {
// Set exp/money multipliers based on which university. // Set exp/money multipliers based on which university.
// Also check that the sleeve is in the right city // Also check that the sleeve is in the right city
let costMult = 1; let loc: LocationName | undefined;
switch (gymName.toLowerCase()) { switch (gymName.toLowerCase()) {
case LocationName.AevumCrushFitnessGym.toLowerCase(): case LocationName.AevumCrushFitnessGym.toLowerCase(): {
if (this.city != CityName.Aevum) { if (this.city != CityName.Aevum) return false;
return false; loc = LocationName.AevumCrushFitnessGym;
}
this.currentTaskLocation = LocationName.AevumCrushFitnessGym;
costMult = 3;
break; break;
case LocationName.AevumSnapFitnessGym.toLowerCase():
if (this.city != CityName.Aevum) {
return false;
} }
this.currentTaskLocation = LocationName.AevumSnapFitnessGym; case LocationName.AevumSnapFitnessGym.toLowerCase(): {
costMult = 10; if (this.city != CityName.Aevum) return false;
loc = LocationName.AevumSnapFitnessGym;
break; break;
case LocationName.Sector12IronGym.toLowerCase():
if (this.city != CityName.Sector12) {
return false;
} }
this.currentTaskLocation = LocationName.Sector12IronGym; case LocationName.Sector12IronGym.toLowerCase(): {
costMult = 1; if (this.city != CityName.Sector12) return false;
loc = LocationName.Sector12IronGym;
break; break;
case LocationName.Sector12PowerhouseGym.toLowerCase():
if (this.city != CityName.Sector12) {
return false;
} }
this.currentTaskLocation = LocationName.Sector12PowerhouseGym; case LocationName.Sector12PowerhouseGym.toLowerCase(): {
costMult = 20; if (this.city != CityName.Sector12) return false;
loc = LocationName.Sector12PowerhouseGym;
break; break;
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase():
if (this.city != CityName.Volhaven) {
return false;
} }
this.currentTaskLocation = LocationName.VolhavenMilleniumFitnessGym; case LocationName.VolhavenMilleniumFitnessGym.toLowerCase(): {
costMult = 7; if (this.city != CityName.Volhaven) return false;
loc = LocationName.VolhavenMilleniumFitnessGym;
break; break;
default:
return false;
} }
}
if (!loc) return false;
// Set experience/money gains based on class // Set experience/money gains based on class
const sanitizedStat: string = stat.toLowerCase(); const sanitizedStat: string = stat.toLowerCase();
// set stat to a default value. // set stat to a default value.
stat = "none"; let classType: ClassType | undefined;
if (sanitizedStat.includes("str")) { if (sanitizedStat.includes("str")) {
stat = "str"; classType = ClassType.GymStrength;
} }
if (sanitizedStat.includes("def")) { if (sanitizedStat.includes("def")) {
stat = "def"; classType = ClassType.GymDefense;
} }
if (sanitizedStat.includes("dex")) { if (sanitizedStat.includes("dex")) {
stat = "dex"; classType = ClassType.GymDexterity;
} }
if (sanitizedStat.includes("agi")) { if (sanitizedStat.includes("agi")) {
stat = "agi"; classType = ClassType.GymAgility;
} }
// if stat is still equals its default value, then validation has failed. // if stat is still equals its default value, then validation has failed.
if (stat === "none") { if (!classType) return false;
return false;
}
// Set cost this.currentWork = new SleeveClassWork({
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassGymBaseCost * costMult); classType: classType,
this.gymStatType = stat; location: loc,
this.currentTask = SleeveTaskType.Gym; });
return true; return true;
} }
@@ -1078,7 +868,7 @@ export class Sleeve extends Person {
* Begin a bladeburner task * Begin a bladeburner task
*/ */
bladeburner(p: IPlayer, action: string, contract: string): boolean { bladeburner(p: IPlayer, action: string, contract: string): boolean {
if (this.currentTask !== SleeveTaskType.Idle) { if (this.currentTask !== SleeveTaskType.Idle || this.currentWork === null) {
this.finishTask(p); this.finishTask(p);
} else { } else {
this.resetTaskStatus(p); this.resetTaskStatus(p);
@@ -1098,24 +888,26 @@ export class Sleeve extends Person {
this.bbContract = "------"; this.bbContract = "------";
switch (action) { switch (action) {
case "Field analysis": case "Field analysis":
time = this.getBladeburnerActionTime(p, "General", action); // time = this.getBladeburnerActionTime(p, "General", action);
this.gainRatesForTask.hack = 20 * this.mults.hacking_exp; // this.gainRatesForTask.hack = 20 * this.mults.hacking_exp;
this.gainRatesForTask.cha = 20 * this.mults.charisma_exp; // this.gainRatesForTask.cha = 20 * this.mults.charisma_exp;
break; this.currentWork = new SleeveBladeburnerGeneralWork("Field analysis");
return true;
case "Recruitment": case "Recruitment":
time = this.getBladeburnerActionTime(p, "General", action); // time = this.getBladeburnerActionTime(p, "General", action);
this.gainRatesForTask.cha = // this.gainRatesForTask.cha =
2 * BladeburnerConstants.BaseStatGain * (p.bladeburner?.getRecruitmentTime(this) ?? 0) * 1000; // 2 * BladeburnerConstants.BaseStatGain * (p.bladeburner?.getRecruitmentTime(this) ?? 0) * 1000;
this.currentTaskLocation = `(Success Rate: ${numeralWrapper.formatPercentage( // this.currentTaskLocation = `(Success Rate: ${numeralWrapper.formatPercentage(
this.recruitmentSuccessChance(p), // this.recruitmentSuccessChance(p),
)})`; // )})`;
this.currentWork = new SleeveBladeburnerGeneralWork("Recruitment");
break; break;
case "Diplomacy": case "Diplomacy":
time = this.getBladeburnerActionTime(p, "General", action); // time = this.getBladeburnerActionTime(p, "General", action);
this.currentWork = new SleeveBladeburnerGeneralWork("Diplomacy");
break; break;
case "Infiltrate synthoids": case "Infiltrate synthoids":
time = 60000; this.currentWork = new SleeveInfiltrateWork();
this.currentTaskLocation = "This will generate additional contracts and operations";
break; break;
case "Support main sleeve": case "Support main sleeve":
p.bladeburner?.sleeveSupport(true); p.bladeburner?.sleeveSupport(true);
@@ -0,0 +1,59 @@
import { IPlayer } from "../../IPlayer";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { Work, WorkType } from "./Work";
import { CONSTANTS } from "../../../Constants";
export const isSleeveBladeburnerGeneralWork = (w: Work | null): w is SleeveBladeburnerGeneralWork =>
w !== null && w.type === WorkType.BLADEBURNER_GENERAL;
export class SleeveBladeburnerGeneralWork extends Work {
cyclesWorked = 0;
action: string;
constructor(action?: string) {
super(WorkType.BLADEBURNER_GENERAL);
this.action = action ?? "Field analysis";
}
cyclesNeeded(player: IPlayer, sleeve: Sleeve): number {
const ret = player.bladeburner?.getActionTimeNetscriptFn(sleeve, "General", this.action);
if (!ret || typeof ret === "string") throw new Error(`Error querying ${this.action} time`);
return ret / CONSTANTS._idleSpeed;
}
process(player: IPlayer, sleeve: Sleeve, cycles: number): number {
if (!player.bladeburner) throw new Error("sleeve doing blade work without being a member");
this.cyclesWorked += cycles;
while (this.cyclesWorked > this.cyclesNeeded(player, sleeve)) {
const actionIdent = player.bladeburner.getActionIdFromTypeAndName("General", this.action);
if (!actionIdent) throw new Error(`Error getting ${this.action} action`);
player.bladeburner.completeAction(player, sleeve, actionIdent, false);
this.cyclesWorked -= this.cyclesNeeded(player, sleeve);
}
return 0;
}
APICopy(): Record<string, unknown> {
return {
type: this.type,
action: this.action,
};
}
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): IReviverValue {
return Generic_toJSON("SleeveBladeburnerGeneralWork", this);
}
/**
* Initiatizes a BladeburnerWork object from a JSON save state.
*/
static fromJSON(value: IReviverValue): SleeveBladeburnerGeneralWork {
return Generic_fromJSON(SleeveBladeburnerGeneralWork, value.data);
}
}
Reviver.constructors.SleeveBladeburnerGeneralWork = SleeveBladeburnerGeneralWork;
@@ -0,0 +1,71 @@
import { IPlayer } from "../../IPlayer";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver";
import { Work, WorkType } from "./Work";
import { ClassType } from "../../../Work/ClassWork";
import { LocationName } from "../../../Locations/data/LocationNames";
import { calculateClassEarnings } from "../../../Work/formulas/Class";
import { Sleeve } from "../Sleeve";
import { applyWorkStats, applyWorkStatsExp, scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
export const isSleeveClassWork = (w: Work | null): w is SleeveClassWork => w !== null && w.type === WorkType.CLASS;
interface ClassWorkParams {
classType: ClassType;
location: LocationName;
}
export class SleeveClassWork extends Work {
classType: ClassType;
location: LocationName;
constructor(params?: ClassWorkParams) {
super(WorkType.CLASS);
this.classType = params?.classType ?? ClassType.StudyComputerScience;
this.location = params?.location ?? LocationName.Sector12RothmanUniversity;
}
calculateRates(player: IPlayer, sleeve: Sleeve): WorkStats {
return scaleWorkStats(
calculateClassEarnings(player, sleeve, this.classType, this.location),
sleeve.shockBonus(),
false,
);
}
isGym(): boolean {
return [ClassType.GymAgility, ClassType.GymDefense, ClassType.GymDexterity, ClassType.GymStrength].includes(
this.classType,
);
}
process(player: IPlayer, sleeve: Sleeve, cycles: number): number {
let rate = this.calculateRates(player, sleeve);
applyWorkStatsExp(sleeve, rate, cycles);
rate = scaleWorkStats(rate, sleeve.syncBonus(), false);
applyWorkStats(player, player, rate, cycles, "sleeves");
player.sleeves.filter((s) => s != sleeve).forEach((s) => applyWorkStatsExp(s, rate, cycles));
return 0;
}
APICopy(): Record<string, unknown> {
return {
type: this.type,
classType: this.classType,
location: this.location,
};
}
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): IReviverValue {
return Generic_toJSON("SleeveClassWork", this);
}
/**
* Initiatizes a ClassWork object from a JSON save state.
*/
static fromJSON(value: IReviverValue): SleeveClassWork {
return Generic_fromJSON(SleeveClassWork, value.data);
}
}
Reviver.constructors.SleeveClassWork = SleeveClassWork;
@@ -0,0 +1,70 @@
import { IPlayer } from "../../IPlayer";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { Work, WorkType } from "./Work";
import { LocationName } from "../../../Locations/data/LocationNames";
import { Companies } from "../../../Company/Companies";
import { Company } from "../../../Company/Company";
import { calculateCompanyWorkStats } from "../../../Work/formulas/Company";
import { applyWorkStats, applyWorkStatsExp, WorkStats } from "../../../Work/WorkStats";
import { influenceStockThroughCompanyWork } from "../../../StockMarket/PlayerInfluencing";
interface SleeveCompanyWorkParams {
companyName: string;
}
export const isSleeveCompanyWork = (w: Work | null): w is SleeveCompanyWork =>
w !== null && w.type === WorkType.COMPANY;
export class SleeveCompanyWork extends Work {
companyName: string;
constructor(params?: SleeveCompanyWorkParams) {
super(WorkType.COMPANY);
this.companyName = params?.companyName ?? LocationName.NewTokyoNoodleBar;
}
getCompany(): Company {
const c = Companies[this.companyName];
if (!c) throw new Error(`Company not found: '${this.companyName}'`);
return c;
}
getGainRates(player: IPlayer, sleeve: Sleeve): WorkStats {
return calculateCompanyWorkStats(player, sleeve, this.getCompany());
}
process(player: IPlayer, sleeve: Sleeve, cycles: number): number {
const company = this.getCompany();
const gains = this.getGainRates(player, sleeve);
applyWorkStatsExp(sleeve, gains, cycles);
applyWorkStats(player, player, gains, cycles, "sleeves");
player.sleeves.filter((s) => s != sleeve).forEach((s) => applyWorkStatsExp(s, gains, cycles));
company.playerReputation += gains.reputation * cycles;
influenceStockThroughCompanyWork(company, gains.reputation, cycles);
return 0;
}
APICopy(): Record<string, unknown> {
return {
type: this.type,
companyName: this.companyName,
};
}
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): IReviverValue {
return Generic_toJSON("SleeveCompanyWork", this);
}
/**
* Initiatizes a CompanyWork object from a JSON save state.
*/
static fromJSON(value: IReviverValue): SleeveCompanyWork {
return Generic_fromJSON(SleeveCompanyWork, value.data);
}
}
Reviver.constructors.SleeveCompanyWork = SleeveCompanyWork;
@@ -0,0 +1,96 @@
import { IPlayer } from "../../IPlayer";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { Work, WorkType } from "./Work";
import { FactionWorkType } from "../../../Work/data/FactionWorkType";
import { FactionNames } from "../../../Faction/data/FactionNames";
import { Factions } from "../../../Faction/Factions";
import { calculateFactionExp } from "../../../Work/formulas/Faction";
import { applyWorkStatsExp, scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
import { Faction } from "../../../Faction/Faction";
import {
getFactionFieldWorkRepGain,
getFactionSecurityWorkRepGain,
getHackingWorkRepGain,
} from "../../../PersonObjects/formulas/reputation";
interface SleeveFactionWorkParams {
factionWorkType: FactionWorkType;
factionName: string;
}
export const isSleeveFactionWork = (w: Work | null): w is SleeveFactionWork =>
w !== null && w.type === WorkType.FACTION;
export class SleeveFactionWork extends Work {
factionWorkType: FactionWorkType;
factionName: string;
constructor(params?: SleeveFactionWorkParams) {
super(WorkType.FACTION);
this.factionWorkType = params?.factionWorkType ?? FactionWorkType.HACKING;
this.factionName = params?.factionName ?? FactionNames.Sector12;
}
getExpRates(sleeve: Sleeve): WorkStats {
return scaleWorkStats(calculateFactionExp(sleeve, this.factionWorkType), sleeve.shockBonus());
}
getReputationRate(sleeve: Sleeve): number {
const faction = this.getFaction();
const repFormulas = {
[FactionWorkType.HACKING]: getHackingWorkRepGain,
[FactionWorkType.FIELD]: getFactionFieldWorkRepGain,
[FactionWorkType.SECURITY]: getFactionSecurityWorkRepGain,
};
return repFormulas[this.factionWorkType](sleeve, faction) * sleeve.shockBonus();
}
getFaction(): Faction {
const f = Factions[this.factionName];
if (!f) throw new Error(`Faction work started with invalid / unknown faction: '${this.factionName}'`);
return f;
}
process(player: IPlayer, sleeve: Sleeve, cycles: number): number {
if (player.gang) {
if (this.factionName === player.gang.facName) {
sleeve.currentWork = null;
return 0;
}
}
let exp = this.getExpRates(sleeve);
applyWorkStatsExp(sleeve, exp, cycles);
exp = scaleWorkStats(exp, sleeve.syncBonus());
applyWorkStatsExp(player, exp, cycles);
player.sleeves.filter((s) => s != sleeve).forEach((s) => applyWorkStatsExp(s, exp, cycles));
const rep = this.getReputationRate(sleeve);
this.getFaction().playerReputation += rep;
return 0;
}
APICopy(): Record<string, unknown> {
return {
type: this.type,
factionWorkType: this.factionWorkType,
factionName: this.factionName,
};
}
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): IReviverValue {
return Generic_toJSON("SleeveFactionWork", this);
}
/**
* Initiatizes a FactionWork object from a JSON save state.
*/
static fromJSON(value: IReviverValue): SleeveFactionWork {
return Generic_fromJSON(SleeveFactionWork, value.data);
}
}
Reviver.constructors.SleeveFactionWork = SleeveFactionWork;
@@ -0,0 +1,54 @@
import { IPlayer } from "../../IPlayer";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { Work, WorkType } from "./Work";
import { CONSTANTS } from "../../../Constants";
const infiltrateCycles = 600000 / CONSTANTS._idleSpeed;
export const isSleeveInfiltrateWork = (w: Work | null): w is SleeveInfiltrateWork =>
w !== null && w.type === WorkType.INFILTRATE;
export class SleeveInfiltrateWork extends Work {
cyclesWorked = 0;
constructor() {
super(WorkType.INFILTRATE);
}
cyclesNeeded(): number {
return infiltrateCycles;
}
process(player: IPlayer, sleeve: Sleeve, cycles: number): number {
if (!player.bladeburner) throw new Error("sleeve doing blade work without being a member");
this.cyclesWorked += cycles;
if (this.cyclesWorked > this.cyclesNeeded()) {
this.cyclesWorked -= this.cyclesNeeded();
player.bladeburner.infiltrateSynthoidCommunities(player);
}
return 0;
}
APICopy(): Record<string, unknown> {
return {
type: this.type,
};
}
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): IReviverValue {
return Generic_toJSON("SleeveInfiltrateWork", this);
}
/**
* Initiatizes a BladeburnerWork object from a JSON save state.
*/
static fromJSON(value: IReviverValue): SleeveInfiltrateWork {
return Generic_fromJSON(SleeveInfiltrateWork, value.data);
}
}
Reviver.constructors.SleeveInfiltrateWork = SleeveInfiltrateWork;
@@ -0,0 +1,41 @@
import { IPlayer } from "../../IPlayer";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { Work, WorkType } from "./Work";
export const isSleeveRecoveryWork = (w: Work | null): w is SleeveRecoveryWork =>
w !== null && w.type === WorkType.RECOVERY;
export class SleeveRecoveryWork extends Work {
constructor() {
super(WorkType.RECOVERY);
}
process(player: IPlayer, sleeve: Sleeve, cycles: number): number {
sleeve.shock = Math.min(100, sleeve.shock + 0.0002 * cycles);
if (sleeve.shock >= 100) sleeve.currentWork = null;
return 0;
}
APICopy(): Record<string, unknown> {
return {
type: this.type,
};
}
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): IReviverValue {
return Generic_toJSON("SleeveRecoveryWork", this);
}
/**
* Initiatizes a RecoveryWork object from a JSON save state.
*/
static fromJSON(value: IReviverValue): SleeveRecoveryWork {
return Generic_fromJSON(SleeveRecoveryWork, value.data);
}
}
Reviver.constructors.SleeveRecoveryWork = SleeveRecoveryWork;
@@ -0,0 +1,41 @@
import { IPlayer } from "../../IPlayer";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { Work, WorkType } from "./Work";
export const isSleeveSynchroWork = (w: Work | null): w is SleeveSynchroWork =>
w !== null && w.type === WorkType.SYNCHRO;
export class SleeveSynchroWork extends Work {
constructor() {
super(WorkType.SYNCHRO);
}
process(player: IPlayer, sleeve: Sleeve, cycles: number): number {
sleeve.sync = Math.min(100, sleeve.sync + player.getIntelligenceBonus(0.5) * 0.0002 * cycles);
if (sleeve.sync >= 100) sleeve.currentWork = null;
return 0;
}
APICopy(): Record<string, unknown> {
return {
type: this.type,
};
}
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): IReviverValue {
return Generic_toJSON("SleeveSynchroWork", this);
}
/**
* Initiatizes a SynchroWork object from a JSON save state.
*/
static fromJSON(value: IReviverValue): SleeveSynchroWork {
return Generic_fromJSON(SleeveSynchroWork, value.data);
}
}
Reviver.constructors.SleeveSynchroWork = SleeveSynchroWork;
+28
View File
@@ -0,0 +1,28 @@
import { IPlayer } from "../../IPlayer";
import { IReviverValue } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
export abstract class Work {
type: WorkType;
constructor(type: WorkType) {
this.type = type;
}
abstract process(player: IPlayer, sleeve: Sleeve, cycles: number): number;
abstract APICopy(): Record<string, unknown>;
abstract toJSON(): IReviverValue;
}
export enum WorkType {
COMPANY = "COMPANY",
FACTION = "FACTION",
CRIME = "CRIME",
CLASS = "CLASS",
RECOVERY = "RECOVERY",
SYNCHRO = "SYNCHRO",
BLADEBURNER_GENERAL = "BLADEBURNER_GENERAL",
INFILTRATE = "INFILTRATE",
BLADEBURNER_SUPPORT = "SUPPORT",
BLADEBURNER_CONTRACTS = "CONTRACTS",
}
+67 -37
View File
@@ -14,6 +14,13 @@ import { SleeveAugmentationsModal } from "./SleeveAugmentationsModal";
import { EarningsElement, StatsElement } from "./StatsElement"; import { EarningsElement, StatsElement } from "./StatsElement";
import { TaskSelector } from "./TaskSelector"; import { TaskSelector } from "./TaskSelector";
import { TravelModal } from "./TravelModal"; import { TravelModal } from "./TravelModal";
import { isSleeveClassWork } from "../Work/SleeveClassWork";
import { isSleeveSynchroWork } from "../Work/SleeveSynchroWork";
import { isSleeveRecoveryWork } from "../Work/SleeveRecoveryWork";
import { isSleeveFactionWork } from "../Work/SleeveFactionWork";
import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork";
import { isSleeveBladeburnerGeneralWork } from "../Work/SleeveBladeburnerGeneralActionWork";
import { isSleeveInfiltrateWork } from "../Work/SleeveInfiltrateWork";
interface IProps { interface IProps {
sleeve: Sleeve; sleeve: Sleeve;
@@ -69,29 +76,6 @@ export function SleeveElem(props: IProps): React.ReactElement {
case SleeveTaskType.Idle: case SleeveTaskType.Idle:
desc = <>This sleeve is currently idle</>; desc = <>This sleeve is currently idle</>;
break; break;
case SleeveTaskType.Company:
desc = <>This sleeve is currently working your job at {props.sleeve.currentTaskLocation}.</>;
break;
case SleeveTaskType.Faction: {
let doing = "nothing";
switch (props.sleeve.factionWorkType) {
case FactionWorkType.FIELD:
doing = "Field work";
break;
case FactionWorkType.HACKING:
doing = "Hacking contracts";
break;
case FactionWorkType.SECURITY:
doing = "Security work";
break;
}
desc = (
<>
This sleeve is currently doing {doing} for {props.sleeve.currentTaskLocation}.
</>
);
break;
}
case SleeveTaskType.Crime: { case SleeveTaskType.Crime: {
const crime = Object.values(Crimes).find((crime) => crime.name === props.sleeve.crimeType); const crime = Object.values(Crimes).find((crime) => crime.name === props.sleeve.crimeType);
if (!crime) throw new Error("crime should not be undefined"); if (!crime) throw new Error("crime should not be undefined");
@@ -106,9 +90,6 @@ export function SleeveElem(props: IProps): React.ReactElement {
case SleeveTaskType.Class: case SleeveTaskType.Class:
desc = <>This sleeve is currently studying/taking a course at {props.sleeve.currentTaskLocation}.</>; desc = <>This sleeve is currently studying/taking a course at {props.sleeve.currentTaskLocation}.</>;
break; break;
case SleeveTaskType.Gym:
desc = <>This sleeve is currently working out at {props.sleeve.currentTaskLocation}.</>;
break;
case SleeveTaskType.Bladeburner: { case SleeveTaskType.Bladeburner: {
let message = ""; let message = "";
if (props.sleeve.bbContract !== "------") { if (props.sleeve.bbContract !== "------") {
@@ -123,24 +104,73 @@ export function SleeveElem(props: IProps): React.ReactElement {
); );
break; break;
} }
case SleeveTaskType.Recovery:
desc = ( default:
<> console.error(`Invalid/Unrecognized taskValue in updateSleeveTaskDescription(): ${abc[0]}`);
This sleeve is currently set to focus on shock recovery. This causes the Sleeve's shock to decrease at a }
faster rate.
</> if (isSleeveClassWork(props.sleeve.currentWork)) {
); if (props.sleeve.currentWork.isGym())
break; desc = <>This sleeve is currently working out at {props.sleeve.currentWork.location}.</>;
case SleeveTaskType.Synchro: else desc = <>This sleeve is currently studying at {props.sleeve.currentWork.location}.</>;
}
if (isSleeveSynchroWork(props.sleeve.currentWork)) {
desc = ( desc = (
<> <>
This sleeve is currently set to synchronize with the original consciousness. This causes the Sleeve's This sleeve is currently set to synchronize with the original consciousness. This causes the Sleeve's
synchronization to increase. synchronization to increase.
</> </>
); );
}
if (isSleeveRecoveryWork(props.sleeve.currentWork)) {
desc = (
<>
This sleeve is currently set to focus on shock recovery. This causes the Sleeve's shock to decrease at a faster
rate.
</>
);
}
if (isSleeveFactionWork(props.sleeve.currentWork)) {
let doing = "nothing";
switch (props.sleeve.currentWork.factionWorkType) {
case FactionWorkType.FIELD:
doing = "Field work";
break; break;
default: case FactionWorkType.HACKING:
console.error(`Invalid/Unrecognized taskValue in updateSleeveTaskDescription(): ${abc[0]}`); doing = "Hacking contracts";
break;
case FactionWorkType.SECURITY:
doing = "Security work";
break;
}
desc = (
<>
This sleeve is currently doing {doing} for {props.sleeve.currentWork.factionName}.
</>
);
}
if (isSleeveCompanyWork(props.sleeve.currentWork)) {
desc = <>This sleeve is currently working your job at {props.sleeve.currentWork.companyName}.</>;
}
if (isSleeveBladeburnerGeneralWork(props.sleeve.currentWork)) {
const w = props.sleeve.currentWork;
desc = (
<>
This sleeve is currently attempting to perform {w.action}. (
{((100 * w.cyclesWorked) / w.cyclesNeeded(player, props.sleeve)).toFixed(2)}%)
</>
);
}
if (isSleeveInfiltrateWork(props.sleeve.currentWork)) {
const w = props.sleeve.currentWork;
desc = (
<>
This sleeve is currently attempting to infiltrate synthoids communities. (
{((100 * w.cyclesWorked) / w.cyclesNeeded()).toFixed(2)}%)
</>
);
} }
return ( return (
+43 -1
View File
@@ -13,6 +13,9 @@ import { use } from "../../../ui/Context";
import { Sleeve } from "../Sleeve"; import { Sleeve } from "../Sleeve";
import { SleeveTaskType } from "../SleeveTaskTypesEnum"; import { SleeveTaskType } from "../SleeveTaskTypesEnum";
import { isSleeveClassWork } from "../Work/SleeveClassWork";
import { isSleeveFactionWork } from "../Work/SleeveFactionWork";
import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork";
interface IProps { interface IProps {
sleeve: Sleeve; sleeve: Sleeve;
@@ -124,13 +127,52 @@ export function EarningsElement(props: IProps): React.ReactElement {
data.push([`Reputation:`, <ReputationRate reputation={5 * repGain} />]); data.push([`Reputation:`, <ReputationRate reputation={5 * repGain} />]);
} }
} }
if (isSleeveClassWork(props.sleeve.currentWork)) {
const rates = props.sleeve.currentWork.calculateRates(player, props.sleeve);
data = [
[`Money:`, <MoneyRate money={5 * rates.money} />],
[`Hacking Exp:`, `${numeralWrapper.formatExp(5 * rates.hackExp)} / sec`],
[`Strength Exp:`, `${numeralWrapper.formatExp(5 * rates.strExp)} / sec`],
[`Defense Exp:`, `${numeralWrapper.formatExp(5 * rates.defExp)} / sec`],
[`Dexterity Exp:`, `${numeralWrapper.formatExp(5 * rates.dexExp)} / sec`],
[`Agility Exp:`, `${numeralWrapper.formatExp(5 * rates.agiExp)} / sec`],
[`Charisma Exp:`, `${numeralWrapper.formatExp(5 * rates.chaExp)} / sec`],
];
}
if (isSleeveFactionWork(props.sleeve.currentWork)) {
const rates = props.sleeve.currentWork.getExpRates(props.sleeve);
const repGain = props.sleeve.currentWork.getReputationRate(props.sleeve);
data = [
[`Hacking Exp:`, `${numeralWrapper.formatExp(5 * rates.hackExp)} / sec`],
[`Strength Exp:`, `${numeralWrapper.formatExp(5 * rates.strExp)} / sec`],
[`Defense Exp:`, `${numeralWrapper.formatExp(5 * rates.defExp)} / sec`],
[`Dexterity Exp:`, `${numeralWrapper.formatExp(5 * rates.dexExp)} / sec`],
[`Agility Exp:`, `${numeralWrapper.formatExp(5 * rates.agiExp)} / sec`],
[`Charisma Exp:`, `${numeralWrapper.formatExp(5 * rates.chaExp)} / sec`],
[`Reputation:`, <ReputationRate reputation={5 * repGain} />],
];
}
if (isSleeveCompanyWork(props.sleeve.currentWork)) {
const rates = props.sleeve.currentWork.getGainRates(player, props.sleeve);
data = [
[`Money:`, <MoneyRate money={5 * rates.money} />],
[`Hacking Exp:`, `${numeralWrapper.formatExp(5 * rates.hackExp)} / sec`],
[`Strength Exp:`, `${numeralWrapper.formatExp(5 * rates.strExp)} / sec`],
[`Defense Exp:`, `${numeralWrapper.formatExp(5 * rates.defExp)} / sec`],
[`Dexterity Exp:`, `${numeralWrapper.formatExp(5 * rates.dexExp)} / sec`],
[`Agility Exp:`, `${numeralWrapper.formatExp(5 * rates.agiExp)} / sec`],
[`Charisma Exp:`, `${numeralWrapper.formatExp(5 * rates.chaExp)} / sec`],
[`Reputation:`, <ReputationRate reputation={5 * rates.reputation} />],
];
}
return ( return (
<Table sx={{ display: "table", mb: 1, width: "100%", lineHeight: 0 }}> <Table sx={{ display: "table", mb: 1, width: "100%", lineHeight: 0 }}>
<TableBody> <TableBody>
<TableRow> <TableRow>
<TableCell classes={{ root: classes.cellNone }}> <TableCell classes={{ root: classes.cellNone }}>
<Typography variant="h6">Earnings</Typography> <Typography variant="h6">Earnings {props.sleeve.storedCycles > 50 ? "(overclock)" : ""}</Typography>
</TableCell> </TableCell>
</TableRow> </TableRow>
{data.map(([a, b]) => ( {data.map(([a, b]) => (
+4 -4
View File
@@ -1,8 +1,8 @@
import { IPlayer } from "../IPlayer";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { CalculateShareMult } from "../../NetworkShare/Share"; import { CalculateShareMult } from "../../NetworkShare/Share";
import { IPerson } from "../IPerson";
function mult(f: Faction): number { function mult(f: Faction): number {
let favorMult = 1 + f.favor / 100; let favorMult = 1 + f.favor / 100;
@@ -12,7 +12,7 @@ function mult(f: Faction): number {
return favorMult * BitNodeMultipliers.FactionWorkRepGain; return favorMult * BitNodeMultipliers.FactionWorkRepGain;
} }
export function getHackingWorkRepGain(p: IPlayer, f: Faction): number { export function getHackingWorkRepGain(p: IPerson, f: Faction): number {
return ( return (
((p.skills.hacking + p.skills.intelligence / 3) / CONSTANTS.MaxSkillLevel) * ((p.skills.hacking + p.skills.intelligence / 3) / CONSTANTS.MaxSkillLevel) *
p.mults.faction_rep * p.mults.faction_rep *
@@ -22,7 +22,7 @@ export function getHackingWorkRepGain(p: IPlayer, f: Faction): number {
); );
} }
export function getFactionSecurityWorkRepGain(p: IPlayer, f: Faction): number { export function getFactionSecurityWorkRepGain(p: IPerson, f: Faction): number {
const t = const t =
(0.9 * (0.9 *
(p.skills.strength + (p.skills.strength +
@@ -35,7 +35,7 @@ export function getFactionSecurityWorkRepGain(p: IPlayer, f: Faction): number {
return t * p.mults.faction_rep * mult(f) * p.getIntelligenceBonus(1); return t * p.mults.faction_rep * mult(f) * p.getIntelligenceBonus(1);
} }
export function getFactionFieldWorkRepGain(p: IPlayer, f: Faction): number { export function getFactionFieldWorkRepGain(p: IPerson, f: Faction): number {
const t = const t =
(0.9 * (0.9 *
(p.skills.strength + (p.skills.strength +
+2 -2
View File
@@ -147,13 +147,13 @@ export class ClassWork extends Work {
} }
calculateRates(player: IPlayer): WorkStats { calculateRates(player: IPlayer): WorkStats {
return calculateClassEarningsRate(player, this); return calculateClassEarningsRate(player, player, this.classType, this.location);
} }
process(player: IPlayer, cycles: number): boolean { process(player: IPlayer, cycles: number): boolean {
this.cyclesWorked += cycles; this.cyclesWorked += cycles;
const rate = this.calculateRates(player); const rate = this.calculateRates(player);
const earnings = applyWorkStats(player, rate, cycles, "class"); const earnings = applyWorkStats(player, player, rate, cycles, "class");
this.earnings = sumWorkStats(this.earnings, earnings); this.earnings = sumWorkStats(this.earnings, earnings);
return false; return false;
} }
+8 -2
View File
@@ -10,6 +10,8 @@ import { applyWorkStats, WorkStats } from "./WorkStats";
import { Company } from "../Company/Company"; import { Company } from "../Company/Company";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reputation } from "../ui/React/Reputation"; import { Reputation } from "../ui/React/Reputation";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { CONSTANTS } from "../Constants";
interface CompanyWorkParams { interface CompanyWorkParams {
companyName: string; companyName: string;
@@ -32,14 +34,18 @@ export class CompanyWork extends Work {
} }
getGainRates(player: IPlayer): WorkStats { getGainRates(player: IPlayer): WorkStats {
return calculateCompanyWorkStats(player, this.getCompany()); let focusBonus = 1;
if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager)) {
focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus;
}
return calculateCompanyWorkStats(player, player, this.getCompany());
} }
process(player: IPlayer, cycles: number): boolean { process(player: IPlayer, cycles: number): boolean {
this.cyclesWorked += cycles; this.cyclesWorked += cycles;
const company = this.getCompany(); const company = this.getCompany();
const gains = this.getGainRates(player); const gains = this.getGainRates(player);
applyWorkStats(player, gains, cycles, "work"); applyWorkStats(player, player, gains, cycles, "work");
company.playerReputation += gains.reputation * cycles; company.playerReputation += gains.reputation * cycles;
influenceStockThroughCompanyWork(company, gains.reputation, cycles); influenceStockThroughCompanyWork(company, gains.reputation, cycles);
return false; return false;
+8 -3
View File
@@ -5,7 +5,7 @@ import { IPlayer } from "../PersonObjects/IPlayer";
import { FactionNames } from "../Faction/data/FactionNames"; import { FactionNames } from "../Faction/data/FactionNames";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { Faction } from "../Faction/Faction"; import { Faction } from "../Faction/Faction";
import { applyWorkStats, WorkStats } from "./WorkStats"; import { applyWorkStats, scaleWorkStats, WorkStats } from "./WorkStats";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reputation } from "../ui/React/Reputation"; import { Reputation } from "../ui/React/Reputation";
import { import {
@@ -58,7 +58,12 @@ export class FactionWork extends Work {
} }
getExpRates(player: IPlayer): WorkStats { getExpRates(player: IPlayer): WorkStats {
return calculateFactionExp(player, this.factionWorkType); let focusBonus = 1;
if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager)) {
focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus;
}
const rate = calculateFactionExp(player, this.factionWorkType);
return scaleWorkStats(rate, focusBonus, false);
} }
process(player: IPlayer, cycles: number): boolean { process(player: IPlayer, cycles: number): boolean {
@@ -66,7 +71,7 @@ export class FactionWork extends Work {
this.getFaction().playerReputation += this.getReputationRate(player) * cycles; this.getFaction().playerReputation += this.getReputationRate(player) * cycles;
const rate = this.getExpRates(player); const rate = this.getExpRates(player);
applyWorkStats(player, rate, cycles, "class"); applyWorkStats(player, player, rate, cycles, "class");
return false; return false;
} }
+36 -11
View File
@@ -1,3 +1,4 @@
import { IPerson } from "src/PersonObjects/IPerson";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
export interface WorkStats { export interface WorkStats {
@@ -52,9 +53,10 @@ export const sumWorkStats = (w0: WorkStats, w1: WorkStats): WorkStats => {
}; };
}; };
export const scaleWorkStats = (w: WorkStats, n: number): WorkStats => { export const scaleWorkStats = (w: WorkStats, n: number, scaleMoney = true): WorkStats => {
const m = scaleMoney ? n : 1;
return { return {
money: w.money * n, money: w.money * m,
reputation: w.reputation * n, reputation: w.reputation * n,
hackExp: w.hackExp * n, hackExp: w.hackExp * n,
strExp: w.strExp * n, strExp: w.strExp * n,
@@ -66,10 +68,34 @@ export const scaleWorkStats = (w: WorkStats, n: number): WorkStats => {
}; };
}; };
export const applyWorkStats = (player: IPlayer, workStats: WorkStats, cycles: number, source: string): WorkStats => { export const applyWorkStats = (
player: IPlayer,
target: IPerson,
workStats: WorkStats,
cycles: number,
source: string,
): WorkStats => {
const expStats = applyWorkStatsExp(target, workStats, cycles);
const gains = { const gains = {
money: workStats.money * cycles, money: workStats.money * cycles,
reputation: 0, reputation: 0,
hackExp: expStats.hackExp,
strExp: expStats.strExp,
defExp: expStats.defExp,
dexExp: expStats.dexExp,
agiExp: expStats.agiExp,
chaExp: expStats.chaExp,
intExp: expStats.intExp,
};
player.gainMoney(gains.money, source);
return gains;
};
export const applyWorkStatsExp = (target: IPerson, workStats: WorkStats, cycles: number): WorkStats => {
const gains = {
money: 0,
reputation: 0,
hackExp: workStats.hackExp * cycles, hackExp: workStats.hackExp * cycles,
strExp: workStats.strExp * cycles, strExp: workStats.strExp * cycles,
defExp: workStats.defExp * cycles, defExp: workStats.defExp * cycles,
@@ -78,13 +104,12 @@ export const applyWorkStats = (player: IPlayer, workStats: WorkStats, cycles: nu
chaExp: workStats.chaExp * cycles, chaExp: workStats.chaExp * cycles,
intExp: workStats.intExp * cycles, intExp: workStats.intExp * cycles,
}; };
player.gainHackingExp(gains.hackExp); target.gainHackingExp(gains.hackExp);
player.gainStrengthExp(gains.strExp); target.gainStrengthExp(gains.strExp);
player.gainDefenseExp(gains.defExp); target.gainDefenseExp(gains.defExp);
player.gainDexterityExp(gains.dexExp); target.gainDexterityExp(gains.dexExp);
player.gainAgilityExp(gains.agiExp); target.gainAgilityExp(gains.agiExp);
player.gainCharismaExp(gains.chaExp); target.gainCharismaExp(gains.chaExp);
player.gainIntelligenceExp(gains.intExp); target.gainIntelligenceExp(gains.intExp);
player.gainMoney(gains.money, source);
return gains; return gains;
}; };
+22 -11
View File
@@ -3,11 +3,13 @@ import { Location } from "../../Locations/Location";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { Class, Classes, ClassWork } from "../ClassWork"; import { Class, Classes, ClassType } from "../ClassWork";
import { WorkStats } from "../WorkStats"; import { WorkStats } from "../WorkStats";
import { Server } from "../../Server/Server"; import { Server } from "../../Server/Server";
import { GetServer } from "../../Server/AllServers"; import { GetServer } from "../../Server/AllServers";
import { serverMetadata } from "../../Server/data/servers"; import { serverMetadata } from "../../Server/data/servers";
import { IPerson } from "../../PersonObjects/IPerson";
import { LocationName } from "../../Locations/data/LocationNames";
const gameCPS = 1000 / CONSTANTS._idleSpeed; // 5 cycles per second const gameCPS = 1000 / CONSTANTS._idleSpeed; // 5 cycles per second
@@ -18,13 +20,22 @@ export function calculateCost(classs: Class, location: Location): number {
return classs.earnings.money * location.costMult * discount; return classs.earnings.money * location.costMult * discount;
} }
export function calculateClassEarnings(player: IPlayer, work: ClassWork): WorkStats { export function calculateClassEarnings(
player: IPlayer,
target: IPerson,
type: ClassType,
locationName: LocationName,
): WorkStats {
//Find cost and exp gain per game cycle //Find cost and exp gain per game cycle
const hashManager = player.hashManager; const hashManager = player.hashManager;
const classs = Classes[work.classType]; const classs = Classes[type];
const location = Locations[work.location]; const location = Locations[locationName];
const hashMult = work.isGym() ? hashManager.getTrainingMult() : hashManager.getStudyMult(); const hashMult = [ClassType.GymAgility, ClassType.GymDefense, ClassType.GymStrength, ClassType.GymDexterity].includes(
type,
)
? hashManager.getTrainingMult()
: hashManager.getStudyMult();
const cost = calculateCost(classs, location) / gameCPS; const cost = calculateCost(classs, location) / gameCPS;
const hackExp = ((classs.earnings.hackExp * location.expMult) / gameCPS) * hashMult; const hackExp = ((classs.earnings.hackExp * location.expMult) / gameCPS) * hashMult;
@@ -36,12 +47,12 @@ export function calculateClassEarnings(player: IPlayer, work: ClassWork): WorkSt
return { return {
money: cost, money: cost,
reputation: 0, reputation: 0,
hackExp: hackExp * player.mults.hacking_exp * BitNodeMultipliers.ClassGymExpGain, hackExp: hackExp * target.mults.hacking_exp * BitNodeMultipliers.ClassGymExpGain,
strExp: strExp * player.mults.strength_exp * BitNodeMultipliers.ClassGymExpGain, strExp: strExp * target.mults.strength_exp * BitNodeMultipliers.ClassGymExpGain,
defExp: defExp * player.mults.defense_exp * BitNodeMultipliers.ClassGymExpGain, defExp: defExp * target.mults.defense_exp * BitNodeMultipliers.ClassGymExpGain,
dexExp: dexExp * player.mults.dexterity_exp * BitNodeMultipliers.ClassGymExpGain, dexExp: dexExp * target.mults.dexterity_exp * BitNodeMultipliers.ClassGymExpGain,
agiExp: agiExp * player.mults.agility_exp * BitNodeMultipliers.ClassGymExpGain, agiExp: agiExp * target.mults.agility_exp * BitNodeMultipliers.ClassGymExpGain,
chaExp: chaExp * player.mults.charisma_exp * BitNodeMultipliers.ClassGymExpGain, chaExp: chaExp * target.mults.charisma_exp * BitNodeMultipliers.ClassGymExpGain,
intExp: 0, intExp: 0,
}; };
} }
+17 -28
View File
@@ -5,16 +5,12 @@ import { WorkStats } from "../WorkStats";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { IPerson } from "src/PersonObjects/IPerson";
export const calculateCompanyWorkStats = (player: IPlayer, company: Company): WorkStats => { export const calculateCompanyWorkStats = (player: IPlayer, worker: IPerson, company: Company): WorkStats => {
const companyPositionName = player.jobs[company.name]; const companyPositionName = player.jobs[company.name];
const companyPosition = CompanyPositions[companyPositionName]; const companyPosition = CompanyPositions[companyPositionName];
let focusBonus = 1;
if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager)) {
focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus;
}
// If player has SF-11, calculate salary multiplier from favor // If player has SF-11, calculate salary multiplier from favor
let favorMult = 1 + company.favor / 100; let favorMult = 1 + company.favor / 100;
if (isNaN(favorMult)) { if (isNaN(favorMult)) {
@@ -27,60 +23,53 @@ export const calculateCompanyWorkStats = (player: IPlayer, company: Company): Wo
} }
let jobPerformance = companyPosition.calculateJobPerformance( let jobPerformance = companyPosition.calculateJobPerformance(
player.skills.hacking, worker.skills.hacking,
player.skills.strength, worker.skills.strength,
player.skills.defense, worker.skills.defense,
player.skills.dexterity, worker.skills.dexterity,
player.skills.agility, worker.skills.agility,
player.skills.charisma, worker.skills.charisma,
); );
jobPerformance += player.skills.intelligence / CONSTANTS.MaxSkillLevel; jobPerformance += worker.skills.intelligence / CONSTANTS.MaxSkillLevel;
return { return {
money: money:
focusBonus *
companyPosition.baseSalary * companyPosition.baseSalary *
company.salaryMultiplier * company.salaryMultiplier *
player.mults.work_money * worker.mults.work_money *
BitNodeMultipliers.CompanyWorkMoney * BitNodeMultipliers.CompanyWorkMoney *
bn11Mult, bn11Mult,
reputation: focusBonus * jobPerformance * player.mults.company_rep * favorMult, reputation: jobPerformance * worker.mults.company_rep * favorMult,
hackExp: hackExp:
focusBonus *
companyPosition.hackingExpGain * companyPosition.hackingExpGain *
company.expMultiplier * company.expMultiplier *
player.mults.hacking_exp * worker.mults.hacking_exp *
BitNodeMultipliers.CompanyWorkExpGain, BitNodeMultipliers.CompanyWorkExpGain,
strExp: strExp:
focusBonus *
companyPosition.strengthExpGain * companyPosition.strengthExpGain *
company.expMultiplier * company.expMultiplier *
player.mults.strength_exp * worker.mults.strength_exp *
BitNodeMultipliers.CompanyWorkExpGain, BitNodeMultipliers.CompanyWorkExpGain,
defExp: defExp:
focusBonus *
companyPosition.defenseExpGain * companyPosition.defenseExpGain *
company.expMultiplier * company.expMultiplier *
player.mults.defense_exp * worker.mults.defense_exp *
BitNodeMultipliers.CompanyWorkExpGain, BitNodeMultipliers.CompanyWorkExpGain,
dexExp: dexExp:
focusBonus *
companyPosition.dexterityExpGain * companyPosition.dexterityExpGain *
company.expMultiplier * company.expMultiplier *
player.mults.dexterity_exp * worker.mults.dexterity_exp *
BitNodeMultipliers.CompanyWorkExpGain, BitNodeMultipliers.CompanyWorkExpGain,
agiExp: agiExp:
focusBonus *
companyPosition.agilityExpGain * companyPosition.agilityExpGain *
company.expMultiplier * company.expMultiplier *
player.mults.agility_exp * worker.mults.agility_exp *
BitNodeMultipliers.CompanyWorkExpGain, BitNodeMultipliers.CompanyWorkExpGain,
chaExp: chaExp:
focusBonus *
companyPosition.charismaExpGain * companyPosition.charismaExpGain *
company.expMultiplier * company.expMultiplier *
player.mults.charisma_exp * worker.mults.charisma_exp *
BitNodeMultipliers.CompanyWorkExpGain, BitNodeMultipliers.CompanyWorkExpGain,
intExp: 0, intExp: 0,
}; };
+8 -19
View File
@@ -1,7 +1,6 @@
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { IPerson } from "../../PersonObjects/IPerson";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { FactionWorkType } from "../data/FactionWorkType"; import { FactionWorkType } from "../data/FactionWorkType";
import { newWorkStats, WorkStats } from "../WorkStats"; import { newWorkStats, WorkStats } from "../WorkStats";
@@ -26,27 +25,17 @@ export const FactionWorkStats: Record<FactionWorkType, WorkStats> = {
}), }),
}; };
export function calculateFactionExp(player: IPlayer, tpe: FactionWorkType): WorkStats { export function calculateFactionExp(person: IPerson, tpe: FactionWorkType): WorkStats {
let focusBonus = 1;
if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager)) {
focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus;
}
const baseStats = FactionWorkStats[tpe]; const baseStats = FactionWorkStats[tpe];
return { return {
money: 0, money: 0,
reputation: 0, reputation: 0,
hackExp: hackExp: (baseStats.hackExp * person.mults.hacking_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
(focusBonus * (baseStats.hackExp * player.mults.hacking_exp * BitNodeMultipliers.FactionWorkExpGain)) / gameCPS, strExp: (baseStats.strExp * person.mults.strength_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
strExp: defExp: (baseStats.defExp * person.mults.defense_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
(focusBonus * (baseStats.strExp * player.mults.strength_exp * BitNodeMultipliers.FactionWorkExpGain)) / gameCPS, dexExp: (baseStats.dexExp * person.mults.dexterity_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
defExp: agiExp: (baseStats.agiExp * person.mults.agility_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
(focusBonus * (baseStats.defExp * player.mults.defense_exp * BitNodeMultipliers.FactionWorkExpGain)) / gameCPS, chaExp: (baseStats.chaExp * person.mults.charisma_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
dexExp:
(focusBonus * (baseStats.dexExp * player.mults.dexterity_exp * BitNodeMultipliers.FactionWorkExpGain)) / gameCPS,
agiExp:
(focusBonus * (baseStats.agiExp * player.mults.agility_exp * BitNodeMultipliers.FactionWorkExpGain)) / gameCPS,
chaExp:
(focusBonus * (baseStats.chaExp * player.mults.charisma_exp * BitNodeMultipliers.FactionWorkExpGain)) / gameCPS,
intExp: 0, intExp: 0,
}; };
} }