convert faction work to new work system

This commit is contained in:
Olivier Gagnon 2022-07-12 01:54:19 -04:00
parent e86a42716c
commit f7805c4a51
28 changed files with 299 additions and 404 deletions

@ -19,6 +19,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { InvitationEvent } from "./ui/InvitationModal"; import { InvitationEvent } from "./ui/InvitationModal";
import { FactionNames } from "./data/FactionNames"; import { FactionNames } from "./data/FactionNames";
import { SFC32RNG } from "../Casino/RNG"; import { SFC32RNG } from "../Casino/RNG";
import { isFactionWork } from "../Work/FactionWork";
export function inviteToFaction(faction: Faction): void { export function inviteToFaction(faction: Faction): void {
Player.receiveInvite(faction.name); Player.receiveInvite(faction.name);
@ -113,7 +114,7 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
export function processPassiveFactionRepGain(numCycles: number): void { export function processPassiveFactionRepGain(numCycles: number): void {
for (const name of Object.keys(Factions)) { for (const name of Object.keys(Factions)) {
if (name === Player.currentWorkFactionName) continue; if (isFactionWork(Player.currentWork) && name === Player.currentWork.factionName) continue;
if (!Factions.hasOwnProperty(name)) continue; if (!Factions.hasOwnProperty(name)) continue;
const faction = Factions[name]; const faction = Factions[name];
if (!faction.isMember) continue; if (!faction.isMember) continue;

@ -1,6 +0,0 @@
export enum FactionWorkType {
Field,
Hacking,
None,
Security,
}

@ -21,6 +21,8 @@ import { Typography, Button } from "@mui/material";
import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPurchasesRoot"; import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPurchasesRoot";
import { FactionNames } from "../data/FactionNames"; import { FactionNames } from "../data/FactionNames";
import { GangButton } from "./GangButton"; import { GangButton } from "./GangButton";
import { FactionWork } from "../../Work/FactionWork";
import { FactionWorkType } from "../../Work/data/FactionWorkType";
type IProps = { type IProps = {
faction: Faction; faction: Faction;
@ -67,17 +69,35 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
} }
function startFieldWork(faction: Faction): void { function startFieldWork(faction: Faction): void {
player.startFactionFieldWork(faction); player.startNEWWork(
new FactionWork({
singularity: false,
faction: faction.name,
factionWorkType: FactionWorkType.HACKING,
}),
);
startWork(); startWork();
} }
function startHackingContracts(faction: Faction): void { function startHackingContracts(faction: Faction): void {
player.startFactionHackWork(faction); player.startNEWWork(
new FactionWork({
singularity: false,
faction: faction.name,
factionWorkType: FactionWorkType.HACKING,
}),
);
startWork(); startWork();
} }
function startSecurityWork(faction: Faction): void { function startSecurityWork(faction: Faction): void {
player.startFactionSecurityWork(faction); player.startNEWWork(
new FactionWork({
singularity: false,
faction: faction.name,
factionWorkType: FactionWorkType.HACKING,
}),
);
startWork(); startWork();
} }

@ -2399,8 +2399,6 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
crime_success_mult: Player.crime_success_mult, crime_success_mult: Player.crime_success_mult,
isWorking: Player.isWorking, isWorking: Player.isWorking,
workType: Player.workType, workType: Player.workType,
currentWorkFactionName: Player.currentWorkFactionName,
currentWorkFactionDescription: Player.currentWorkFactionDescription,
workHackExpGainRate: Player.workHackExpGainRate, workHackExpGainRate: Player.workHackExpGainRate,
workStrExpGainRate: Player.workStrExpGainRate, workStrExpGainRate: Player.workStrExpGainRate,
workDefExpGainRate: Player.workDefExpGainRate, workDefExpGainRate: Player.workDefExpGainRate,

@ -52,6 +52,8 @@ import { FactionNames } from "../Faction/data/FactionNames";
import { WorkType } from "../utils/WorkType"; import { WorkType } from "../utils/WorkType";
import { ClassWork, ClassType } from "../Work/ClassWork"; import { ClassWork, ClassType } from "../Work/ClassWork";
import { CreateProgramWork, isCreateProgramWork } from "../Work/CreateProgramWork"; import { CreateProgramWork, isCreateProgramWork } from "../Work/CreateProgramWork";
import { FactionWork } from "../Work/FactionWork";
import { FactionWorkType } from "../Work/data/FactionWorkType";
export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript): InternalAPI<ISingularity> { export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript): InternalAPI<ISingularity> {
const getAugmentation = function (_ctx: NetscriptContext, name: string): Augmentation { const getAugmentation = function (_ctx: NetscriptContext, name: string): Augmentation {
@ -1033,7 +1035,13 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
_ctx.log(() => `Faction '${faction.name}' do not need help with hacking contracts.`); _ctx.log(() => `Faction '${faction.name}' do not need help with hacking contracts.`);
return false; return false;
} }
player.startFactionHackWork(faction); player.startNEWWork(
new FactionWork({
singularity: true,
factionWorkType: FactionWorkType.HACKING,
faction: faction.name,
}),
);
if (focus) { if (focus) {
player.startFocusing(); player.startFocusing();
Router.toWork(); Router.toWork();
@ -1050,7 +1058,13 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
_ctx.log(() => `Faction '${faction.name}' do not need help with field missions.`); _ctx.log(() => `Faction '${faction.name}' do not need help with field missions.`);
return false; return false;
} }
player.startFactionFieldWork(faction); player.startNEWWork(
new FactionWork({
singularity: true,
factionWorkType: FactionWorkType.FIELD,
faction: faction.name,
}),
);
if (focus) { if (focus) {
player.startFocusing(); player.startFocusing();
Router.toWork(); Router.toWork();
@ -1067,7 +1081,13 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
_ctx.log(() => `Faction '${faction.name}' do not need help with security work.`); _ctx.log(() => `Faction '${faction.name}' do not need help with security work.`);
return false; return false;
} }
player.startFactionSecurityWork(faction); player.startNEWWork(
new FactionWork({
singularity: true,
factionWorkType: FactionWorkType.SECURITY,
faction: faction.name,
}),
);
if (focus) { if (focus) {
player.startFocusing(); player.startFocusing();
Router.toWork(); Router.toWork();

@ -1,5 +1,4 @@
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { FactionWorkType } from "../Faction/FactionWorkTypeEnum";
import { SleeveTaskType } from "../PersonObjects/Sleeve/SleeveTaskTypesEnum"; import { SleeveTaskType } from "../PersonObjects/Sleeve/SleeveTaskTypesEnum";
import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers"; import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations"; import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
@ -15,6 +14,7 @@ import {
} from "../ScriptEditor/NetscriptDefinitions"; } from "../ScriptEditor/NetscriptDefinitions";
import { checkEnum } from "../utils/helpers/checkEnum"; import { checkEnum } from "../utils/helpers/checkEnum";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { FactionWorkType } from "../Work/data/FactionWorkType";
export function NetscriptSleeve(player: IPlayer): InternalAPI<ISleeve> { export function NetscriptSleeve(player: IPlayer): InternalAPI<ISleeve> {
const checkSleeveAPIAccess = function (ctx: NetscriptContext): void { const checkSleeveAPIAccess = function (ctx: NetscriptContext): void {

@ -125,14 +125,11 @@ export interface IPlayer extends IPerson {
bladeburner_success_chance_mult: number; bladeburner_success_chance_mult: number;
currentWork: Work | null; currentWork: Work | null;
factionWorkType: string;
timeNeededToCompleteWork: number; timeNeededToCompleteWork: number;
focus: boolean; focus: boolean;
currentWorkFactionName: string;
workType: WorkType; workType: WorkType;
workCostMult: number; workCostMult: number;
workExpMult: number; workExpMult: number;
currentWorkFactionDescription: string;
timeWorked: number; timeWorked: number;
workMoneyGained: number; workMoneyGained: number;
workMoneyGainRate: number; workMoneyGainRate: number;
@ -160,7 +157,6 @@ export interface IPlayer extends IPerson {
finishNEWWork(cancelled: boolean): void; finishNEWWork(cancelled: boolean): void;
work(numCycles: number): boolean; work(numCycles: number): boolean;
workPartTime(numCycles: number): boolean; workPartTime(numCycles: number): boolean;
workForFaction(numCycles: number): boolean;
applyForAgentJob(sing?: boolean): boolean; applyForAgentJob(sing?: boolean): boolean;
applyForBusinessConsultantJob(sing?: boolean): boolean; applyForBusinessConsultantJob(sing?: boolean): boolean;
applyForBusinessJob(sing?: boolean): boolean; applyForBusinessJob(sing?: boolean): boolean;
@ -205,11 +201,7 @@ export interface IPlayer extends IPerson {
setMoney(amt: number): void; setMoney(amt: number): void;
singularityStopWork(): string; singularityStopWork(): string;
startBladeburner(p: any): void; startBladeburner(p: any): void;
startFactionWork(faction: Faction): void;
startCorporation(corpName: string, additionalShares?: number): void; startCorporation(corpName: string, additionalShares?: number): void;
startFactionFieldWork(faction: Faction): void;
startFactionHackWork(faction: Faction): void;
startFactionSecurityWork(faction: Faction): void;
startFocusing(): void; startFocusing(): void;
startGang(facName: string, isHacking: boolean): void; startGang(facName: string, isHacking: boolean): void;
startWork(companyName: string): void; startWork(companyName: string): void;
@ -226,7 +218,6 @@ export interface IPlayer extends IPerson {
updateSkillLevels(): void; updateSkillLevels(): void;
gainCodingContractReward(reward: ICodingContractReward, difficulty?: number): string; gainCodingContractReward(reward: ICodingContractReward, difficulty?: number): string;
stopFocusing(): void; stopFocusing(): void;
finishFactionWork(cancelled: boolean, sing?: boolean): string;
finishWork(cancelled: boolean, sing?: boolean): string; finishWork(cancelled: boolean, sing?: boolean): string;
cancelationPenalty(): number; cancelationPenalty(): number;
finishWorkPartTime(sing?: boolean): string; finishWorkPartTime(sing?: boolean): string;

@ -39,7 +39,7 @@ import { cyrb53 } from "../../utils/StringHelperFunctions";
import { getRandomInt } from "../../utils/helpers/getRandomInt"; import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { ITaskTracker } from "../ITaskTracker"; import { ITaskTracker } from "../ITaskTracker";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { WorkType, PlayerFactionWorkType } from "../../utils/WorkType"; import { WorkType } from "../../utils/WorkType";
import { Work } from "src/Work/Work"; import { Work } from "src/Work/Work";
export class PlayerObject implements IPlayer { export class PlayerObject implements IPlayer {
@ -138,14 +138,11 @@ export class PlayerObject implements IPlayer {
bladeburner_success_chance_mult: number; bladeburner_success_chance_mult: number;
currentWork: Work | null; currentWork: Work | null;
factionWorkType: PlayerFactionWorkType;
timeNeededToCompleteWork: number; timeNeededToCompleteWork: number;
focus: boolean; focus: boolean;
currentWorkFactionName: string;
workType: WorkType; workType: WorkType;
workCostMult: number; workCostMult: number;
workExpMult: number; workExpMult: number;
currentWorkFactionDescription: string;
timeWorked: number; timeWorked: number;
workMoneyGained: number; workMoneyGained: number;
workMoneyGainRate: number; workMoneyGainRate: number;
@ -173,7 +170,6 @@ export class PlayerObject implements IPlayer {
finishNEWWork: (cancelled: boolean) => void; finishNEWWork: (cancelled: boolean) => void;
work: (numCycles: number) => boolean; work: (numCycles: number) => boolean;
workPartTime: (numCycles: number) => boolean; workPartTime: (numCycles: number) => boolean;
workForFaction: (numCycles: number) => boolean;
applyForAgentJob: (sing?: boolean) => boolean; applyForAgentJob: (sing?: boolean) => boolean;
applyForBusinessConsultantJob: (sing?: boolean) => boolean; applyForBusinessConsultantJob: (sing?: boolean) => boolean;
applyForBusinessJob: (sing?: boolean) => boolean; applyForBusinessJob: (sing?: boolean) => boolean;
@ -227,11 +223,7 @@ export class PlayerObject implements IPlayer {
setMoney: (amt: number) => void; setMoney: (amt: number) => void;
singularityStopWork: () => string; singularityStopWork: () => string;
startBladeburner: (p: any) => void; startBladeburner: (p: any) => void;
startFactionWork: (faction: Faction) => void;
startCorporation: (corpName: string, additionalShares?: number) => void; startCorporation: (corpName: string, additionalShares?: number) => void;
startFactionFieldWork: (faction: Faction) => void;
startFactionHackWork: (faction: Faction) => void;
startFactionSecurityWork: (faction: Faction) => void;
startFocusing: () => void; startFocusing: () => void;
startGang: (facName: string, isHacking: boolean) => void; startGang: (facName: string, isHacking: boolean) => void;
startWork: (companyName: string) => void; startWork: (companyName: string) => void;
@ -252,7 +244,6 @@ export class PlayerObject implements IPlayer {
updateSkillLevels: () => void; updateSkillLevels: () => void;
gainCodingContractReward: (reward: ICodingContractReward, difficulty?: number) => string; gainCodingContractReward: (reward: ICodingContractReward, difficulty?: number) => string;
stopFocusing: () => void; stopFocusing: () => void;
finishFactionWork: (cancelled: boolean, sing?: boolean) => string;
finishWork: (cancelled: boolean, sing?: boolean) => string; finishWork: (cancelled: boolean, sing?: boolean) => string;
cancelationPenalty: () => number; cancelationPenalty: () => number;
finishWorkPartTime: (sing?: boolean) => string; finishWorkPartTime: (sing?: boolean) => string;
@ -377,9 +368,6 @@ export class PlayerObject implements IPlayer {
this.workCostMult = 1; this.workCostMult = 1;
this.workExpMult = 1; this.workExpMult = 1;
this.currentWorkFactionName = "";
this.currentWorkFactionDescription = "";
this.workHackExpGainRate = 0; this.workHackExpGainRate = 0;
this.workStrExpGainRate = 0; this.workStrExpGainRate = 0;
this.workDefExpGainRate = 0; this.workDefExpGainRate = 0;
@ -505,12 +493,6 @@ export class PlayerObject implements IPlayer {
this.finishWorkPartTime = generalMethods.finishWorkPartTime; this.finishWorkPartTime = generalMethods.finishWorkPartTime;
this.startFocusing = generalMethods.startFocusing; this.startFocusing = generalMethods.startFocusing;
this.stopFocusing = generalMethods.stopFocusing; this.stopFocusing = generalMethods.stopFocusing;
this.startFactionWork = generalMethods.startFactionWork;
this.startFactionHackWork = generalMethods.startFactionHackWork;
this.startFactionFieldWork = generalMethods.startFactionFieldWork;
this.startFactionSecurityWork = generalMethods.startFactionSecurityWork;
this.workForFaction = generalMethods.workForFaction;
this.finishFactionWork = generalMethods.finishFactionWork;
this.getWorkMoneyGain = generalMethods.getWorkMoneyGain; this.getWorkMoneyGain = generalMethods.getWorkMoneyGain;
this.getWorkHackExpGain = generalMethods.getWorkHackExpGain; this.getWorkHackExpGain = generalMethods.getWorkHackExpGain;
this.getWorkStrExpGain = generalMethods.getWorkStrExpGain; this.getWorkStrExpGain = generalMethods.getWorkStrExpGain;
@ -576,7 +558,6 @@ export class PlayerObject implements IPlayer {
this.getUpgradeHomeRamCost = serverMethods.getUpgradeHomeRamCost; this.getUpgradeHomeRamCost = serverMethods.getUpgradeHomeRamCost;
this.getUpgradeHomeCoresCost = serverMethods.getUpgradeHomeCoresCost; this.getUpgradeHomeCoresCost = serverMethods.getUpgradeHomeCoresCost;
this.createHacknetServer = serverMethods.createHacknetServer; this.createHacknetServer = serverMethods.createHacknetServer;
this.factionWorkType = PlayerFactionWorkType.None;
this.getMult = generalMethods.getMult; this.getMult = generalMethods.getMult;
this.setMult = generalMethods.setMult; this.setMult = generalMethods.setMult;

@ -29,11 +29,6 @@ import {
ISkillProgress, ISkillProgress,
} from "../formulas/skill"; } from "../formulas/skill";
import { calculateIntelligenceBonus } from "../formulas/intelligence"; import { calculateIntelligenceBonus } from "../formulas/intelligence";
import {
getHackingWorkRepGain,
getFactionSecurityWorkRepGain,
getFactionFieldWorkRepGain,
} from "../formulas/reputation";
import { GetServer, AddToAllServers, createUniqueRandomIp } from "../../Server/AllServers"; import { GetServer, AddToAllServers, createUniqueRandomIp } from "../../Server/AllServers";
import { Server } from "../../Server/Server"; import { Server } from "../../Server/Server";
import { safetlyCreateUniqueServer } from "../../Server/ServerHelpers"; import { safetlyCreateUniqueServer } from "../../Server/ServerHelpers";
@ -64,7 +59,7 @@ import { ITaskTracker } from "../ITaskTracker";
import { IPerson } from "../IPerson"; import { IPerson } from "../IPerson";
import { Player } from "../../Player"; import { Player } from "../../Player";
import { WorkType, PlayerFactionWorkType } from "../../utils/WorkType"; import { WorkType } from "../../utils/WorkType";
export function init(this: IPlayer): void { export function init(this: IPlayer): void {
/* Initialize Player's home computer */ /* Initialize Player's home computer */
@ -137,8 +132,6 @@ export function prestigeAugmentation(this: PlayerObject): void {
} }
this.isWorking = false; this.isWorking = false;
this.currentWorkFactionName = "";
this.currentWorkFactionDescription = "";
this.workHackExpGainRate = 0; this.workHackExpGainRate = 0;
this.workStrExpGainRate = 0; this.workStrExpGainRate = 0;
@ -509,8 +502,7 @@ export function queryStatFromString(this: IPlayer, str: string): number {
/******* Working functions *******/ /******* Working functions *******/
export function resetWorkStatus(this: IPlayer, generalType?: WorkType, group?: string, workType?: string): void { export function resetWorkStatus(this: IPlayer, generalType?: WorkType, group?: string, workType?: string): void {
if (this.workType !== WorkType.Faction && generalType === this.workType && group === this.companyName) return; if (this.workType !== WorkType.Faction && generalType === this.workType && group === this.companyName) return;
if (generalType === this.workType && group === this.currentWorkFactionName && workType === this.factionWorkType) if (generalType === this.workType) return;
return;
if (this.isWorking) this.singularityStopWork(); if (this.isWorking) this.singularityStopWork();
this.workHackExpGainRate = 0; this.workHackExpGainRate = 0;
this.workStrExpGainRate = 0; this.workStrExpGainRate = 0;
@ -533,8 +525,6 @@ export function resetWorkStatus(this: IPlayer, generalType?: WorkType, group?: s
this.timeWorked = 0; this.timeWorked = 0;
this.currentWorkFactionName = "";
this.currentWorkFactionDescription = "";
this.workType = WorkType.None; this.workType = WorkType.None;
} }
@ -590,11 +580,7 @@ export function startWork(this: IPlayer, companyName: string): void {
export function process(this: IPlayer, router: IRouter, numCycles = 1): void { export function process(this: IPlayer, router: IRouter, numCycles = 1): void {
// Working // Working
if (this.isWorking) { if (this.isWorking) {
if (this.workType === WorkType.Faction) { if (this.workType === WorkType.CompanyPartTime) {
if (this.workForFaction(numCycles)) {
router.toFaction(Factions[this.currentWorkFactionName]);
}
} else if (this.workType === WorkType.CompanyPartTime) {
if (this.workPartTime(numCycles)) { if (this.workPartTime(numCycles)) {
router.toCity(); router.toCity();
} }
@ -855,165 +841,6 @@ export function stopFocusing(this: IPlayer): void {
this.focus = false; this.focus = false;
} }
/* Working for Faction */
export function startFactionWork(this: IPlayer, faction: Faction): void {
//Update reputation gain rate to account for faction favor
let favorMult = 1 + faction.favor / 100;
if (isNaN(favorMult)) {
favorMult = 1;
}
this.workRepGainRate *= favorMult;
this.workRepGainRate *= BitNodeMultipliers.FactionWorkRepGain;
this.isWorking = true;
this.workType = WorkType.Faction;
this.currentWorkFactionName = faction.name;
this.timeNeededToCompleteWork = CONSTANTS.MillisecondsPer20Hours;
}
export function startFactionHackWork(this: IPlayer, faction: Faction): void {
this.resetWorkStatus(WorkType.Faction, faction.name, PlayerFactionWorkType.Hacking);
this.workHackExpGainRate = 0.15 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workRepGainRate = getHackingWorkRepGain(this, faction);
this.factionWorkType = PlayerFactionWorkType.Hacking;
this.currentWorkFactionDescription = "carrying out hacking contracts";
this.startFactionWork(faction);
}
export function startFactionFieldWork(this: IPlayer, faction: Faction): void {
this.resetWorkStatus(WorkType.Faction, faction.name, PlayerFactionWorkType.Field);
this.workHackExpGainRate = 0.1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workStrExpGainRate = 0.1 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workDefExpGainRate = 0.1 * this.defense_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workDexExpGainRate = 0.1 * this.dexterity_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workAgiExpGainRate = 0.1 * this.agility_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workChaExpGainRate = 0.1 * this.charisma_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workRepGainRate = getFactionFieldWorkRepGain(this, faction);
this.factionWorkType = PlayerFactionWorkType.Field;
this.currentWorkFactionDescription = "carrying out field missions";
this.startFactionWork(faction);
}
export function startFactionSecurityWork(this: IPlayer, faction: Faction): void {
this.resetWorkStatus(WorkType.Faction, faction.name, PlayerFactionWorkType.Security);
this.workHackExpGainRate = 0.05 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workStrExpGainRate = 0.15 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workDefExpGainRate = 0.15 * this.defense_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workDexExpGainRate = 0.15 * this.dexterity_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workAgiExpGainRate = 0.15 * this.agility_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workChaExpGainRate = 0.0 * this.charisma_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.workRepGainRate = getFactionSecurityWorkRepGain(this, faction);
this.factionWorkType = PlayerFactionWorkType.Security;
this.currentWorkFactionDescription = "performing security detail";
this.startFactionWork(faction);
}
export function workForFaction(this: IPlayer, numCycles: number): boolean {
const faction = Factions[this.currentWorkFactionName];
if (!faction) {
return false;
}
//Constantly update the rep gain rate
switch (this.factionWorkType) {
case PlayerFactionWorkType.Hacking:
this.workRepGainRate = getHackingWorkRepGain(this, faction);
break;
case PlayerFactionWorkType.Field:
this.workRepGainRate = getFactionFieldWorkRepGain(this, faction);
break;
case PlayerFactionWorkType.Security:
this.workRepGainRate = getFactionSecurityWorkRepGain(this, faction);
break;
default:
break;
}
this.workRepGainRate *= BitNodeMultipliers.FactionWorkRepGain;
//Cap the number of cycles being processed to whatever would put you at limit (20 hours)
let overMax = false;
if (this.timeWorked + CONSTANTS._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer20Hours) {
overMax = true;
numCycles = Math.round((CONSTANTS.MillisecondsPer20Hours - this.timeWorked) / CONSTANTS._idleSpeed);
}
this.timeWorked += CONSTANTS._idleSpeed * numCycles;
this.processWorkEarnings(numCycles);
//If timeWorked == 20 hours, then finish. You can only work for the faction for 20 hours
if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer20Hours) {
this.finishFactionWork(false);
return true;
}
return false;
}
export function finishFactionWork(this: IPlayer, cancelled: boolean, sing = false): string {
const faction = Factions[this.currentWorkFactionName];
faction.playerReputation += this.workRepGained;
this.updateSkillLevels();
let res = "";
if (!sing) {
dialogBoxCreate(
<>
You worked for your faction {faction.name} for a total of {convertTimeMsToTimeElapsedString(this.timeWorked)}{" "}
<br />
<br />
You earned a total of: <br />
<Money money={this.workMoneyGained} />
<br />
<Reputation reputation={this.workRepGained} /> reputation for the faction <br />
{numeralWrapper.formatExp(this.workHackExpGained)} hacking exp <br />
{numeralWrapper.formatExp(this.workStrExpGained)} strength exp <br />
{numeralWrapper.formatExp(this.workDefExpGained)} defense exp <br />
{numeralWrapper.formatExp(this.workDexExpGained)} dexterity exp <br />
{numeralWrapper.formatExp(this.workAgiExpGained)} agility exp <br />
{numeralWrapper.formatExp(this.workChaExpGained)} charisma exp
<br />
</>,
);
} else {
res =
"You worked for your faction " +
faction.name +
" for a total of " +
convertTimeMsToTimeElapsedString(this.timeWorked) +
". " +
"You earned " +
numeralWrapper.formatReputation(this.workRepGained) +
" rep, " +
numeralWrapper.formatExp(this.workHackExpGained) +
" hacking exp, " +
numeralWrapper.formatExp(this.workStrExpGained) +
" str exp, " +
numeralWrapper.formatExp(this.workDefExpGained) +
" def exp, " +
numeralWrapper.formatExp(this.workDexExpGained) +
" dex exp, " +
numeralWrapper.formatExp(this.workAgiExpGained) +
" agi exp, and " +
numeralWrapper.formatExp(this.workChaExpGained) +
" cha exp.";
}
this.isWorking = false;
this.resetWorkStatus();
return res;
}
//Money gained per game cycle //Money gained per game cycle
export function getWorkMoneyGain(this: IPlayer): number { export function getWorkMoneyGain(this: IPlayer): number {
// If player has SF-11, calculate salary multiplier from favor // If player has SF-11, calculate salary multiplier from favor
@ -1220,26 +1047,6 @@ export function getWorkRepGain(this: IPlayer): number {
return jobPerformance * this.company_rep_mult * favorMult; return jobPerformance * this.company_rep_mult * favorMult;
} }
// export function getFactionSecurityWorkRepGain(this: IPlayer) {
// var t = 0.9 * (this.hacking / CONSTANTS.MaxSkillLevel +
// this.strength / CONSTANTS.MaxSkillLevel +
// this.defense / CONSTANTS.MaxSkillLevel +
// this.dexterity / CONSTANTS.MaxSkillLevel +
// this.agility / CONSTANTS.MaxSkillLevel) / 4.5;
// return t * this.faction_rep_mult;
// }
// export function getFactionFieldWorkRepGain(this: IPlayer) {
// var t = 0.9 * (this.hacking / CONSTANTS.MaxSkillLevel +
// this.strength / CONSTANTS.MaxSkillLevel +
// this.defense / CONSTANTS.MaxSkillLevel +
// this.dexterity / CONSTANTS.MaxSkillLevel +
// this.agility / CONSTANTS.MaxSkillLevel +
// this.charisma / CONSTANTS.MaxSkillLevel +
// this.intelligence / CONSTANTS.MaxSkillLevel) / 5.5;
// return t * this.faction_rep_mult;
// }
//Cancels the player's current "work" assignment and gives the proper rewards //Cancels the player's current "work" assignment and gives the proper rewards
//Used only for Singularity functions, so no popups are created //Used only for Singularity functions, so no popups are created
export function singularityStopWork(this: IPlayer): string { export function singularityStopWork(this: IPlayer): string {
@ -1257,9 +1064,6 @@ export function singularityStopWork(this: IPlayer): string {
case WorkType.CompanyPartTime: case WorkType.CompanyPartTime:
res = this.finishWorkPartTime(true); res = this.finishWorkPartTime(true);
break; break;
case WorkType.Faction:
res = this.finishFactionWork(true, true);
break;
default: default:
console.error(`Unrecognized work type (${this.workType})`); console.error(`Unrecognized work type (${this.workType})`);
return ""; return "";

@ -28,7 +28,6 @@ import { CONSTANTS } from "../../Constants";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { FactionWorkType } from "../../Faction/FactionWorkTypeEnum";
import { CityName } from "../../Locations/data/CityNames"; import { CityName } from "../../Locations/data/CityNames";
import { LocationName } from "../../Locations/data/LocationNames"; import { LocationName } from "../../Locations/data/LocationNames";
@ -37,6 +36,7 @@ import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviv
import { BladeburnerConstants } from "../../Bladeburner/data/Constants"; 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";
export class Sleeve extends Person { export class Sleeve extends Person {
/** /**
@ -94,7 +94,7 @@ export class Sleeve extends Person {
/** /**
* Keeps track of what type of work sleeve is doing for faction, if applicable * Keeps track of what type of work sleeve is doing for faction, if applicable
*/ */
factionWorkType: FactionWorkType = FactionWorkType.None; factionWorkType: FactionWorkType = FactionWorkType.HACKING;
/** /**
* Records experience gain rate for the current task * Records experience gain rate for the current task
@ -444,11 +444,11 @@ export class Sleeve extends Person {
} }
switch (this.factionWorkType) { switch (this.factionWorkType) {
case FactionWorkType.Hacking: case FactionWorkType.HACKING:
return this.getFactionHackingWorkRepGain() * (this.shock / 100) * favorMult; return this.getFactionHackingWorkRepGain() * (this.shock / 100) * favorMult;
case FactionWorkType.Field: case FactionWorkType.FIELD:
return this.getFactionFieldWorkRepGain() * (this.shock / 100) * favorMult; return this.getFactionFieldWorkRepGain() * (this.shock / 100) * favorMult;
case FactionWorkType.Security: case FactionWorkType.SECURITY:
return this.getFactionSecurityWorkRepGain() * (this.shock / 100) * favorMult; return this.getFactionSecurityWorkRepGain() * (this.shock / 100) * favorMult;
default: default:
console.warn(`Invalid Sleeve.factionWorkType property in Sleeve.getRepGain(): ${this.factionWorkType}`); console.warn(`Invalid Sleeve.factionWorkType property in Sleeve.getRepGain(): ${this.factionWorkType}`);
@ -660,7 +660,7 @@ export class Sleeve extends Person {
this.currentTask = SleeveTaskType.Idle; this.currentTask = SleeveTaskType.Idle;
this.currentTaskTime = 0; this.currentTaskTime = 0;
this.currentTaskMaxTime = 0; this.currentTaskMaxTime = 0;
this.factionWorkType = FactionWorkType.None; this.factionWorkType = FactionWorkType.HACKING;
this.crimeType = ""; this.crimeType = "";
this.currentTaskLocation = ""; this.currentTaskLocation = "";
this.gymStatType = ""; this.gymStatType = "";
@ -969,13 +969,13 @@ export class Sleeve extends Person {
if (!factionInfo.offerHackingWork) { if (!factionInfo.offerHackingWork) {
return false; return false;
} }
this.factionWorkType = FactionWorkType.Hacking; this.factionWorkType = FactionWorkType.HACKING;
this.gainRatesForTask.hack = 0.15 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.hack = 0.15 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
} else if (sanitizedWorkType.includes("field")) { } else if (sanitizedWorkType.includes("field")) {
if (!factionInfo.offerFieldWork) { if (!factionInfo.offerFieldWork) {
return false; return false;
} }
this.factionWorkType = FactionWorkType.Field; this.factionWorkType = FactionWorkType.FIELD;
this.gainRatesForTask.hack = 0.1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.hack = 0.1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.str = 0.1 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.str = 0.1 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.def = 0.1 * this.defense_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.def = 0.1 * this.defense_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
@ -986,7 +986,7 @@ export class Sleeve extends Person {
if (!factionInfo.offerSecurityWork) { if (!factionInfo.offerSecurityWork) {
return false; return false;
} }
this.factionWorkType = FactionWorkType.Security; this.factionWorkType = FactionWorkType.SECURITY;
this.gainRatesForTask.hack = 0.1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.hack = 0.1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.str = 0.15 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.str = 0.15 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.def = 0.15 * this.defense_exp_mult * BitNodeMultipliers.FactionWorkExpGain; this.gainRatesForTask.def = 0.15 * this.defense_exp_mult * BitNodeMultipliers.FactionWorkExpGain;

@ -1,8 +1,8 @@
import { Box, Button, Paper, Tooltip, Typography } from "@mui/material"; import { Box, Button, Paper, Tooltip, Typography } from "@mui/material";
import React, { useState } from "react"; import React, { useState } from "react";
import { FactionWorkType } from "../../../Work/data/FactionWorkType";
import { CONSTANTS } from "../../../Constants"; import { CONSTANTS } from "../../../Constants";
import { Crimes } from "../../../Crime/Crimes"; import { Crimes } from "../../../Crime/Crimes";
import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum";
import { use } from "../../../ui/Context"; import { use } from "../../../ui/Context";
import { numeralWrapper } from "../../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { ProgressBar } from "../../../ui/React/Progress"; import { ProgressBar } from "../../../ui/React/Progress";
@ -75,13 +75,13 @@ export function SleeveElem(props: IProps): React.ReactElement {
case SleeveTaskType.Faction: { case SleeveTaskType.Faction: {
let doing = "nothing"; let doing = "nothing";
switch (props.sleeve.factionWorkType) { switch (props.sleeve.factionWorkType) {
case FactionWorkType.Field: case FactionWorkType.FIELD:
doing = "Field work"; doing = "Field work";
break; break;
case FactionWorkType.Hacking: case FactionWorkType.HACKING:
doing = "Hacking contracts"; doing = "Hacking contracts";
break; break;
case FactionWorkType.Security: case FactionWorkType.SECURITY:
doing = "Security work"; doing = "Security work";
break; break;
} }

@ -6,10 +6,10 @@ import { Crimes } from "../../../Crime/Crimes";
import { LocationName } from "../../../Locations/data/LocationNames"; import { LocationName } from "../../../Locations/data/LocationNames";
import { CityName } from "../../../Locations/data/CityNames"; import { CityName } from "../../../Locations/data/CityNames";
import { Factions } from "../../../Faction/Factions"; import { Factions } from "../../../Faction/Factions";
import { FactionWorkType } from "../../../Faction/FactionWorkTypeEnum";
import Select, { SelectChangeEvent } from "@mui/material/Select"; import Select, { SelectChangeEvent } from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import { FactionNames } from "../../../Faction/data/FactionNames"; import { FactionNames } from "../../../Faction/data/FactionNames";
import { FactionWorkType } from "../../../Work/data/FactionWorkType";
const universitySelectorOptions: string[] = [ const universitySelectorOptions: string[] = [
"Study Computer Science", "Study Computer Science",
@ -254,13 +254,13 @@ function getABC(sleeve: Sleeve): [string, string, string] {
case SleeveTaskType.Faction: { case SleeveTaskType.Faction: {
let workType = ""; let workType = "";
switch (sleeve.factionWorkType) { switch (sleeve.factionWorkType) {
case FactionWorkType.Hacking: case FactionWorkType.HACKING:
workType = "Hacking Contracts"; workType = "Hacking Contracts";
break; break;
case FactionWorkType.Field: case FactionWorkType.FIELD:
workType = "Field Work"; workType = "Field Work";
break; break;
case FactionWorkType.Security: case FactionWorkType.SECURITY:
workType = "Security Work"; workType = "Security Work";
break; break;
} }

@ -49,8 +49,6 @@ interface Player {
crime_success_mult: number; crime_success_mult: number;
isWorking: boolean; isWorking: boolean;
workType: string; workType: string;
currentWorkFactionName: string;
currentWorkFactionDescription: string;
workHackExpGainRate: number; workHackExpGainRate: number;
workStrExpGainRate: number; workStrExpGainRate: number;
workDefExpGainRate: number; workDefExpGainRate: number;

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { LocationName } from "../Locations/data/LocationNames"; import { LocationName } from "../Locations/data/LocationNames";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
@ -129,14 +129,12 @@ export class ClassWork extends Work {
classType: ClassType; classType: ClassType;
location: LocationName; location: LocationName;
cyclesWorked: number; cyclesWorked: number;
singularity: boolean;
earnings = newWorkStats(); earnings = newWorkStats();
constructor(params?: ClassWorkParams) { constructor(params?: ClassWorkParams) {
super(WorkType.CLASS); super(WorkType.CLASS, params?.singularity ?? true);
this.classType = params?.classType ?? ClassType.StudyComputerScience; this.classType = params?.classType ?? ClassType.StudyComputerScience;
this.location = params?.location ?? LocationName.Sector12RothmanUniversity; this.location = params?.location ?? LocationName.Sector12RothmanUniversity;
this.singularity = params?.singularity ?? false;
this.cyclesWorked = 0; this.cyclesWorked = 0;
} }
@ -186,15 +184,14 @@ export class ClassWork extends Work {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("ClassWork", this); return Generic_toJSON("ClassWork", this);
} }
/** /**
* Initiatizes a ClassWork object from a JSON save state. * Initiatizes a ClassWork object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): ClassWork {
static fromJSON(value: any): ClassWork {
return Generic_fromJSON(ClassWork, value.data); return Generic_fromJSON(ClassWork, value.data);
} }
} }

@ -1,5 +1,5 @@
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
@ -20,16 +20,14 @@ export class CreateProgramWork extends Work {
programName: string; programName: string;
// amount of cycles spent doing this task // amount of cycles spent doing this task
cyclesWorked: number; cyclesWorked: number;
singularity: boolean;
// amount of effective work completed on the program (time boosted by skills). // amount of effective work completed on the program (time boosted by skills).
unitCompleted: number; unitCompleted: number;
constructor(params?: CreateProgramWorkParams) { constructor(params?: CreateProgramWorkParams) {
super(WorkType.CREATE_PROGRAM); super(WorkType.CREATE_PROGRAM, params?.singularity ?? true);
this.cyclesWorked = 0; this.cyclesWorked = 0;
this.unitCompleted = 0; this.unitCompleted = 0;
this.programName = params?.programName ?? ""; this.programName = params?.programName ?? "";
this.singularity = params?.singularity ?? false;
if (params?.player) { if (params?.player) {
const player = params.player; const player = params.player;
@ -109,15 +107,14 @@ export class CreateProgramWork extends Work {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("CreateProgramWork", this); return Generic_toJSON("CreateProgramWork", this);
} }
/** /**
* Initiatizes a CreateProgramWork object from a JSON save state. * Initiatizes a CreateProgramWork object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): CreateProgramWork {
static fromJSON(value: any): CreateProgramWork {
return Generic_fromJSON(CreateProgramWork, value.data); return Generic_fromJSON(CreateProgramWork, value.data);
} }
} }

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { Crime } from "../Crime/Crime"; import { Crime } from "../Crime/Crime";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { determineCrimeSuccess } from "../Crime/CrimeHelpers"; import { determineCrimeSuccess } from "../Crime/CrimeHelpers";
@ -21,12 +21,10 @@ export const isCrimeWork = (w: Work | null): w is CrimeWork => w !== null && w.t
export class CrimeWork extends Work { export class CrimeWork extends Work {
crimeType: CrimeType; crimeType: CrimeType;
cyclesWorked: number; cyclesWorked: number;
singularity: boolean;
constructor(params?: CrimeWorkParams) { constructor(params?: CrimeWorkParams) {
super(WorkType.CRIME); super(WorkType.CRIME, params?.singularity ?? true);
this.crimeType = params?.crimeType ?? CrimeType.Shoplift; this.crimeType = params?.crimeType ?? CrimeType.Shoplift;
this.singularity = params?.singularity ?? false;
this.cyclesWorked = 0; this.cyclesWorked = 0;
} }
@ -121,15 +119,14 @@ export class CrimeWork extends Work {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("CrimeWork", this); return Generic_toJSON("CrimeWork", this);
} }
/** /**
* Initiatizes a CrimeWork object from a JSON save state. * Initiatizes a CrimeWork object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): CrimeWork {
static fromJSON(value: any): CrimeWork {
return Generic_fromJSON(CrimeWork, value.data); return Generic_fromJSON(CrimeWork, value.data);
} }
} }

103
src/Work/FactionWork.tsx Normal file

@ -0,0 +1,103 @@
import React from "react";
import { Work, WorkType } from "./Work";
import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { IPlayer } from "../PersonObjects/IPlayer";
import { FactionNames } from "../Faction/data/FactionNames";
import { Factions } from "../Faction/Factions";
import { Faction } from "../Faction/Faction";
import { applyWorkStats, WorkStats } from "./WorkStats";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reputation } from "../ui/React/Reputation";
import {
getFactionFieldWorkRepGain,
getFactionSecurityWorkRepGain,
getHackingWorkRepGain,
} from "../PersonObjects/formulas/reputation";
import { CONSTANTS } from "../Constants";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { calculateFactionExp } from "./formulas/Faction";
import { FactionWorkType } from "./data/FactionWorkType";
interface FactionWorkParams {
singularity: boolean;
factionWorkType: FactionWorkType;
faction: string;
}
export const isFactionWork = (w: Work | null): w is FactionWork => w !== null && w.type === WorkType.FACTION;
export class FactionWork extends Work {
factionWorkType: FactionWorkType;
factionName: string;
cyclesWorked: number;
constructor(params?: FactionWorkParams) {
super(WorkType.FACTION, params?.singularity ?? true);
this.factionWorkType = params?.factionWorkType ?? FactionWorkType.HACKING;
this.factionName = params?.faction ?? FactionNames.Sector12;
this.cyclesWorked = 0;
}
getFaction(): Faction {
const f = Factions[this.factionName];
if (!f) throw new Error(`Faction work started with invalid / unknown faction: '${this.factionName}'`);
return f;
}
getReputationRate(player: IPlayer): number {
const faction = this.getFaction();
const repFormulas = {
[FactionWorkType.HACKING]: getHackingWorkRepGain,
[FactionWorkType.FIELD]: getFactionFieldWorkRepGain,
[FactionWorkType.SECURITY]: getFactionSecurityWorkRepGain,
};
const rep = repFormulas[this.factionWorkType](player, faction);
let focusBonus = 1;
if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager)) {
focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus;
}
return rep * focusBonus;
}
getExpRates(player: IPlayer): WorkStats {
return calculateFactionExp(player, this.factionWorkType);
}
process(player: IPlayer, cycles: number): boolean {
this.cyclesWorked += cycles;
this.getFaction().playerReputation += this.getReputationRate(player) * cycles;
const rate = this.getExpRates(player);
applyWorkStats(player, rate, cycles, "class");
return false;
}
finish(): void {
if (!this.singularity) {
dialogBoxCreate(
<>
You worked for {this.getFaction().name}.
<br />
They now have a total of <Reputation reputation={this.getFaction().playerReputation} /> reputation.
</>,
);
}
}
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): IReviverValue {
return Generic_toJSON("FactionWork", this);
}
/**
* Initiatizes a FactionWork object from a JSON save state.
*/
static fromJSON(value: IReviverValue): FactionWork {
return Generic_fromJSON(FactionWork, value.data);
}
}
Reviver.constructors.FactionWork = FactionWork;

@ -7,7 +7,7 @@ import { Work, WorkType } from "./Work";
import { graftingIntBonus } from "../PersonObjects/Grafting/GraftingHelpers"; import { graftingIntBonus } from "../PersonObjects/Grafting/GraftingHelpers";
import { applyAugmentation } from "../Augmentation/AugmentationHelpers"; import { applyAugmentation } from "../Augmentation/AugmentationHelpers";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation"; import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations"; import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
@ -15,22 +15,21 @@ export const isGraftingWork = (w: Work | null): w is GraftingWork => w !== null
interface GraftingWorkParams { interface GraftingWorkParams {
augmentation: string; augmentation: string;
singularity: boolean;
player: IPlayer; player: IPlayer;
singularity: boolean;
} }
export class GraftingWork extends Work { export class GraftingWork extends Work {
augmentation: string; augmentation: string;
singularity: boolean;
cyclesWorked: number; cyclesWorked: number;
unitCompleted: number; unitCompleted: number;
constructor(params?: GraftingWorkParams) { constructor(params?: GraftingWorkParams) {
super(WorkType.GRAFTING); super(WorkType.GRAFTING, params?.singularity ?? true);
this.cyclesWorked = 0; this.cyclesWorked = 0;
this.unitCompleted = 0; this.unitCompleted = 0;
this.augmentation = params?.augmentation ?? AugmentationNames.Targeting1; this.augmentation = params?.augmentation ?? AugmentationNames.Targeting1;
this.singularity = params?.singularity ?? false;
if (params?.player) params.player.loseMoney(GraftableAugmentations[this.augmentation].cost, "augmentations"); if (params?.player) params.player.loseMoney(GraftableAugmentations[this.augmentation].cost, "augmentations");
} }
@ -90,15 +89,14 @@ export class GraftingWork extends Work {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("GraftingWork", this); return Generic_toJSON("GraftingWork", this);
} }
/** /**
* Initiatizes a GraftingWork object from a JSON save state. * Initiatizes a GraftingWork object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): GraftingWork {
static fromJSON(value: any): GraftingWork {
return Generic_fromJSON(GraftingWork, value.data); return Generic_fromJSON(GraftingWork, value.data);
} }
} }

@ -1,15 +1,18 @@
import { IPlayer } from "src/PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IReviverValue } from "../utils/JSONReviver";
export abstract class Work { export abstract class Work {
type: WorkType; type: WorkType;
singularity: boolean;
constructor(type: WorkType) { constructor(type: WorkType, singularity: boolean) {
this.type = type; this.type = type;
this.singularity = singularity;
} }
abstract process(player: IPlayer, cycles: number): boolean; abstract process(player: IPlayer, cycles: number): boolean;
abstract finish(player: IPlayer, cancelled: boolean): void; abstract finish(player: IPlayer, cancelled: boolean): void;
abstract toJSON(): any; abstract toJSON(): IReviverValue;
} }
export enum WorkType { export enum WorkType {
@ -17,4 +20,5 @@ export enum WorkType {
CLASS = "CLASS", CLASS = "CLASS",
CREATE_PROGRAM = "CREATE_PROGRAM", CREATE_PROGRAM = "CREATE_PROGRAM",
GRAFTING = "GRAFTING", GRAFTING = "GRAFTING",
FACTION = "FACTION",
} }

@ -4,7 +4,6 @@ import { IPlayer } from "../PersonObjects/IPlayer";
export interface WorkStats { export interface WorkStats {
money: number; money: number;
reputation: number;
hackExp: number; hackExp: number;
strExp: number; strExp: number;
defExp: number; defExp: number;
@ -16,7 +15,6 @@ export interface WorkStats {
interface newWorkStatsParams { interface newWorkStatsParams {
money?: number; money?: number;
reputation?: number;
hackExp?: number; hackExp?: number;
strExp?: number; strExp?: number;
defExp?: number; defExp?: number;
@ -29,7 +27,6 @@ interface newWorkStatsParams {
export const newWorkStats = (params?: newWorkStatsParams): WorkStats => { export const newWorkStats = (params?: newWorkStatsParams): WorkStats => {
return { return {
money: params?.money ?? 0, money: params?.money ?? 0,
reputation: params?.reputation ?? 0,
hackExp: params?.hackExp ?? 0, hackExp: params?.hackExp ?? 0,
strExp: params?.strExp ?? 0, strExp: params?.strExp ?? 0,
defExp: params?.defExp ?? 0, defExp: params?.defExp ?? 0,
@ -43,7 +40,6 @@ export const newWorkStats = (params?: newWorkStatsParams): WorkStats => {
export const sumWorkStats = (w0: WorkStats, w1: WorkStats): WorkStats => { export const sumWorkStats = (w0: WorkStats, w1: WorkStats): WorkStats => {
return { return {
money: w0.money + w1.money, money: w0.money + w1.money,
reputation: w0.reputation + w1.reputation,
hackExp: w0.hackExp + w1.hackExp, hackExp: w0.hackExp + w1.hackExp,
strExp: w0.strExp + w1.strExp, strExp: w0.strExp + w1.strExp,
defExp: w0.defExp + w1.defExp, defExp: w0.defExp + w1.defExp,
@ -57,7 +53,6 @@ export const sumWorkStats = (w0: WorkStats, w1: WorkStats): WorkStats => {
export const scaleWorkStats = (w: WorkStats, n: number): WorkStats => { export const scaleWorkStats = (w: WorkStats, n: number): WorkStats => {
return { return {
money: w.money * n, money: w.money * n,
reputation: w.reputation * n,
hackExp: w.hackExp * n, hackExp: w.hackExp * n,
strExp: w.strExp * n, strExp: w.strExp * n,
defExp: w.defExp * n, defExp: w.defExp * n,
@ -75,7 +70,6 @@ export const applyWorkStats = (player: IPlayer, workStats: WorkStats, cycles: nu
} }
const gains = { const gains = {
money: workStats.money * cycles, money: workStats.money * cycles,
reputation: focusBonus * workStats.reputation * cycles,
hackExp: focusBonus * workStats.hackExp * cycles, hackExp: focusBonus * workStats.hackExp * cycles,
strExp: focusBonus * workStats.strExp * cycles, strExp: focusBonus * workStats.strExp * cycles,
defExp: focusBonus * workStats.defExp * cycles, defExp: focusBonus * workStats.defExp * cycles,

@ -0,0 +1,5 @@
export enum FactionWorkType {
HACKING = "HACKING",
FIELD = "FIELD",
SECURITY = "SECURITY",
}

@ -35,7 +35,6 @@ export function calculateClassEarnings(player: IPlayer, work: ClassWork): WorkSt
const chaExp = ((classs.earnings.chaExp * location.expMult) / gameCPS) * hashMult; const chaExp = ((classs.earnings.chaExp * location.expMult) / gameCPS) * hashMult;
return { return {
money: cost, money: cost,
reputation: 0,
hackExp: hackExp * player.hacking_exp_mult * BitNodeMultipliers.ClassGymExpGain, hackExp: hackExp * player.hacking_exp_mult * BitNodeMultipliers.ClassGymExpGain,
strExp: strExp * player.strength_exp_mult * BitNodeMultipliers.ClassGymExpGain, strExp: strExp * player.strength_exp_mult * BitNodeMultipliers.ClassGymExpGain,
defExp: defExp * player.defense_exp_mult * BitNodeMultipliers.ClassGymExpGain, defExp: defExp * player.defense_exp_mult * BitNodeMultipliers.ClassGymExpGain,

@ -0,0 +1,40 @@
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { FactionWorkType } from "../data/FactionWorkType";
import { newWorkStats, WorkStats } from "../WorkStats";
const gameCPS = 1000 / CONSTANTS._idleSpeed; // 5 cycles per second
export const FactionWorkStats: Record<FactionWorkType, WorkStats> = {
[FactionWorkType.HACKING]: newWorkStats({ hackExp: 15 }),
[FactionWorkType.FIELD]: newWorkStats({
hackExp: 10,
strExp: 10,
defExp: 10,
dexExp: 10,
agiExp: 10,
chaExp: 10,
}),
[FactionWorkType.SECURITY]: newWorkStats({
hackExp: 5,
strExp: 15,
defExp: 15,
dexExp: 15,
agiExp: 15,
}),
};
export function calculateFactionExp(player: IPlayer, tpe: FactionWorkType): WorkStats {
const baseStats = FactionWorkStats[tpe];
return {
money: 0,
hackExp: (baseStats.hackExp * player.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
strExp: (baseStats.strExp * player.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
defExp: (baseStats.defExp * player.defense_exp_mult * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
dexExp: (baseStats.dexExp * player.dexterity_exp_mult * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
agiExp: (baseStats.agiExp * player.agility_exp_mult * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
chaExp: (baseStats.chaExp * player.charisma_exp_mult * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
intExp: 0,
};
}

@ -303,9 +303,6 @@ const Engine: {
} else if (Player.isWorking) { } else if (Player.isWorking) {
Player.focus = true; Player.focus = true;
switch (Player.workType) { switch (Player.workType) {
case WorkType.Faction:
Player.workForFaction(numCyclesOffline);
break;
case WorkType.CompanyPartTime: case WorkType.CompanyPartTime:
Player.workPartTime(numCyclesOffline); Player.workPartTime(numCyclesOffline);
break; break;

@ -87,6 +87,7 @@ import { BypassWrapper } from "./React/BypassWrapper";
import _wrap from "lodash/wrap"; import _wrap from "lodash/wrap";
import _functions from "lodash/functions"; import _functions from "lodash/functions";
import { Apr1 } from "./Apr1"; import { Apr1 } from "./Apr1";
import { isFactionWork } from "../Work/FactionWork";
const htmlLocation = location; const htmlLocation = location;
@ -165,7 +166,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
const setRerender = useState(0)[1]; const setRerender = useState(0)[1];
const [augPage, setAugPage] = useState<boolean>(false); const [augPage, setAugPage] = useState<boolean>(false);
const [faction, setFaction] = useState<Faction>( const [faction, setFaction] = useState<Faction>(
player.currentWorkFactionName ? Factions[player.currentWorkFactionName] : (undefined as unknown as Faction), isFactionWork(player.currentWork) ? Factions[player.currentWork.factionName] : (undefined as unknown as Faction),
); );
if (faction === undefined && page === Page.Faction) if (faction === undefined && page === Page.Faction)
throw new Error("Trying to go to a page without the proper setup"); throw new Error("Trying to go to a page without the proper setup");

@ -31,6 +31,8 @@ import { isClassWork } from "../../Work/ClassWork";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { isCreateProgramWork } from "../../Work/CreateProgramWork"; import { isCreateProgramWork } from "../../Work/CreateProgramWork";
import { isGraftingWork } from "../../Work/GraftingWork"; import { isGraftingWork } from "../../Work/GraftingWork";
import { isFactionWork } from "../../Work/FactionWork";
import { ReputationRate } from "./ReputationRate";
interface IProps { interface IProps {
save: () => void; save: () => void;
@ -172,6 +174,22 @@ function Work(): React.ReactElement {
</> </>
); );
} }
if (isFactionWork(player.currentWork)) {
const factionWork = player.currentWork;
header = (
<>
Working for <strong>{factionWork.factionName}</strong>
</>
);
innerText = (
<>
<Reputation reputation={factionWork.getFaction().playerReputation} /> rep
<br />(
<ReputationRate reputation={factionWork.getReputationRate(player) * (1000 / CONSTANTS._idleSpeed)} />)
</>
);
}
switch (player.workType) { switch (player.workType) {
case WorkType.CompanyPartTime: case WorkType.CompanyPartTime:
case WorkType.Company: case WorkType.Company:
@ -191,23 +209,6 @@ function Work(): React.ReactElement {
</> </>
); );
break; break;
case WorkType.Faction:
details = (
<>
{player.factionWorkType} for <strong>{player.currentWorkFactionName}</strong>
</>
);
header = (
<>
Working for <strong>{player.currentWorkFactionName}</strong>
</>
);
innerText = (
<>
+<Reputation reputation={player.workRepGained} /> rep
</>
);
break;
} }
return ( return (

@ -6,7 +6,6 @@ import React, { useEffect, useState } from "react";
import { Companies } from "../Company/Companies"; import { Companies } from "../Company/Companies";
import { Company } from "../Company/Company"; import { Company } from "../Company/Company";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { Factions } from "../Faction/Factions";
import { LocationName } from "../Locations/data/LocationNames"; import { LocationName } from "../Locations/data/LocationNames";
import { Locations } from "../Locations/Locations"; import { Locations } from "../Locations/Locations";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
@ -22,9 +21,11 @@ import { StatsRow } from "./React/StatsRow";
import { WorkType } from "../utils/WorkType"; import { WorkType } from "../utils/WorkType";
import { isCrimeWork } from "../Work/CrimeWork"; import { isCrimeWork } from "../Work/CrimeWork";
import { isClassWork } from "../Work/ClassWork"; import { isClassWork } from "../Work/ClassWork";
import { WorkStats } from "../Work/WorkStats"; import { newWorkStats, WorkStats } from "../Work/WorkStats";
import { isCreateProgramWork } from "../Work/CreateProgramWork"; import { isCreateProgramWork } from "../Work/CreateProgramWork";
import { isGraftingWork } from "../Work/GraftingWork"; import { isGraftingWork } from "../Work/GraftingWork";
import { isFactionWork } from "../Work/FactionWork";
import { FactionWorkType } from "../Work/data/FactionWorkType";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle; const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
@ -47,81 +48,69 @@ interface IWorkInfo {
stopTooltip?: string | React.ReactElement; stopTooltip?: string | React.ReactElement;
} }
export function ExpRows(total: WorkStats, rate: WorkStats): React.ReactElement[] { export function ExpRows(rate: WorkStats): React.ReactElement[] {
return [ return [
total.hackExp > 0 ? ( rate.hackExp > 0 ? (
<StatsRow <StatsRow
name="Hacking Exp" name="Hacking Exp"
color={Settings.theme.hack} color={Settings.theme.hack}
data={{ data={{
content: `${numeralWrapper.formatExp(total.hackExp)} (${numeralWrapper.formatExp( content: `${numeralWrapper.formatExp(rate.hackExp * CYCLES_PER_SEC)} / sec`,
rate.hackExp * CYCLES_PER_SEC,
)} / sec)`,
}} }}
/> />
) : ( ) : (
<></> <></>
), ),
total.strExp > 0 ? ( rate.strExp > 0 ? (
<StatsRow <StatsRow
name="Strength Exp" name="Strength Exp"
color={Settings.theme.combat} color={Settings.theme.combat}
data={{ data={{
content: `${numeralWrapper.formatExp(total.strExp)} (${numeralWrapper.formatExp( content: `${numeralWrapper.formatExp(rate.strExp * CYCLES_PER_SEC)} / sec`,
rate.strExp * CYCLES_PER_SEC,
)} / sec)`,
}} }}
/> />
) : ( ) : (
<></> <></>
), ),
total.defExp > 0 ? ( rate.defExp > 0 ? (
<StatsRow <StatsRow
name="Defense Exp" name="Defense Exp"
color={Settings.theme.combat} color={Settings.theme.combat}
data={{ data={{
content: `${numeralWrapper.formatExp(total.defExp)} (${numeralWrapper.formatExp( content: `${numeralWrapper.formatExp(rate.defExp * CYCLES_PER_SEC)} / sec`,
rate.defExp * CYCLES_PER_SEC,
)} / sec)`,
}} }}
/> />
) : ( ) : (
<></> <></>
), ),
total.dexExp > 0 ? ( rate.dexExp > 0 ? (
<StatsRow <StatsRow
name="Dexterity Exp" name="Dexterity Exp"
color={Settings.theme.combat} color={Settings.theme.combat}
data={{ data={{
content: `${numeralWrapper.formatExp(total.dexExp)} (${numeralWrapper.formatExp( content: `${numeralWrapper.formatExp(rate.dexExp * CYCLES_PER_SEC)} / sec`,
rate.dexExp * CYCLES_PER_SEC,
)} / sec)`,
}} }}
/> />
) : ( ) : (
<></> <></>
), ),
total.agiExp > 0 ? ( rate.agiExp > 0 ? (
<StatsRow <StatsRow
name="Agility Exp" name="Agility Exp"
color={Settings.theme.combat} color={Settings.theme.combat}
data={{ data={{
content: `${numeralWrapper.formatExp(total.agiExp)} (${numeralWrapper.formatExp( content: `${numeralWrapper.formatExp(rate.agiExp * CYCLES_PER_SEC)} / sec`,
rate.agiExp * CYCLES_PER_SEC,
)} / sec)`,
}} }}
/> />
) : ( ) : (
<></> <></>
), ),
total.chaExp > 0 ? ( rate.chaExp > 0 ? (
<StatsRow <StatsRow
name="Charisma Exp" name="Charisma Exp"
color={Settings.theme.cha} color={Settings.theme.cha}
data={{ data={{
content: `${numeralWrapper.formatExp(total.chaExp)} (${numeralWrapper.formatExp( content: `${numeralWrapper.formatExp(rate.chaExp * CYCLES_PER_SEC)} / sec`,
rate.chaExp * CYCLES_PER_SEC,
)} / sec)`,
}} }}
/> />
) : ( ) : (
@ -276,7 +265,7 @@ export function WorkInProgressRoot(): React.ReactElement {
} }
const rates = classWork.calculateRates(player); const rates = classWork.calculateRates(player);
expGains = ExpRows(classWork.earnings, rates); expGains = ExpRows(rates);
workInfo = { workInfo = {
buttons: { buttons: {
cancel: cancel, cancel: cancel,
@ -373,18 +362,16 @@ export function WorkInProgressRoot(): React.ReactElement {
), ),
}; };
} }
}
switch (player.workType) { if (isFactionWork(player.currentWork)) {
case WorkType.Faction: { const faction = player.currentWork.getFaction();
const faction = Factions[player.currentWorkFactionName];
if (!faction) { if (!faction) {
workInfo = { workInfo = {
buttons: { buttons: {
cancel: () => router.toFactions(), cancel: () => router.toFactions(),
}, },
title: title:
`You have not joined ${player.currentWorkFactionName || "(Faction not found)"} at this time,` + `You have not joined ${player.currentWork.factionName || "(Faction not found)"} at this time,` +
" please try again if you think this should have worked", " please try again if you think this should have worked",
stopText: "Back to Factions", stopText: "Back to Factions",
@ -393,13 +380,21 @@ export function WorkInProgressRoot(): React.ReactElement {
function cancel(): void { function cancel(): void {
router.toFaction(faction); router.toFaction(faction);
player.finishFactionWork(true); player.finishNEWWork(true);
} }
function unfocus(): void { function unfocus(): void {
router.toFaction(faction); router.toFaction(faction);
player.stopFocusing(); player.stopFocusing();
} }
const description = {
[FactionWorkType.HACKING]: "carrying out hacking contracts",
[FactionWorkType.FIELD]: "carrying out field missions",
[FactionWorkType.SECURITY]: "performing security detail",
};
const exp = player.currentWork.getExpRates(player);
workInfo = { workInfo = {
buttons: { buttons: {
cancel: cancel, cancel: cancel,
@ -407,44 +402,27 @@ export function WorkInProgressRoot(): React.ReactElement {
}, },
title: ( title: (
<> <>
You are currently {player.currentWorkFactionDescription} for your faction <b>{faction.name}</b> You are currently {description[player.currentWork.factionWorkType]} for <b>{faction.name}</b>
</> </>
), ),
description: ( description: (
<> <>
Current Faction Reputation: <Reputation reputation={faction.playerReputation} /> Current Faction Reputation: <Reputation reputation={faction.playerReputation} /> (
<ReputationRate reputation={player.currentWork.getReputationRate(player) * CYCLES_PER_SEC} />)
</> </>
), ),
gains: [ gains: ExpRows(exp),
player.workMoneyGained > 0 ? (
<StatsRow name="Money" color={Settings.theme.money}>
<Typography>
<Money money={player.workMoneyGained} /> (
<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />)
</Typography>
</StatsRow>
) : (
<></>
),
<StatsRow name="Faction Reputation" color={Settings.theme.rep}>
<Typography>
<Reputation reputation={player.workRepGained} /> (
<ReputationRate reputation={player.workRepGainRate * CYCLES_PER_SEC} />)
</Typography>
</StatsRow>,
...expGains,
],
progress: { progress: {
elapsed: player.timeWorked, elapsed: player.currentWork.cyclesWorked * CONSTANTS._idleSpeed,
}, },
stopText: "Stop Faction work", stopText: "Stop Faction work",
}; };
break;
} }
}
switch (player.workType) {
case WorkType.Company: { case WorkType.Company: {
const comp = Companies[player.companyName]; const comp = Companies[player.companyName];
if (comp == null || !(comp instanceof Company)) { if (comp == null || !(comp instanceof Company)) {

@ -9,29 +9,6 @@ export enum WorkType {
GraftAugmentation = "Grafting an Augmentation", GraftAugmentation = "Grafting an Augmentation",
} }
export enum PlayerFactionWorkType {
None = "",
Hacking = "Faction Hacking Work",
Field = "Faction Field Work",
Security = "Faction Security Work",
}
export enum ClassType {
None = "",
StudyComputerScience = "studying Computer Science",
DataStructures = "taking a Data Structures course",
Networks = "taking a Networks course",
Algorithms = "taking an Algorithms course",
Management = "taking a Management course",
Leadership = "taking a Leadership course",
GymStrength = "training your strength at a gym",
GymDefense = "training your defense at a gym",
GymDexterity = "training your dexterity at a gym",
GymAgility = "training your agility at a gym",
}
export enum CrimeType { export enum CrimeType {
None = "", None = "",
Shoplift = "shoplift", Shoplift = "shoplift",