mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-19 14:13:48 +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 { 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) {
|
case LocationName.Sector12RothmanUniversity.toLowerCase(): {
|
||||||
return false;
|
if (this.city !== CityName.Sector12) return false;
|
||||||
}
|
loc = LocationName.Sector12RothmanUniversity;
|
||||||
this.currentTaskLocation = LocationName.Sector12RothmanUniversity;
|
|
||||||
costMult = 3;
|
|
||||||
break;
|
break;
|
||||||
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase():
|
}
|
||||||
if (this.city !== CityName.Volhaven) {
|
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase(): {
|
||||||
return false;
|
if (this.city !== CityName.Volhaven) return false;
|
||||||
}
|
loc = LocationName.VolhavenZBInstituteOfTechnology;
|
||||||
this.currentTaskLocation = LocationName.VolhavenZBInstituteOfTechnology;
|
|
||||||
costMult = 5;
|
|
||||||
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) {
|
case LocationName.AevumSnapFitnessGym.toLowerCase(): {
|
||||||
return false;
|
if (this.city != CityName.Aevum) return false;
|
||||||
}
|
loc = LocationName.AevumSnapFitnessGym;
|
||||||
this.currentTaskLocation = LocationName.AevumSnapFitnessGym;
|
|
||||||
costMult = 10;
|
|
||||||
break;
|
break;
|
||||||
case LocationName.Sector12IronGym.toLowerCase():
|
}
|
||||||
if (this.city != CityName.Sector12) {
|
case LocationName.Sector12IronGym.toLowerCase(): {
|
||||||
return false;
|
if (this.city != CityName.Sector12) return false;
|
||||||
}
|
loc = LocationName.Sector12IronGym;
|
||||||
this.currentTaskLocation = LocationName.Sector12IronGym;
|
|
||||||
costMult = 1;
|
|
||||||
break;
|
break;
|
||||||
case LocationName.Sector12PowerhouseGym.toLowerCase():
|
}
|
||||||
if (this.city != CityName.Sector12) {
|
case LocationName.Sector12PowerhouseGym.toLowerCase(): {
|
||||||
return false;
|
if (this.city != CityName.Sector12) return false;
|
||||||
}
|
loc = LocationName.Sector12PowerhouseGym;
|
||||||
this.currentTaskLocation = LocationName.Sector12PowerhouseGym;
|
|
||||||
costMult = 20;
|
|
||||||
break;
|
break;
|
||||||
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase():
|
}
|
||||||
if (this.city != CityName.Volhaven) {
|
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase(): {
|
||||||
return false;
|
if (this.city != CityName.Volhaven) return false;
|
||||||
}
|
loc = LocationName.VolhavenMilleniumFitnessGym;
|
||||||
this.currentTaskLocation = LocationName.VolhavenMilleniumFitnessGym;
|
|
||||||
costMult = 7;
|
|
||||||
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;
|
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 { 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,26 +104,75 @@ export function SleeveElem(props: IProps): React.ReactElement {
|
|||||||
);
|
);
|
||||||
break;
|
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:
|
default:
|
||||||
console.error(`Invalid/Unrecognized taskValue in updateSleeveTaskDescription(): ${abc[0]}`);
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Paper sx={{ p: 1, display: "grid", gridTemplateColumns: "1fr 1fr", width: "auto", gap: 1 }}>
|
<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 { 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]) => (
|
||||||
|
@ -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 +
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user