mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-10 09:43:54 +01:00
SLEEVE: Fixed inconsistencies in how sleeve work rewards are handled. (#211)
This commit is contained in:
parent
426ad5f296
commit
c669e473d1
@ -4,7 +4,7 @@ import { Sleeve } from "../Sleeve";
|
||||
import { applySleeveGains, Work, WorkType } from "./Work";
|
||||
import { CONSTANTS } from "../../../Constants";
|
||||
import { GeneralActions } from "../../../Bladeburner/data/GeneralActions";
|
||||
import { WorkStats } from "../../../Work/WorkStats";
|
||||
import { scaleWorkStats } from "../../../Work/WorkStats";
|
||||
|
||||
interface SleeveBladeburnerWorkParams {
|
||||
type: string;
|
||||
@ -31,7 +31,7 @@ export class SleeveBladeburnerWork extends Work {
|
||||
return ret / CONSTANTS._idleSpeed;
|
||||
}
|
||||
|
||||
process(sleeve: Sleeve, cycles: number): number {
|
||||
process(sleeve: Sleeve, cycles: number) {
|
||||
if (!Player.bladeburner) throw new Error("sleeve doing blade work without being a member");
|
||||
this.cyclesWorked += cycles;
|
||||
const actionIdent = Player.bladeburner.getActionIdFromTypeAndName(this.actionType, this.actionName);
|
||||
@ -39,35 +39,27 @@ export class SleeveBladeburnerWork extends Work {
|
||||
if (this.actionType === "Contracts") {
|
||||
const action = Player.bladeburner.getActionObject(actionIdent);
|
||||
if (!action) throw new Error(`Error getting ${this.actionName} action object`);
|
||||
if (action.count <= 0) {
|
||||
sleeve.stopWork();
|
||||
return 0;
|
||||
}
|
||||
if (action.count <= 0) return sleeve.stopWork();
|
||||
}
|
||||
|
||||
while (this.cyclesWorked > this.cyclesNeeded(sleeve)) {
|
||||
if (this.actionType === "Contracts") {
|
||||
const action = Player.bladeburner.getActionObject(actionIdent);
|
||||
if (!action) throw new Error(`Error getting ${this.actionName} action object`);
|
||||
if (action.count <= 0) {
|
||||
sleeve.stopWork();
|
||||
return 0;
|
||||
}
|
||||
if (action.count <= 0) return sleeve.stopWork();
|
||||
}
|
||||
const retValue = Player.bladeburner.completeAction(sleeve, actionIdent, false);
|
||||
let exp: WorkStats | undefined;
|
||||
if (this.actionType === "General") {
|
||||
exp = GeneralActions[this.actionName]?.exp;
|
||||
const exp = GeneralActions[this.actionName]?.exp;
|
||||
if (!exp) throw new Error(`Somehow there was no exp for action ${this.actionType} ${this.actionName}`);
|
||||
applySleeveGains(sleeve, exp, 1);
|
||||
applySleeveGains(sleeve, scaleWorkStats(exp, sleeve.shockBonus(), false));
|
||||
}
|
||||
|
||||
if (this.actionType === "Contracts") {
|
||||
applySleeveGains(sleeve, retValue, 1);
|
||||
applySleeveGains(sleeve, scaleWorkStats(retValue, sleeve.shockBonus(), false));
|
||||
}
|
||||
this.cyclesWorked -= this.cyclesNeeded(sleeve);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
APICopy(): Record<string, unknown> {
|
||||
|
@ -33,11 +33,11 @@ export class SleeveClassWork extends Work {
|
||||
);
|
||||
}
|
||||
|
||||
process(sleeve: Sleeve, cycles: number): number {
|
||||
process(sleeve: Sleeve, cycles: number) {
|
||||
const rate = this.calculateRates(sleeve);
|
||||
applySleeveGains(sleeve, rate, cycles);
|
||||
return 0;
|
||||
}
|
||||
|
||||
APICopy(): Record<string, unknown> {
|
||||
return {
|
||||
type: this.type,
|
||||
|
@ -5,7 +5,7 @@ import { LocationName } from "../../../Locations/data/LocationNames";
|
||||
import { Companies } from "../../../Company/Companies";
|
||||
import { Company } from "../../../Company/Company";
|
||||
import { calculateCompanyWorkStats } from "../../../Work/Formulas";
|
||||
import { WorkStats } from "../../../Work/WorkStats";
|
||||
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
|
||||
import { influenceStockThroughCompanyWork } from "../../../StockMarket/PlayerInfluencing";
|
||||
import { Player } from "@player";
|
||||
import { CompanyPositions } from "../../../Company/CompanyPositions";
|
||||
@ -33,16 +33,19 @@ export class SleeveCompanyWork extends Work {
|
||||
|
||||
getGainRates(sleeve: Sleeve): WorkStats {
|
||||
const company = this.getCompany();
|
||||
return calculateCompanyWorkStats(sleeve, company, CompanyPositions[Player.jobs[company.name]], company.favor);
|
||||
return scaleWorkStats(
|
||||
calculateCompanyWorkStats(sleeve, company, CompanyPositions[Player.jobs[company.name]], company.favor),
|
||||
sleeve.shockBonus(),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
process(sleeve: Sleeve, cycles: number): number {
|
||||
process(sleeve: Sleeve, cycles: number) {
|
||||
const company = this.getCompany();
|
||||
const gains = this.getGainRates(sleeve);
|
||||
applySleeveGains(sleeve, gains, cycles);
|
||||
company.playerReputation += gains.reputation * cycles;
|
||||
influenceStockThroughCompanyWork(company, gains.reputation, cycles);
|
||||
return 0;
|
||||
}
|
||||
|
||||
APICopy(): Record<string, unknown> {
|
||||
|
@ -26,29 +26,24 @@ export class SleeveCrimeWork extends Work {
|
||||
}
|
||||
|
||||
getExp(sleeve: Sleeve): WorkStats {
|
||||
return calculateCrimeWorkStats(sleeve, this.getCrime());
|
||||
return scaleWorkStats(calculateCrimeWorkStats(sleeve, this.getCrime()), sleeve.shockBonus(), false);
|
||||
}
|
||||
|
||||
cyclesNeeded(): number {
|
||||
return this.getCrime().time / CONSTANTS._idleSpeed;
|
||||
}
|
||||
|
||||
process(sleeve: Sleeve, cycles: number): number {
|
||||
process(sleeve: Sleeve, cycles: number) {
|
||||
this.cyclesWorked += cycles;
|
||||
if (this.cyclesWorked < this.cyclesNeeded()) return;
|
||||
|
||||
const crime = this.getCrime();
|
||||
let gains = this.getExp(sleeve);
|
||||
if (this.cyclesWorked >= this.cyclesNeeded()) {
|
||||
if (Math.random() < crime.successRate(sleeve)) {
|
||||
Player.karma -= crime.karma * sleeve.syncBonus();
|
||||
} else {
|
||||
gains.money = 0;
|
||||
gains = scaleWorkStats(gains, 0.25);
|
||||
}
|
||||
applySleeveGains(sleeve, gains, cycles);
|
||||
this.cyclesWorked -= this.cyclesNeeded();
|
||||
}
|
||||
return 0;
|
||||
const gains = this.getExp(sleeve);
|
||||
const success = Math.random() < crime.successRate(sleeve);
|
||||
if (success) Player.karma -= crime.karma * sleeve.syncBonus();
|
||||
else gains.money = 0;
|
||||
applySleeveGains(sleeve, gains, success ? 1 : 0.25);
|
||||
this.cyclesWorked -= this.cyclesNeeded();
|
||||
}
|
||||
|
||||
APICopy(): Record<string, unknown> {
|
||||
|
@ -28,7 +28,7 @@ export class SleeveFactionWork extends Work {
|
||||
}
|
||||
|
||||
getExpRates(sleeve: Sleeve): WorkStats {
|
||||
return scaleWorkStats(calculateFactionExp(sleeve, this.factionWorkType), sleeve.shockBonus());
|
||||
return scaleWorkStats(calculateFactionExp(sleeve, this.factionWorkType), sleeve.shockBonus(), false);
|
||||
}
|
||||
|
||||
getReputationRate(sleeve: Sleeve): number {
|
||||
@ -41,17 +41,13 @@ export class SleeveFactionWork extends Work {
|
||||
return f;
|
||||
}
|
||||
|
||||
process(sleeve: Sleeve, cycles: number): number {
|
||||
if (this.factionName === Player.gang?.facName) {
|
||||
sleeve.stopWork();
|
||||
return 0;
|
||||
}
|
||||
process(sleeve: Sleeve, cycles: number) {
|
||||
if (this.factionName === Player.gang?.facName) return sleeve.stopWork();
|
||||
|
||||
const exp = this.getExpRates(sleeve);
|
||||
applySleeveGains(sleeve, exp, cycles);
|
||||
const rep = this.getReputationRate(sleeve);
|
||||
this.getFaction().playerReputation += rep;
|
||||
return 0;
|
||||
this.getFaction().playerReputation += rep * cycles;
|
||||
}
|
||||
|
||||
APICopy(): Record<string, unknown> {
|
||||
|
@ -20,14 +20,13 @@ export class SleeveInfiltrateWork extends Work {
|
||||
return infiltrateCycles;
|
||||
}
|
||||
|
||||
process(_sleeve: Sleeve, cycles: number): number {
|
||||
process(_sleeve: Sleeve, cycles: 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();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
APICopy(): Record<string, unknown> {
|
||||
|
@ -10,10 +10,9 @@ export class SleeveRecoveryWork extends Work {
|
||||
super(WorkType.RECOVERY);
|
||||
}
|
||||
|
||||
process(sleeve: Sleeve, cycles: number): number {
|
||||
process(sleeve: Sleeve, cycles: number) {
|
||||
sleeve.shock = Math.min(100, sleeve.shock + 0.0002 * cycles);
|
||||
if (sleeve.shock >= 100) sleeve.stopWork();
|
||||
return 0;
|
||||
}
|
||||
|
||||
APICopy(): Record<string, unknown> {
|
||||
|
@ -11,8 +11,8 @@ export class SleeveSupportWork extends Work {
|
||||
Player.bladeburner?.sleeveSupport(true);
|
||||
}
|
||||
|
||||
process(): number {
|
||||
return 0;
|
||||
process() {
|
||||
return;
|
||||
}
|
||||
|
||||
finish(): void {
|
||||
|
@ -12,13 +12,12 @@ export class SleeveSynchroWork extends Work {
|
||||
super(WorkType.SYNCHRO);
|
||||
}
|
||||
|
||||
process(sleeve: Sleeve, cycles: number): number {
|
||||
process(sleeve: Sleeve, cycles: number) {
|
||||
sleeve.sync = Math.min(
|
||||
100,
|
||||
sleeve.sync + calculateIntelligenceBonus(Player.skills.intelligence, 0.5) * 0.0002 * cycles,
|
||||
);
|
||||
if (sleeve.sync >= 100) sleeve.stopWork();
|
||||
return 0;
|
||||
}
|
||||
|
||||
APICopy(): Record<string, unknown> {
|
||||
|
@ -1,14 +1,16 @@
|
||||
import { Player } from "@player";
|
||||
import { IReviverValue } from "../../../utils/JSONReviver";
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { applyWorkStats, applyWorkStatsExp, scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
|
||||
import { applyWorkStatsExp, WorkStats } from "../../../Work/WorkStats";
|
||||
|
||||
export const applySleeveGains = (sleeve: Sleeve, rawStats: WorkStats, cycles = 1): void => {
|
||||
const shockedStats = scaleWorkStats(rawStats, sleeve.shockBonus(), rawStats.money > 0);
|
||||
applyWorkStatsExp(sleeve, shockedStats, cycles);
|
||||
const syncStats = scaleWorkStats(shockedStats, sleeve.syncBonus(), rawStats.money > 0);
|
||||
applyWorkStats(Player, syncStats, cycles, "sleeves");
|
||||
Player.sleeves.filter((s) => s !== sleeve).forEach((s) => applyWorkStatsExp(s, syncStats, cycles));
|
||||
export const applySleeveGains = (sleeve: Sleeve, shockedStats: WorkStats, mult = 1): void => {
|
||||
applyWorkStatsExp(sleeve, shockedStats, mult);
|
||||
Player.gainMoney(shockedStats.money * mult, "sleeves");
|
||||
const sync = sleeve.syncBonus();
|
||||
// The receiving sleeves and the player do not apply their xp multipliers from augs (avoid double dipping xp mults)
|
||||
applyWorkStatsExp(Player, shockedStats, mult * sync);
|
||||
// Sleeves apply their own shock bonus to the XP they receive, even though it is also shocked by the working sleeve
|
||||
Player.sleeves.forEach((s) => s !== sleeve && applyWorkStatsExp(s, shockedStats, mult * sync * s.shockBonus()));
|
||||
};
|
||||
|
||||
export abstract class Work {
|
||||
@ -18,7 +20,7 @@ export abstract class Work {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
abstract process(sleeve: Sleeve, cycles: number): number;
|
||||
abstract process(sleeve: Sleeve, cycles: number): void;
|
||||
abstract APICopy(): Record<string, unknown>;
|
||||
abstract toJSON(): IReviverValue;
|
||||
finish(): void {
|
||||
|
@ -133,7 +133,7 @@ export function EarningsElement(props: IProps): React.ReactElement {
|
||||
[`Dexterity Exp:`, `${numeralWrapper.formatExp(CYCLES_PER_SEC * rates.dexExp)} / sec`],
|
||||
[`Agility Exp:`, `${numeralWrapper.formatExp(CYCLES_PER_SEC * rates.agiExp)} / sec`],
|
||||
[`Charisma Exp:`, `${numeralWrapper.formatExp(CYCLES_PER_SEC * rates.chaExp)} / sec`],
|
||||
[`Reputation:`, <ReputationRate reputation={repGain} />],
|
||||
[`Reputation:`, <ReputationRate reputation={CYCLES_PER_SEC * repGain} />],
|
||||
];
|
||||
}
|
||||
|
||||
|
6
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
6
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -3814,10 +3814,14 @@ export interface WorkStats {
|
||||
* @public
|
||||
*/
|
||||
interface WorkFormulas {
|
||||
crimeSuccessChance(person: Person, crimeType: CrimeType | CrimeNames): number;
|
||||
crimeSuccessChance(person: Person, crimeType: CrimeType | `${CrimeType}`): number;
|
||||
/** @returns The WorkStats gained when completing one instance of the specified crime. */
|
||||
crimeGains(person: Person, crimeType: CrimeType | `${CrimeType}`): WorkStats;
|
||||
/** @returns The WorkStats applied every game cycle (200ms) by taking the specified class. */
|
||||
classGains(person: Person, classType: ClassType | `${ClassType}`, locationName: string): WorkStats;
|
||||
/** @returns The WorkStats applied every game cycle (200ms) by performing the specified faction work. */
|
||||
factionGains(person: Person, workType: FactionWorkType | `${FactionWorkType}`, favor: number): WorkStats;
|
||||
/** @returns The WorkStats applied every game cycle (200ms) by performing the specified company work. */
|
||||
companyGains(
|
||||
person: Person,
|
||||
companyName: string,
|
||||
|
@ -63,6 +63,7 @@ export function calculateCrimeWorkStats(person: IPerson, crime: Crime): WorkStat
|
||||
return gains;
|
||||
}
|
||||
|
||||
/** @returns faction rep rate per cycle */
|
||||
export const calculateFactionRep = (person: IPerson, type: FactionWorkType, favor: number): number => {
|
||||
const repFormulas = {
|
||||
[FactionWorkType.HACKING]: getHackingWorkRepGain,
|
||||
@ -72,6 +73,7 @@ export const calculateFactionRep = (person: IPerson, type: FactionWorkType, favo
|
||||
return repFormulas[type](person, favor);
|
||||
};
|
||||
|
||||
/** @returns per-cycle WorkStats */
|
||||
export function calculateFactionExp(person: IPerson, type: FactionWorkType): WorkStats {
|
||||
return scaleWorkStats(
|
||||
multWorkStats(FactionWorkStats[type], person.mults),
|
||||
@ -87,6 +89,7 @@ export function calculateCost(classs: Class, location: Location): number {
|
||||
return classs.earnings.money * location.costMult * discount;
|
||||
}
|
||||
|
||||
/** @returns per-cycle WorkStats */
|
||||
export function calculateClassEarnings(person: IPerson, type: ClassType, locationName: LocationName): WorkStats {
|
||||
const hashManager = Player.hashManager;
|
||||
const classs = Classes[type];
|
||||
@ -106,6 +109,7 @@ export function calculateClassEarnings(person: IPerson, type: ClassType, locatio
|
||||
return earnings;
|
||||
}
|
||||
|
||||
/** @returns per-cycle WorkStats */
|
||||
export const calculateCompanyWorkStats = (
|
||||
worker: IPerson,
|
||||
company: Company,
|
||||
|
@ -77,18 +77,10 @@ export const applyWorkStats = (target: Person, workStats: WorkStats, cycles: num
|
||||
return gains;
|
||||
};
|
||||
|
||||
export const applyWorkStatsExp = (target: Person, workStats: WorkStats, cycles: number): WorkStats => {
|
||||
const gains = {
|
||||
money: 0,
|
||||
reputation: 0,
|
||||
hackExp: workStats.hackExp * cycles,
|
||||
strExp: workStats.strExp * cycles,
|
||||
defExp: workStats.defExp * cycles,
|
||||
dexExp: workStats.dexExp * cycles,
|
||||
agiExp: workStats.agiExp * cycles,
|
||||
chaExp: workStats.chaExp * cycles,
|
||||
intExp: workStats.intExp * cycles,
|
||||
};
|
||||
export const applyWorkStatsExp = (target: Person, workStats: WorkStats, mult = 1): WorkStats => {
|
||||
const gains = scaleWorkStats(workStats, mult, false);
|
||||
gains.money = 0;
|
||||
gains.reputation = 0;
|
||||
target.gainHackingExp(gains.hackExp);
|
||||
target.gainStrengthExp(gains.strExp);
|
||||
target.gainDefenseExp(gains.defExp);
|
||||
|
Loading…
Reference in New Issue
Block a user