mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-20 05:05:47 +01:00
work on sleeve new work system
This commit is contained in:
parent
315b2adf30
commit
ebe953b498
@ -37,8 +37,18 @@ import { BladeburnerConstants } from "../../Bladeburner/data/Constants";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { capitalizeFirstLetter, capitalizeEachWord } from "../../utils/StringHelperFunctions";
|
||||
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 {
|
||||
currentWork: Work | null = null;
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@ -184,9 +201,8 @@ export class Sleeve extends Person {
|
||||
/**
|
||||
* Called to stop the current task
|
||||
*/
|
||||
finishTask(p: IPlayer): ITaskTracker {
|
||||
let retValue: ITaskTracker = createTaskTracker(); // Amount of exp to be gained by other sleeves
|
||||
|
||||
finishTask(p: IPlayer): void {
|
||||
this.currentWork = null;
|
||||
if (this.currentTask === SleeveTaskType.Crime) {
|
||||
// For crimes, all experience and money is gained at the end
|
||||
if (this.currentTaskTime >= this.currentTaskMaxTime) {
|
||||
@ -194,7 +210,7 @@ export class Sleeve extends Person {
|
||||
if (!crime) {
|
||||
console.error(`Invalid data stored in sleeve.crimeType: ${this.crimeType}`);
|
||||
this.resetTaskStatus(p);
|
||||
return retValue;
|
||||
return;
|
||||
}
|
||||
if (Math.random() < crime.successRate(this)) {
|
||||
// Success
|
||||
@ -205,22 +221,22 @@ export class Sleeve extends Person {
|
||||
const key = keysForIteration[i];
|
||||
successGainRates[key] = this.gainRatesForTask[key] * 2;
|
||||
}
|
||||
retValue = this.gainExperience(p, successGainRates);
|
||||
this.gainExperience(p, successGainRates);
|
||||
this.gainMoney(p, this.gainRatesForTask);
|
||||
|
||||
p.karma -= crime.karma * (this.sync / 100);
|
||||
} else {
|
||||
retValue = this.gainExperience(p, this.gainRatesForTask);
|
||||
this.gainExperience(p, this.gainRatesForTask);
|
||||
}
|
||||
|
||||
// Do not reset task to IDLE
|
||||
this.currentTaskTime = 0;
|
||||
return retValue;
|
||||
return;
|
||||
}
|
||||
} else if (this.currentTask === SleeveTaskType.Bladeburner) {
|
||||
if (this.currentTaskMaxTime === 0) {
|
||||
this.currentTaskTime = 0;
|
||||
return retValue;
|
||||
return;
|
||||
}
|
||||
// For bladeburner, all experience and money is gained at the end
|
||||
const bb = p.bladeburner;
|
||||
@ -228,14 +244,14 @@ export class Sleeve extends Person {
|
||||
const errorLogText = `bladeburner is null`;
|
||||
console.error(`Function: sleeves.finishTask; Message: '${errorLogText}'`);
|
||||
this.resetTaskStatus(p);
|
||||
return retValue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.currentTaskTime >= this.currentTaskMaxTime) {
|
||||
if (this.bbAction === "Infiltrate synthoids") {
|
||||
bb.infiltrateSynthoidCommunities(p);
|
||||
this.currentTaskTime = 0;
|
||||
return retValue;
|
||||
return;
|
||||
}
|
||||
let type: string;
|
||||
let name: string;
|
||||
@ -252,19 +268,19 @@ export class Sleeve extends Person {
|
||||
const errorLogText = `Invalid action: type='${type}' name='${name}'`;
|
||||
console.error(`Function: sleeves.finishTask; Message: '${errorLogText}'`);
|
||||
this.resetTaskStatus(p);
|
||||
return retValue;
|
||||
return;
|
||||
}
|
||||
|
||||
const action = bb.getActionObject(actionIdent);
|
||||
if ((action?.count ?? 0) > 0) {
|
||||
const bbRetValue = bb.completeAction(p, this, actionIdent, false);
|
||||
if (bbRetValue) {
|
||||
retValue = this.gainExperience(p, bbRetValue);
|
||||
this.gainExperience(p, bbRetValue);
|
||||
this.gainMoney(p, bbRetValue);
|
||||
|
||||
// Do not reset task to IDLE
|
||||
this.currentTaskTime = 0;
|
||||
return retValue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -272,7 +288,7 @@ export class Sleeve extends Person {
|
||||
|
||||
this.resetTaskStatus(p);
|
||||
|
||||
return retValue;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -431,25 +447,7 @@ export class Sleeve extends Person {
|
||||
* Only applicable when working for company or faction
|
||||
*/
|
||||
getRepGain(p: IPlayer): number {
|
||||
if (this.currentTask === SleeveTaskType.Faction) {
|
||||
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) {
|
||||
if (this.currentTask === SleeveTaskType.Company) {
|
||||
const companyName: string = this.currentTaskLocation;
|
||||
const company: Company | null = Companies[companyName];
|
||||
if (company == null) {
|
||||
@ -532,12 +530,16 @@ export class Sleeve extends Person {
|
||||
// Only process once every second (5 cycles)
|
||||
const CyclesPerSecond = 1000 / CONSTANTS.MilliPerCycle;
|
||||
this.storedCycles += numCycles;
|
||||
if (this.storedCycles < CyclesPerSecond) {
|
||||
return;
|
||||
}
|
||||
if (this.storedCycles < CyclesPerSecond) return;
|
||||
|
||||
let cyclesUsed = this.storedCycles;
|
||||
cyclesUsed = Math.min(cyclesUsed, 15);
|
||||
if (this.currentWork) {
|
||||
this.currentWork.process(p, this, cyclesUsed);
|
||||
this.storedCycles -= cyclesUsed;
|
||||
return;
|
||||
}
|
||||
|
||||
let time = cyclesUsed * CONSTANTS.MilliPerCycle;
|
||||
if (this.currentTaskMaxTime !== 0 && this.currentTaskTime + time > this.currentTaskMaxTime) {
|
||||
time = this.currentTaskMaxTime - this.currentTaskTime;
|
||||
@ -556,36 +558,6 @@ export class Sleeve extends Person {
|
||||
this.shock = Math.min(100, this.shock + 0.0001 * cyclesUsed);
|
||||
|
||||
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: {
|
||||
this.gainExperience(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;
|
||||
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) {
|
||||
@ -630,15 +592,10 @@ export class Sleeve extends Person {
|
||||
* Resets all parameters used to keep information about the current task
|
||||
*/
|
||||
resetTaskStatus(p: IPlayer): void {
|
||||
this.currentWork = null;
|
||||
if (this.bbAction == "Support main sleeve") {
|
||||
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.gainRatesForTask = createTaskTracker();
|
||||
this.currentTask = SleeveTaskType.Idle;
|
||||
@ -654,24 +611,22 @@ export class Sleeve extends Person {
|
||||
}
|
||||
|
||||
shockRecovery(p: IPlayer): boolean {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
if (this.currentTask !== SleeveTaskType.Idle || this.currentWork === null) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus(p);
|
||||
}
|
||||
|
||||
this.currentTask = SleeveTaskType.Recovery;
|
||||
this.currentWork = new SleeveRecoveryWork();
|
||||
return true;
|
||||
}
|
||||
|
||||
synchronize(p: IPlayer): boolean {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
if (this.currentTask !== SleeveTaskType.Idle || this.currentWork !== null) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus(p);
|
||||
}
|
||||
|
||||
this.currentTask = SleeveTaskType.Synchro;
|
||||
this.currentWork = new SleeveSynchroWork();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -679,7 +634,7 @@ export class Sleeve extends Person {
|
||||
* Take a course at a university
|
||||
*/
|
||||
takeUniversityCourse(p: IPlayer, universityName: string, className: string): boolean {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
if (this.currentTask !== SleeveTaskType.Idle || this.currentWork) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus(p);
|
||||
@ -687,58 +642,54 @@ export class Sleeve extends Person {
|
||||
|
||||
// Set exp/money multipliers based on which university.
|
||||
// Also check that the sleeve is in the right city
|
||||
let costMult = 1;
|
||||
let loc: LocationName | undefined;
|
||||
switch (universityName.toLowerCase()) {
|
||||
case LocationName.AevumSummitUniversity.toLowerCase():
|
||||
if (this.city !== CityName.Aevum) {
|
||||
return false;
|
||||
}
|
||||
this.currentTaskLocation = LocationName.AevumSummitUniversity;
|
||||
costMult = 4;
|
||||
case LocationName.AevumSummitUniversity.toLowerCase(): {
|
||||
if (this.city !== CityName.Aevum) return false;
|
||||
loc = LocationName.AevumSummitUniversity;
|
||||
break;
|
||||
case LocationName.Sector12RothmanUniversity.toLowerCase():
|
||||
if (this.city !== CityName.Sector12) {
|
||||
return false;
|
||||
}
|
||||
this.currentTaskLocation = LocationName.Sector12RothmanUniversity;
|
||||
costMult = 3;
|
||||
}
|
||||
case LocationName.Sector12RothmanUniversity.toLowerCase(): {
|
||||
if (this.city !== CityName.Sector12) return false;
|
||||
loc = LocationName.Sector12RothmanUniversity;
|
||||
break;
|
||||
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase():
|
||||
if (this.city !== CityName.Volhaven) {
|
||||
return false;
|
||||
}
|
||||
this.currentTaskLocation = LocationName.VolhavenZBInstituteOfTechnology;
|
||||
costMult = 5;
|
||||
}
|
||||
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase(): {
|
||||
if (this.city !== CityName.Volhaven) return false;
|
||||
loc = LocationName.VolhavenZBInstituteOfTechnology;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!loc) return false;
|
||||
|
||||
// Set experience/money gains based on class
|
||||
let classType: ClassType | undefined;
|
||||
switch (className.toLowerCase()) {
|
||||
case "study computer science":
|
||||
classType = ClassType.StudyComputerScience;
|
||||
break;
|
||||
case "data structures":
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassDataStructuresBaseCost * costMult);
|
||||
classType = ClassType.DataStructures;
|
||||
break;
|
||||
case "networks":
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassNetworksBaseCost * costMult);
|
||||
classType = ClassType.Networks;
|
||||
break;
|
||||
case "algorithms":
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassAlgorithmsBaseCost * costMult);
|
||||
classType = ClassType.Algorithms;
|
||||
break;
|
||||
case "management":
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassManagementBaseCost * costMult);
|
||||
classType = ClassType.Management;
|
||||
break;
|
||||
case "leadership":
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassLeadershipBaseCost * costMult);
|
||||
classType = ClassType.Leadership;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
if (!classType) return false;
|
||||
|
||||
this.className = className;
|
||||
this.currentTask = SleeveTaskType.Class;
|
||||
this.currentWork = new SleeveClassWork({
|
||||
classType: classType,
|
||||
location: loc,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -767,99 +718,6 @@ export class Sleeve extends Person {
|
||||
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 {
|
||||
if (n < 0) {
|
||||
console.warn(`Sleeve.upgradeMemory() called with negative value: ${n}`);
|
||||
@ -878,7 +736,7 @@ export class Sleeve extends Person {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
if (this.currentTask !== SleeveTaskType.Idle || this.currentWork !== null) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus(p);
|
||||
@ -886,50 +744,10 @@ export class Sleeve extends Person {
|
||||
|
||||
const company: Company | null = Companies[companyName];
|
||||
const companyPosition: CompanyPosition | null = CompanyPositions[p.jobs[companyName]];
|
||||
if (company == 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;
|
||||
if (company == null) return false;
|
||||
if (companyPosition == null) return false;
|
||||
|
||||
this.currentTaskLocation = companyName;
|
||||
this.currentTask = SleeveTaskType.Company;
|
||||
this.currentWork = new SleeveCompanyWork({ companyName: companyName });
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -944,7 +762,7 @@ export class Sleeve extends Person {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
if (this.currentTask !== SleeveTaskType.Idle || this.currentWork === null) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus(p);
|
||||
@ -953,40 +771,25 @@ export class Sleeve extends Person {
|
||||
const factionInfo = faction.getInfo();
|
||||
|
||||
// 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 (!factionInfo.offerHackingWork) {
|
||||
return false;
|
||||
}
|
||||
this.factionWorkType = FactionWorkType.HACKING;
|
||||
this.gainRatesForTask.hack = 0.15 * this.mults.hacking_exp * BitNodeMultipliers.FactionWorkExpGain;
|
||||
if (!factionInfo.offerHackingWork) return false;
|
||||
factionWorkType = FactionWorkType.HACKING;
|
||||
} else if (sanitizedWorkType.includes("field")) {
|
||||
if (!factionInfo.offerFieldWork) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
if (!factionInfo.offerFieldWork) return false;
|
||||
factionWorkType = FactionWorkType.FIELD;
|
||||
} else if (sanitizedWorkType.includes("security")) {
|
||||
if (!factionInfo.offerSecurityWork) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
if (!factionInfo.offerSecurityWork) return false;
|
||||
factionWorkType = FactionWorkType.SECURITY;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.currentTaskLocation = factionName;
|
||||
this.currentTask = SleeveTaskType.Faction;
|
||||
this.currentWork = new SleeveFactionWork({
|
||||
factionWorkType: factionWorkType,
|
||||
factionName: faction.name,
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -995,7 +798,7 @@ export class Sleeve extends Person {
|
||||
* Begin a gym workout task
|
||||
*/
|
||||
workoutAtGym(p: IPlayer, gymName: string, stat: string): boolean {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
if (this.currentTask !== SleeveTaskType.Idle || this.currentWork) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus(p);
|
||||
@ -1003,73 +806,60 @@ export class Sleeve extends Person {
|
||||
|
||||
// Set exp/money multipliers based on which university.
|
||||
// Also check that the sleeve is in the right city
|
||||
let costMult = 1;
|
||||
let loc: LocationName | undefined;
|
||||
switch (gymName.toLowerCase()) {
|
||||
case LocationName.AevumCrushFitnessGym.toLowerCase():
|
||||
if (this.city != CityName.Aevum) {
|
||||
return false;
|
||||
}
|
||||
this.currentTaskLocation = LocationName.AevumCrushFitnessGym;
|
||||
costMult = 3;
|
||||
case LocationName.AevumCrushFitnessGym.toLowerCase(): {
|
||||
if (this.city != CityName.Aevum) return false;
|
||||
loc = LocationName.AevumCrushFitnessGym;
|
||||
break;
|
||||
case LocationName.AevumSnapFitnessGym.toLowerCase():
|
||||
if (this.city != CityName.Aevum) {
|
||||
return false;
|
||||
}
|
||||
this.currentTaskLocation = LocationName.AevumSnapFitnessGym;
|
||||
costMult = 10;
|
||||
}
|
||||
case LocationName.AevumSnapFitnessGym.toLowerCase(): {
|
||||
if (this.city != CityName.Aevum) return false;
|
||||
loc = LocationName.AevumSnapFitnessGym;
|
||||
break;
|
||||
case LocationName.Sector12IronGym.toLowerCase():
|
||||
if (this.city != CityName.Sector12) {
|
||||
return false;
|
||||
}
|
||||
this.currentTaskLocation = LocationName.Sector12IronGym;
|
||||
costMult = 1;
|
||||
}
|
||||
case LocationName.Sector12IronGym.toLowerCase(): {
|
||||
if (this.city != CityName.Sector12) return false;
|
||||
loc = LocationName.Sector12IronGym;
|
||||
break;
|
||||
case LocationName.Sector12PowerhouseGym.toLowerCase():
|
||||
if (this.city != CityName.Sector12) {
|
||||
return false;
|
||||
}
|
||||
this.currentTaskLocation = LocationName.Sector12PowerhouseGym;
|
||||
costMult = 20;
|
||||
}
|
||||
case LocationName.Sector12PowerhouseGym.toLowerCase(): {
|
||||
if (this.city != CityName.Sector12) return false;
|
||||
loc = LocationName.Sector12PowerhouseGym;
|
||||
break;
|
||||
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase():
|
||||
if (this.city != CityName.Volhaven) {
|
||||
return false;
|
||||
}
|
||||
this.currentTaskLocation = LocationName.VolhavenMilleniumFitnessGym;
|
||||
costMult = 7;
|
||||
}
|
||||
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase(): {
|
||||
if (this.city != CityName.Volhaven) return false;
|
||||
loc = LocationName.VolhavenMilleniumFitnessGym;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!loc) return false;
|
||||
|
||||
// Set experience/money gains based on class
|
||||
const sanitizedStat: string = stat.toLowerCase();
|
||||
|
||||
// set stat to a default value.
|
||||
stat = "none";
|
||||
let classType: ClassType | undefined;
|
||||
if (sanitizedStat.includes("str")) {
|
||||
stat = "str";
|
||||
classType = ClassType.GymStrength;
|
||||
}
|
||||
if (sanitizedStat.includes("def")) {
|
||||
stat = "def";
|
||||
classType = ClassType.GymDefense;
|
||||
}
|
||||
if (sanitizedStat.includes("dex")) {
|
||||
stat = "dex";
|
||||
classType = ClassType.GymDexterity;
|
||||
}
|
||||
if (sanitizedStat.includes("agi")) {
|
||||
stat = "agi";
|
||||
classType = ClassType.GymAgility;
|
||||
}
|
||||
// if stat is still equals its default value, then validation has failed.
|
||||
if (stat === "none") {
|
||||
return false;
|
||||
}
|
||||
if (!classType) return false;
|
||||
|
||||
// Set cost
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassGymBaseCost * costMult);
|
||||
this.gymStatType = stat;
|
||||
this.currentTask = SleeveTaskType.Gym;
|
||||
this.currentWork = new SleeveClassWork({
|
||||
classType: classType,
|
||||
location: loc,
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1078,7 +868,7 @@ export class Sleeve extends Person {
|
||||
* Begin a bladeburner task
|
||||
*/
|
||||
bladeburner(p: IPlayer, action: string, contract: string): boolean {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
if (this.currentTask !== SleeveTaskType.Idle || this.currentWork === null) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus(p);
|
||||
@ -1098,24 +888,26 @@ export class Sleeve extends Person {
|
||||
this.bbContract = "------";
|
||||
switch (action) {
|
||||
case "Field analysis":
|
||||
time = this.getBladeburnerActionTime(p, "General", action);
|
||||
this.gainRatesForTask.hack = 20 * this.mults.hacking_exp;
|
||||
this.gainRatesForTask.cha = 20 * this.mults.charisma_exp;
|
||||
break;
|
||||
// time = this.getBladeburnerActionTime(p, "General", action);
|
||||
// this.gainRatesForTask.hack = 20 * this.mults.hacking_exp;
|
||||
// this.gainRatesForTask.cha = 20 * this.mults.charisma_exp;
|
||||
this.currentWork = new SleeveBladeburnerGeneralWork("Field analysis");
|
||||
return true;
|
||||
case "Recruitment":
|
||||
time = this.getBladeburnerActionTime(p, "General", action);
|
||||
this.gainRatesForTask.cha =
|
||||
2 * BladeburnerConstants.BaseStatGain * (p.bladeburner?.getRecruitmentTime(this) ?? 0) * 1000;
|
||||
this.currentTaskLocation = `(Success Rate: ${numeralWrapper.formatPercentage(
|
||||
this.recruitmentSuccessChance(p),
|
||||
)})`;
|
||||
// time = this.getBladeburnerActionTime(p, "General", action);
|
||||
// this.gainRatesForTask.cha =
|
||||
// 2 * BladeburnerConstants.BaseStatGain * (p.bladeburner?.getRecruitmentTime(this) ?? 0) * 1000;
|
||||
// this.currentTaskLocation = `(Success Rate: ${numeralWrapper.formatPercentage(
|
||||
// this.recruitmentSuccessChance(p),
|
||||
// )})`;
|
||||
this.currentWork = new SleeveBladeburnerGeneralWork("Recruitment");
|
||||
break;
|
||||
case "Diplomacy":
|
||||
time = this.getBladeburnerActionTime(p, "General", action);
|
||||
// time = this.getBladeburnerActionTime(p, "General", action);
|
||||
this.currentWork = new SleeveBladeburnerGeneralWork("Diplomacy");
|
||||
break;
|
||||
case "Infiltrate synthoids":
|
||||
time = 60000;
|
||||
this.currentTaskLocation = "This will generate additional contracts and operations";
|
||||
this.currentWork = new SleeveInfiltrateWork();
|
||||
break;
|
||||
case "Support main sleeve":
|
||||
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;
|
71
src/PersonObjects/Sleeve/Work/SleeveClassWork.ts
Normal file
71
src/PersonObjects/Sleeve/Work/SleeveClassWork.ts
Normal file
@ -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;
|
70
src/PersonObjects/Sleeve/Work/SleeveCompanyWork.ts
Normal file
70
src/PersonObjects/Sleeve/Work/SleeveCompanyWork.ts
Normal file
@ -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;
|
96
src/PersonObjects/Sleeve/Work/SleeveFactionWork.ts
Normal file
96
src/PersonObjects/Sleeve/Work/SleeveFactionWork.ts
Normal file
@ -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;
|
54
src/PersonObjects/Sleeve/Work/SleeveInfiltrateWork.ts
Normal file
54
src/PersonObjects/Sleeve/Work/SleeveInfiltrateWork.ts
Normal file
@ -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;
|
41
src/PersonObjects/Sleeve/Work/SleeveRecoveryWork.ts
Normal file
41
src/PersonObjects/Sleeve/Work/SleeveRecoveryWork.ts
Normal file
@ -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;
|
41
src/PersonObjects/Sleeve/Work/SleeveSynchroWork.ts
Normal file
41
src/PersonObjects/Sleeve/Work/SleeveSynchroWork.ts
Normal file
@ -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
src/PersonObjects/Sleeve/Work/Work.ts
Normal file
28
src/PersonObjects/Sleeve/Work/Work.ts
Normal 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",
|
||||
}
|
@ -14,6 +14,13 @@ import { SleeveAugmentationsModal } from "./SleeveAugmentationsModal";
|
||||
import { EarningsElement, StatsElement } from "./StatsElement";
|
||||
import { TaskSelector } from "./TaskSelector";
|
||||
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 {
|
||||
sleeve: Sleeve;
|
||||
@ -69,29 +76,6 @@ export function SleeveElem(props: IProps): React.ReactElement {
|
||||
case SleeveTaskType.Idle:
|
||||
desc = <>This sleeve is currently idle</>;
|
||||
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: {
|
||||
const crime = Object.values(Crimes).find((crime) => crime.name === props.sleeve.crimeType);
|
||||
if (!crime) throw new Error("crime should not be undefined");
|
||||
@ -106,9 +90,6 @@ export function SleeveElem(props: IProps): React.ReactElement {
|
||||
case SleeveTaskType.Class:
|
||||
desc = <>This sleeve is currently studying/taking a course at {props.sleeve.currentTaskLocation}.</>;
|
||||
break;
|
||||
case SleeveTaskType.Gym:
|
||||
desc = <>This sleeve is currently working out at {props.sleeve.currentTaskLocation}.</>;
|
||||
break;
|
||||
case SleeveTaskType.Bladeburner: {
|
||||
let message = "";
|
||||
if (props.sleeve.bbContract !== "------") {
|
||||
@ -123,26 +104,75 @@ export function SleeveElem(props: IProps): React.ReactElement {
|
||||
);
|
||||
break;
|
||||
}
|
||||
case SleeveTaskType.Recovery:
|
||||
desc = (
|
||||
<>
|
||||
This sleeve is currently set to focus on shock recovery. This causes the Sleeve's shock to decrease at a
|
||||
faster rate.
|
||||
</>
|
||||
);
|
||||
break;
|
||||
case SleeveTaskType.Synchro:
|
||||
desc = (
|
||||
<>
|
||||
This sleeve is currently set to synchronize with the original consciousness. This causes the Sleeve's
|
||||
synchronization to increase.
|
||||
</>
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error(`Invalid/Unrecognized taskValue in updateSleeveTaskDescription(): ${abc[0]}`);
|
||||
}
|
||||
|
||||
if (isSleeveClassWork(props.sleeve.currentWork)) {
|
||||
if (props.sleeve.currentWork.isGym())
|
||||
desc = <>This sleeve is currently working out at {props.sleeve.currentWork.location}.</>;
|
||||
else desc = <>This sleeve is currently studying at {props.sleeve.currentWork.location}.</>;
|
||||
}
|
||||
if (isSleeveSynchroWork(props.sleeve.currentWork)) {
|
||||
desc = (
|
||||
<>
|
||||
This sleeve is currently set to synchronize with the original consciousness. This causes the Sleeve's
|
||||
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;
|
||||
case FactionWorkType.HACKING:
|
||||
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 (
|
||||
<>
|
||||
<Paper sx={{ p: 1, display: "grid", gridTemplateColumns: "1fr 1fr", width: "auto", gap: 1 }}>
|
||||
|
@ -13,6 +13,9 @@ import { use } from "../../../ui/Context";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { SleeveTaskType } from "../SleeveTaskTypesEnum";
|
||||
import { isSleeveClassWork } from "../Work/SleeveClassWork";
|
||||
import { isSleeveFactionWork } from "../Work/SleeveFactionWork";
|
||||
import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork";
|
||||
|
||||
interface IProps {
|
||||
sleeve: Sleeve;
|
||||
@ -124,13 +127,52 @@ export function EarningsElement(props: IProps): React.ReactElement {
|
||||
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 (
|
||||
<Table sx={{ display: "table", mb: 1, width: "100%", lineHeight: 0 }}>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell classes={{ root: classes.cellNone }}>
|
||||
<Typography variant="h6">Earnings</Typography>
|
||||
<Typography variant="h6">Earnings {props.sleeve.storedCycles > 50 ? "(overclock)" : ""}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{data.map(([a, b]) => (
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { IPlayer } from "../IPlayer";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { CalculateShareMult } from "../../NetworkShare/Share";
|
||||
import { IPerson } from "../IPerson";
|
||||
|
||||
function mult(f: Faction): number {
|
||||
let favorMult = 1 + f.favor / 100;
|
||||
@ -12,7 +12,7 @@ function mult(f: Faction): number {
|
||||
return favorMult * BitNodeMultipliers.FactionWorkRepGain;
|
||||
}
|
||||
|
||||
export function getHackingWorkRepGain(p: IPlayer, f: Faction): number {
|
||||
export function getHackingWorkRepGain(p: IPerson, f: Faction): number {
|
||||
return (
|
||||
((p.skills.hacking + p.skills.intelligence / 3) / CONSTANTS.MaxSkillLevel) *
|
||||
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 =
|
||||
(0.9 *
|
||||
(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);
|
||||
}
|
||||
|
||||
export function getFactionFieldWorkRepGain(p: IPlayer, f: Faction): number {
|
||||
export function getFactionFieldWorkRepGain(p: IPerson, f: Faction): number {
|
||||
const t =
|
||||
(0.9 *
|
||||
(p.skills.strength +
|
||||
|
@ -147,13 +147,13 @@ export class ClassWork extends Work {
|
||||
}
|
||||
|
||||
calculateRates(player: IPlayer): WorkStats {
|
||||
return calculateClassEarningsRate(player, this);
|
||||
return calculateClassEarningsRate(player, player, this.classType, this.location);
|
||||
}
|
||||
|
||||
process(player: IPlayer, cycles: number): boolean {
|
||||
this.cyclesWorked += cycles;
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import { applyWorkStats, WorkStats } from "./WorkStats";
|
||||
import { Company } from "../Company/Company";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { Reputation } from "../ui/React/Reputation";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
interface CompanyWorkParams {
|
||||
companyName: string;
|
||||
@ -32,14 +34,18 @@ export class CompanyWork extends Work {
|
||||
}
|
||||
|
||||
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 {
|
||||
this.cyclesWorked += cycles;
|
||||
const company = this.getCompany();
|
||||
const gains = this.getGainRates(player);
|
||||
applyWorkStats(player, gains, cycles, "work");
|
||||
applyWorkStats(player, player, gains, cycles, "work");
|
||||
company.playerReputation += gains.reputation * cycles;
|
||||
influenceStockThroughCompanyWork(company, gains.reputation, cycles);
|
||||
return false;
|
||||
|
@ -5,7 +5,7 @@ import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { FactionNames } from "../Faction/data/FactionNames";
|
||||
import { Factions } from "../Faction/Factions";
|
||||
import { Faction } from "../Faction/Faction";
|
||||
import { applyWorkStats, WorkStats } from "./WorkStats";
|
||||
import { applyWorkStats, scaleWorkStats, WorkStats } from "./WorkStats";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { Reputation } from "../ui/React/Reputation";
|
||||
import {
|
||||
@ -58,7 +58,12 @@ export class FactionWork extends Work {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -66,7 +71,7 @@ export class FactionWork extends Work {
|
||||
this.getFaction().playerReputation += this.getReputationRate(player) * cycles;
|
||||
|
||||
const rate = this.getExpRates(player);
|
||||
applyWorkStats(player, rate, cycles, "class");
|
||||
applyWorkStats(player, player, rate, cycles, "class");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { IPerson } from "src/PersonObjects/IPerson";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
||||
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 {
|
||||
money: w.money * n,
|
||||
money: w.money * m,
|
||||
reputation: w.reputation * n,
|
||||
hackExp: w.hackExp * 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 = {
|
||||
money: workStats.money * cycles,
|
||||
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,
|
||||
strExp: workStats.strExp * cycles,
|
||||
defExp: workStats.defExp * cycles,
|
||||
@ -78,13 +104,12 @@ export const applyWorkStats = (player: IPlayer, workStats: WorkStats, cycles: nu
|
||||
chaExp: workStats.chaExp * cycles,
|
||||
intExp: workStats.intExp * cycles,
|
||||
};
|
||||
player.gainHackingExp(gains.hackExp);
|
||||
player.gainStrengthExp(gains.strExp);
|
||||
player.gainDefenseExp(gains.defExp);
|
||||
player.gainDexterityExp(gains.dexExp);
|
||||
player.gainAgilityExp(gains.agiExp);
|
||||
player.gainCharismaExp(gains.chaExp);
|
||||
player.gainIntelligenceExp(gains.intExp);
|
||||
player.gainMoney(gains.money, source);
|
||||
target.gainHackingExp(gains.hackExp);
|
||||
target.gainStrengthExp(gains.strExp);
|
||||
target.gainDefenseExp(gains.defExp);
|
||||
target.gainDexterityExp(gains.dexExp);
|
||||
target.gainAgilityExp(gains.agiExp);
|
||||
target.gainCharismaExp(gains.chaExp);
|
||||
target.gainIntelligenceExp(gains.intExp);
|
||||
return gains;
|
||||
};
|
||||
|
@ -3,11 +3,13 @@ import { Location } from "../../Locations/Location";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Class, Classes, ClassWork } from "../ClassWork";
|
||||
import { Class, Classes, ClassType } from "../ClassWork";
|
||||
import { WorkStats } from "../WorkStats";
|
||||
import { Server } from "../../Server/Server";
|
||||
import { GetServer } from "../../Server/AllServers";
|
||||
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
|
||||
|
||||
@ -18,13 +20,22 @@ export function calculateCost(classs: Class, location: Location): number {
|
||||
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
|
||||
const hashManager = player.hashManager;
|
||||
const classs = Classes[work.classType];
|
||||
const location = Locations[work.location];
|
||||
const classs = Classes[type];
|
||||
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 hackExp = ((classs.earnings.hackExp * location.expMult) / gameCPS) * hashMult;
|
||||
@ -36,12 +47,12 @@ export function calculateClassEarnings(player: IPlayer, work: ClassWork): WorkSt
|
||||
return {
|
||||
money: cost,
|
||||
reputation: 0,
|
||||
hackExp: hackExp * player.mults.hacking_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
strExp: strExp * player.mults.strength_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
defExp: defExp * player.mults.defense_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
dexExp: dexExp * player.mults.dexterity_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
agiExp: agiExp * player.mults.agility_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
chaExp: chaExp * player.mults.charisma_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
hackExp: hackExp * target.mults.hacking_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
strExp: strExp * target.mults.strength_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
defExp: defExp * target.mults.defense_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
dexExp: dexExp * target.mults.dexterity_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
agiExp: agiExp * target.mults.agility_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
chaExp: chaExp * target.mults.charisma_exp * BitNodeMultipliers.ClassGymExpGain,
|
||||
intExp: 0,
|
||||
};
|
||||
}
|
||||
|
@ -5,16 +5,12 @@ import { WorkStats } from "../WorkStats";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
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 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
|
||||
let favorMult = 1 + company.favor / 100;
|
||||
if (isNaN(favorMult)) {
|
||||
@ -27,60 +23,53 @@ export const calculateCompanyWorkStats = (player: IPlayer, company: Company): Wo
|
||||
}
|
||||
|
||||
let jobPerformance = companyPosition.calculateJobPerformance(
|
||||
player.skills.hacking,
|
||||
player.skills.strength,
|
||||
player.skills.defense,
|
||||
player.skills.dexterity,
|
||||
player.skills.agility,
|
||||
player.skills.charisma,
|
||||
worker.skills.hacking,
|
||||
worker.skills.strength,
|
||||
worker.skills.defense,
|
||||
worker.skills.dexterity,
|
||||
worker.skills.agility,
|
||||
worker.skills.charisma,
|
||||
);
|
||||
|
||||
jobPerformance += player.skills.intelligence / CONSTANTS.MaxSkillLevel;
|
||||
jobPerformance += worker.skills.intelligence / CONSTANTS.MaxSkillLevel;
|
||||
|
||||
return {
|
||||
money:
|
||||
focusBonus *
|
||||
companyPosition.baseSalary *
|
||||
company.salaryMultiplier *
|
||||
player.mults.work_money *
|
||||
worker.mults.work_money *
|
||||
BitNodeMultipliers.CompanyWorkMoney *
|
||||
bn11Mult,
|
||||
reputation: focusBonus * jobPerformance * player.mults.company_rep * favorMult,
|
||||
reputation: jobPerformance * worker.mults.company_rep * favorMult,
|
||||
hackExp:
|
||||
focusBonus *
|
||||
companyPosition.hackingExpGain *
|
||||
company.expMultiplier *
|
||||
player.mults.hacking_exp *
|
||||
worker.mults.hacking_exp *
|
||||
BitNodeMultipliers.CompanyWorkExpGain,
|
||||
strExp:
|
||||
focusBonus *
|
||||
companyPosition.strengthExpGain *
|
||||
company.expMultiplier *
|
||||
player.mults.strength_exp *
|
||||
worker.mults.strength_exp *
|
||||
BitNodeMultipliers.CompanyWorkExpGain,
|
||||
defExp:
|
||||
focusBonus *
|
||||
companyPosition.defenseExpGain *
|
||||
company.expMultiplier *
|
||||
player.mults.defense_exp *
|
||||
worker.mults.defense_exp *
|
||||
BitNodeMultipliers.CompanyWorkExpGain,
|
||||
dexExp:
|
||||
focusBonus *
|
||||
companyPosition.dexterityExpGain *
|
||||
company.expMultiplier *
|
||||
player.mults.dexterity_exp *
|
||||
worker.mults.dexterity_exp *
|
||||
BitNodeMultipliers.CompanyWorkExpGain,
|
||||
agiExp:
|
||||
focusBonus *
|
||||
companyPosition.agilityExpGain *
|
||||
company.expMultiplier *
|
||||
player.mults.agility_exp *
|
||||
worker.mults.agility_exp *
|
||||
BitNodeMultipliers.CompanyWorkExpGain,
|
||||
chaExp:
|
||||
focusBonus *
|
||||
companyPosition.charismaExpGain *
|
||||
company.expMultiplier *
|
||||
player.mults.charisma_exp *
|
||||
worker.mults.charisma_exp *
|
||||
BitNodeMultipliers.CompanyWorkExpGain,
|
||||
intExp: 0,
|
||||
};
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { IPerson } from "../../PersonObjects/IPerson";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { FactionWorkType } from "../data/FactionWorkType";
|
||||
import { newWorkStats, WorkStats } from "../WorkStats";
|
||||
|
||||
@ -26,27 +25,17 @@ export const FactionWorkStats: Record<FactionWorkType, WorkStats> = {
|
||||
}),
|
||||
};
|
||||
|
||||
export function calculateFactionExp(player: IPlayer, tpe: FactionWorkType): WorkStats {
|
||||
let focusBonus = 1;
|
||||
if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager)) {
|
||||
focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus;
|
||||
}
|
||||
export function calculateFactionExp(person: IPerson, tpe: FactionWorkType): WorkStats {
|
||||
const baseStats = FactionWorkStats[tpe];
|
||||
return {
|
||||
money: 0,
|
||||
reputation: 0,
|
||||
hackExp:
|
||||
(focusBonus * (baseStats.hackExp * player.mults.hacking_exp * BitNodeMultipliers.FactionWorkExpGain)) / gameCPS,
|
||||
strExp:
|
||||
(focusBonus * (baseStats.strExp * player.mults.strength_exp * BitNodeMultipliers.FactionWorkExpGain)) / gameCPS,
|
||||
defExp:
|
||||
(focusBonus * (baseStats.defExp * player.mults.defense_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,
|
||||
hackExp: (baseStats.hackExp * person.mults.hacking_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
|
||||
strExp: (baseStats.strExp * person.mults.strength_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
|
||||
defExp: (baseStats.defExp * person.mults.defense_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
|
||||
dexExp: (baseStats.dexExp * person.mults.dexterity_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
|
||||
agiExp: (baseStats.agiExp * person.mults.agility_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
|
||||
chaExp: (baseStats.chaExp * person.mults.charisma_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
|
||||
intExp: 0,
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user