From bcb231a966f34788d2b5a181870c63f38df4ce89 Mon Sep 17 00:00:00 2001 From: danielyxie Date: Mon, 14 Jan 2019 19:34:04 -0800 Subject: [PATCH] Implemented Sleeve UI. Compiled but untested --- .../netscriptsingularityfunctions.rst | 4 +- favicon.ico | Bin 2734 -> 2734 bytes src/CodingContractGenerator.js | 7 +- src/Constants.ts | 5 +- src/Crime/Crime.ts | 147 +++++++ src/Crime/CrimeHelpers.js | 57 +++ src/Crime/Crimes.ts | 163 ++++++++ src/Crimes.js | 275 ------------- src/Location.js | 36 +- src/NetscriptFunctions.js | 49 ++- src/PersonObjects/Person.ts | 166 +++----- src/PersonObjects/Sleeve/Sleeve.ts | 148 +++++-- src/PersonObjects/Sleeve/SleeveUI.ts | 375 ++++++++++++++++-- src/Player.js | 148 +++---- src/SaveObject.js | 10 + src/engine.js | 15 +- src/ui/numeralFormat.ts | 11 +- utils/uiHelpers/createOptionElement.ts | 6 +- utils/uiHelpers/getSelectData.ts | 9 + 19 files changed, 1058 insertions(+), 573 deletions(-) create mode 100644 src/Crime/Crime.ts create mode 100644 src/Crime/CrimeHelpers.js create mode 100644 src/Crime/Crimes.ts delete mode 100644 src/Crimes.js create mode 100644 utils/uiHelpers/getSelectData.ts diff --git a/doc/source/netscript/netscriptsingularityfunctions.rst b/doc/source/netscript/netscriptsingularityfunctions.rst index 6e44ab72e..63ddae702 100644 --- a/doc/source/netscript/netscriptsingularityfunctions.rst +++ b/doc/source/netscript/netscriptsingularityfunctions.rst @@ -164,9 +164,9 @@ getCharacterInformation { bitnode: Current BitNode number city: Name of city you are currently in - company: Name of company factions: Array of factions you are currently a member of - jobTitle: Name of job + jobs: Array of all companies at which you have jobs + jobTitle: Array of job positions for all companies you are employed at. Same order as 'jobs' tor: Boolean indicating whether or not you have a tor router // The following is an object with many of the player's multipliers from Augmentations/Source Files diff --git a/favicon.ico b/favicon.ico index 6b22eb7bbbdd8fd29a393fb0a96462957010b416..060140bb4a8ec094af5810a262caa96adb88f594 100644 GIT binary patch delta 29 YcmZ1{x=wU+0E-{f<^&cyRALz;0EzVop#T5? literal 2734 zcmeHJJ#Q015Ph~UULyqDDmh%BC>LZD7ku0aK@c>Dlm=0dLzvKju8JsEB=S+ZN?HnP zDt-mOfgeGMlpg`_?fMK9v4j-6@|<_)&BxBn&aQO_w9rA8#pDs*cY%ih(lp3F+rSs< z;yB2|7I1P27}6)>s5rDvGzrY(*w10Qd-fK??0|5bQ*3^IjQHRt?*BT(^`kp~ zd9L8O9f)>dYJ7C2cE+HtF?QyZ-e%&!$H1UR(X|}0quxnhOTRB@kVyq&X8!w#sW(~h zZ+U&q;5{l_BoqDHeTKpjLvg7fC$8j`r(1g|Byw&?gQ1Rn5|K8;!o2%36i7 zG3pYj>BE$BK^F9I9eT2HPMbAsDF>-0!mj#2PE)OTOCa{0zY6_7dB;qvL*sL3D%}Ux z$fsL1=YAaaU-|Pl1q%)SI$oDL-4Pw8^x7rdfjB0PsbJQE{|@yZ6kDUM^G}(0wtw`F z)dzNmsuis|u!s)mJ(F@29L;+Md?z&&PeFHZ#iQFl-r(xX&*w~u^2N%?_=>kZ zjZ!O diff --git a/src/CodingContractGenerator.js b/src/CodingContractGenerator.js index a4c7a1558..cd1e3d9cc 100644 --- a/src/CodingContractGenerator.js +++ b/src/CodingContractGenerator.js @@ -84,7 +84,7 @@ function sanitizeRewardType(rewardType) { if (type === CodingContractRewardType.FactionReputationAll && factionsThatAllowHacking.length === 0) { type = CodingContractRewardType.CompanyReputation; } - if (type === CodingContractRewardType.CompanyReputation && Player.companyName === "") { + if (type === CodingContractRewardType.CompanyReputation && Object.keys(Player.jobs).length === 0) { type = CodingContractRewardType.Money; } @@ -115,8 +115,9 @@ function getRandomReward() { reward.name = randFaction; break; case CodingContractRewardType.CompanyReputation: - if (Player.companyName !== "") { - reward.name = Player.companyName; + const allJobs = Object.keys(Player.jobs); + if (allJobs.length > 0) { + reward.name = allJobs[getRandomInt(0, allJobs.length - 1)]; } else { reward.type = CodingContractRewardType.Money; } diff --git a/src/Constants.ts b/src/Constants.ts index 293a2cda0..3fb272428 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -511,7 +511,10 @@ export let CONSTANTS: IMap = { * Stock Market Changes: ** Each stock now has a maximum number of shares you can purchase (both Long and Short positions combined) ** Added getStockMaxShares() Netscript function to the TIX API - + + * Job Changes: + ** You can now hold multiple jobs at once. This means you no longer lose reputation when leaving a company + ** Because of this change, the getCharacterInformation() Netscript function returns a slightly different value * Home Computer RAM is now capped at 2 ^ 30 GB (1073741824 GB) ` diff --git a/src/Crime/Crime.ts b/src/Crime/Crime.ts new file mode 100644 index 000000000..99783e9cd --- /dev/null +++ b/src/Crime/Crime.ts @@ -0,0 +1,147 @@ +import { CONSTANTS } from "../Constants"; +export interface IConstructorParams { + hacking_success_weight?: number; + strength_success_weight?: number; + defense_success_weight?: number; + dexterity_success_weight?: number; + agility_success_weight?: number; + charisma_success_weight?: number; + hacking_exp?: number; + strength_exp?: number; + defense_exp?: number; + dexterity_exp?: number; + agility_exp?: number; + charisma_exp?: number; + intelligence_exp?: number; + + kills?: number; +} + +interface IPlayer { + startCrime(crimeType: string, + hackExp: number, + strExp: number, + defExp: number, + dexExp: number, + agiExp: number, + chaExp: number, + money: number, + time: number, + singParams: any): void; + + hacking_skill: number; + strength: number; + defense: number; + dexterity: number; + agility: number; + charisma: number; + intelligence: number; + + crime_success_mult: number; +} + +export class Crime { + // Number representing the difficulty of the crime. Used for success chance calculations + difficulty: number = 0; + + // Amount of karma lost for SUCCESSFULLY committing this crime + karma: number = 0; + + // How many people die as a result of this crime + kills: number = 0; + + // How much money is given by the + money: number = 0; + + // Name of crime + name: string = ""; + + // Milliseconds it takes to attempt the crime + time: number = 0; + + // Corresponding type in CONSTANTS. Contains a description for the crime activity + type: string = ""; + + // Weighting factors that determine how stats affect the success rate of this crime + hacking_success_weight: number = 0; + strength_success_weight: number = 0; + defense_success_weight: number = 0; + dexterity_success_weight: number = 0; + agility_success_weight: number = 0; + charisma_success_weight: number = 0; + + // How much stat experience is granted by this crime + hacking_exp: number = 0; + strength_exp: number = 0; + defense_exp: number = 0; + dexterity_exp: number = 0; + agility_exp: number = 0; + charisma_exp: number = 0; + intelligence_exp: number = 0; + + constructor(name: string = "", + type: string = "", + time: number = 0, + money: number = 0, + difficulty: number = 0, + karma: number = 0, + params: IConstructorParams={}) { + this.name = name; + this.type = type; + this.time = time; + this.money = money; + this.difficulty = difficulty; + this.karma = karma; + + this.hacking_success_weight = params.hacking_success_weight ? params.hacking_success_weight : 0; + this.strength_success_weight = params.strength_success_weight ? params.strength_success_weight : 0; + this.defense_success_weight = params.defense_success_weight ? params.defense_success_weight : 0; + this.dexterity_success_weight = params.dexterity_success_weight ? params.dexterity_success_weight : 0; + this.agility_success_weight = params.agility_success_weight ? params.agility_success_weight : 0; + this.charisma_success_weight = params.charisma_success_weight ? params.charisma_success_weight : 0; + + this.hacking_exp = params.hacking_exp ? params.hacking_exp : 0; + this.strength_exp = params.strength_exp ? params.strength_exp : 0; + this.defense_exp = params.defense_exp ? params.defense_exp : 0; + this.dexterity_exp = params.dexterity_exp ? params.dexterity_exp : 0; + this.agility_exp = params.agility_exp ? params.agility_exp : 0; + this.charisma_exp = params.charisma_exp ? params.charisma_exp : 0; + this.intelligence_exp = params.intelligence_exp ? params.intelligence_exp : 0; + + this.kills = params.kills ? params.kills : 0; + } + + commit(p: IPlayer, div: number=1, singParams: any=null): number { + if (div <= 0) { div = 1; } + p.startCrime( + this.type, + this.hacking_exp/div, + this.strength_exp/div, + this.defense_exp/div, + this.dexterity_exp/div, + this.agility_exp/div, + this.charisma_exp/div, + this.money/div, + this.time, + singParams + ); + + return this.time; + } + + successRate(p: IPlayer): number { + let chance: number = (this.hacking_success_weight * p.hacking_skill + + this.strength_success_weight * p.strength + + this.defense_success_weight * p.defense + + this.dexterity_success_weight * p.dexterity + + this.agility_success_weight * p.agility + + this.charisma_success_weight * p.charisma + + CONSTANTS.IntelligenceCrimeWeight * p.intelligence); + chance /= CONSTANTS.MaxSkillLevel; + chance /= this.difficulty; + chance *= p.crime_success_mult; + + return Math.min(chance, 1); + } + +} diff --git a/src/Crime/CrimeHelpers.js b/src/Crime/CrimeHelpers.js new file mode 100644 index 000000000..0a374ae4f --- /dev/null +++ b/src/Crime/CrimeHelpers.js @@ -0,0 +1,57 @@ +export function determineCrimeSuccess(type, moneyGained) { + var chance = 0; + var found = false; + for(const i in Crimes) { + const crime = Crimes[i]; + if(crime.type == type) { + chance = crime.successRate(Player); + found = true; + break; + } + } + + if(!found) { + console.log(crime); + dialogBoxCreate("ERR: Unrecognized crime type. This is probably a bug please contact the developer"); + return; + } + + if (Math.random() <= chance) { + //Success + Player.gainMoney(moneyGained); + return true; + } else { + //Failure + return false; + } +} + +export function findCrime(roughName) { + if (roughName.includes("shoplift")) { + return Crimes.Shoplift; + } else if (roughName.includes("rob") && roughName.includes("store")) { + return Crimes.RobStore; + } else if (roughName.includes("mug")) { + return Crimes.Mug; + } else if (roughName.includes("larceny")) { + return Crimes.Larceny; + } else if (roughName.includes("drugs")) { + return Crimes.DealDrugs; + } else if (roughName.includes("bond") && roughName.includes("forge")) { + return Crimes.BondForgery; + } else if (roughName.includes("traffick") && roughName.includes("arms")) { + return Crimes.TraffickArms; + } else if (roughName.includes("homicide")) { + return Crimes.Homicide; + } else if (roughName.includes("grand") && roughName.includes("auto")) { + return Crimes.GrandTheftAuto; + } else if (roughName.includes("kidnap")) { + return Crimes.Kidnap; + } else if (roughName.includes("assassinate")) { + return Crimes.Assassination; + } else if (roughName.includes("heist")) { + return Crimes.Heist; + } + + return null; +} diff --git a/src/Crime/Crimes.ts b/src/Crime/Crimes.ts new file mode 100644 index 000000000..bb8125127 --- /dev/null +++ b/src/Crime/Crimes.ts @@ -0,0 +1,163 @@ +import { Crime } from "./Crime"; + +import { CONSTANTS } from "../Constants"; +import { IMap } from "../types"; + +export const Crimes: IMap = { + Shoplift: new Crime("Shoplift", CONSTANTS.CrimeShoplift, 2e3, 15e3, 1/20, 0.1, { + dexterity_success_weight: 1, + agility_success_weight: 1, + + dexterity_exp: 2, + agility_exp: 2, + }), + + RobStore: new Crime("Rob Store", CONSTANTS.CrimeRobStore, 60e3, 400e3, 1/5, 0.5, { + hacking_exp: 30, + dexterity_exp: 45, + agility_exp: 45, + + hacking_success_weight: 0.5 , + dexterity_success_weight: 2, + agility_success_weight: 1, + + intelligence_exp: 0.25 * CONSTANTS.IntelligenceCrimeBaseExpGain, + }), + + Mug: new Crime("Mug", CONSTANTS.CrimeMug, 4e3, 36e3, 1/5, 0.25, { + strength_exp: 3, + defense_exp: 3, + dexterity_exp: 3, + agility_exp: 3, + + strength_success_weight: 1.5, + defense_success_weight: 0.5, + dexterity_success_weight: 1.5, + agility_success_weight: 0.5, + }), + + Larceny: new Crime("Larceny", CONSTANTS.CrimeLarceny, 90e3, 800e3, 1/3, 1.5, { + hacking_exp: 45, + dexterity_exp: 60, + agility_exp: 60, + + hacking_success_weight: 0.5, + dexterity_success_weight: 1, + agility_success_weight: 1, + + intelligence_exp: 0.5 * CONSTANTS.IntelligenceCrimeBaseExpGain, + }), + + DealDrugs: new Crime("Deal Drugs", CONSTANTS.CrimeDrugs, 10e3, 120e3, 1, 0.5, { + dexterity_exp: 5, + agility_exp: 5, + charisma_exp: 10, + + charisma_success_weight: 3, + dexterity_success_weight: 2, + agility_success_weight: 1, + }), + + BondForgery: new Crime("Bond Forgery", CONSTANTS.CrimeBondForgery, 300e3, 4.5e6, 1/2, 0.1, { + hacking_exp: 100, + dexterity_exp: 150, + charisma_exp: 15, + + hacking_success_weight: 0.05, + dexterity_success_weight: 1.25, + + intelligence_exp: 2 * CONSTANTS.IntelligenceCrimeBaseExpGain, + }), + + TraffickArms: new Crime("Traffick Arms", CONSTANTS.CrimeTraffickArms, 40e3, 600e3, 2, 1, { + strength_exp: 20, + defense_exp: 20, + dexterity_exp: 20, + agility_exp: 20, + charisma_exp: 40, + + charisma_success_weight: 1, + strength_success_weight: 1, + defense_success_weight: 1, + dexterity_success_weight: 1, + agility_success_weight: 1, + }), + + Homicide: new Crime("Homicide", CONSTANTS.CrimeHomicide, 3e3, 45e3, 1, 3, { + strength_exp: 2, + defense_exp: 2, + dexterity_exp: 2, + agility_exp: 2, + + strength_success_weight: 2, + defense_success_weight: 2, + dexterity_success_weight: 0.5, + agility_success_weight: 0.5, + + kills: 1, + }), + + GrandTheftAuto: new Crime("Grand Theft Auto", CONSTANTS.CrimeGrandTheftAuto, 80e3, 1.6e6, 8, 5, { + strength_exp: 20, + defense_exp: 20, + dexterity_exp: 20, + agility_exp: 80, + charisma_exp: 40, + + hacking_success_weight: 1, + strength_success_weight: 1, + dexterity_success_weight: 4, + agility_success_weight: 2, + charisma_success_weight: 2, + + intelligence_exp: CONSTANTS.IntelligenceCrimeBaseExpGain, + }), + + Kidnap: new Crime("Kidnap", CONSTANTS.CrimeKidnap, 120e3, 3.6e6, 5, 6, { + strength_exp: 80, + defense_exp: 80, + dexterity_exp: 80, + agility_exp: 80, + charisma_exp: 80, + + charisma_success_weight: 1, + strength_success_weight: 1, + dexterity_success_weight: 1, + agility_success_weight: 1, + + intelligence_exp: 2 * CONSTANTS.IntelligenceCrimeBaseExpGain, + }), + + Assassination: new Crime("Assassination", CONSTANTS.CrimeAssassination, 300e3, 12e6, 8, 10, { + strength_exp: 300, + defense_exp: 300, + dexterity_exp: 300, + agility_exp: 300, + + strength_success_weight: 1, + dexterity_success_weight: 2, + agility_success_weight: 1, + + intelligence_exp: 5 * CONSTANTS.IntelligenceCrimeBaseExpGain, + + kills: 1, + }), + + Heist: new Crime("Heist", CONSTANTS.CrimeHeist, 600e3, 120e6, 18, 15, { + hacking_exp: 450, + strength_exp: 450, + defense_exp: 450, + dexterity_exp: 450, + agility_exp: 450, + charisma_exp: 450, + + hacking_success_weight: 1, + strength_success_weight: 1, + defense_success_weight: 1, + dexterity_success_weight: 1, + agility_success_weight: 1, + charisma_success_weight: 1, + + intelligence_exp: 10 * CONSTANTS.IntelligenceCrimeBaseExpGain, + }), +}; diff --git a/src/Crimes.js b/src/Crimes.js deleted file mode 100644 index e4e636c1c..000000000 --- a/src/Crimes.js +++ /dev/null @@ -1,275 +0,0 @@ -import {CONSTANTS} from "./Constants"; -import {Player} from "./Player"; -import {dialogBoxCreate} from "../utils/DialogBox"; - - -function Crime(name, type, time, money, difficulty, karma, params) { - this.name = name; - this.type = type; - this.time = time; - this.money = money; - this.difficulty = difficulty; - this.karma = karma; - - this.hacking_success_weight = params.hacking_success_weight ? params.hacking_success_weight : 0; - this.strength_success_weight = params.strength_success_weight ? params.strength_success_weight : 0; - this.defense_success_weight = params.defense_success_weight ? params.defense_success_weight : 0; - this.dexterity_success_weight = params.dexterity_success_weight ? params.dexterity_success_weight : 0; - this.agility_success_weight = params.agility_success_weight ? params.agility_success_weight : 0; - this.charisma_success_weight = params.charisma_success_weight ? params.charisma_success_weight : 0; - - this.hacking_exp = params.hacking_exp ? params.hacking_exp : 0; - this.strength_exp = params.strength_exp ? params.strength_exp : 0; - this.defense_exp = params.defense_exp ? params.defense_exp : 0; - this.dexterity_exp = params.dexterity_exp ? params.dexterity_exp : 0; - this.agility_exp = params.agility_exp ? params.agility_exp : 0; - this.charisma_exp = params.charisma_exp ? params.charisma_exp : 0; - this.intelligence_exp = params.intelligence_exp ? params.intelligence_exp : 0; - - this.kills = params.kills ? params.kills : 0; -} - -Crime.prototype.commit = function(div=1, singParams=null) { - if (div <= 0) {div = 1;} - Player.crimeType = this.type; - Player.startCrime( - this.hacking_exp/div, - this.strength_exp/div, - this.defense_exp/div, - this.dexterity_exp/div, - this.agility_exp/div, - this.charisma_exp/div, - this.money/div, this.time, singParams); - return this.time; -} - -Crime.prototype.successRate = function() { - var chance = (this.hacking_success_weight * Player.hacking_skill + - this.strength_success_weight * Player.strength + - this.defense_success_weight * Player.defense + - this.dexterity_success_weight * Player.dexterity + - this.agility_success_weight * Player.agility + - this.charisma_success_weight * Player.charisma + - CONSTANTS.IntelligenceCrimeWeight * Player.intelligence); - chance /= CONSTANTS.MaxSkillLevel; - chance /= this.difficulty; - chance *= Player.crime_success_mult; - return Math.min(chance, 1); -} - -const Crimes = { - Shoplift: new Crime("Shoplift", CONSTANTS.CrimeShoplift, 2e3, 15e3, 1/20, 0.1, { - dexterity_success_weight: 1, - agility_success_weight: 1, - - dexterity_exp: 2, - agility_exp: 2, - }), - - RobStore: new Crime("Rob Store", CONSTANTS.CrimeRobStore, 60e3, 400e3, 1/5, 0.5, { - hacking_exp: 30, - dexterity_exp: 45, - agility_exp: 45, - - hacking_success_weight: 0.5 , - dexterity_success_weight: 2, - agility_success_weight: 1, - - intelligence_exp: 0.25 * CONSTANTS.IntelligenceCrimeBaseExpGain, - }), - - Mug: new Crime("Mug", CONSTANTS.CrimeMug, 4e3, 36e3, 1/5, 0.25, { - strength_exp: 3, - defense_exp: 3, - dexterity_exp: 3, - agility_exp: 3, - - strength_success_weight: 1.5, - defense_success_weight: 0.5, - dexterity_success_weight: 1.5, - agility_success_weight: 0.5, - }), - - Larceny: new Crime("Larceny", CONSTANTS.CrimeLarceny, 90e3, 800e3, 1/3, 1.5, { - hacking_exp: 45, - dexterity_exp: 60, - agility_exp: 60, - - hacking_success_weight: 0.5, - dexterity_success_weight: 1, - agility_success_weight: 1, - - intelligence_exp: 0.5 * CONSTANTS.IntelligenceCrimeBaseExpGain, - }), - - DealDrugs: new Crime("Deal Drugs", CONSTANTS.CrimeDrugs, 10e3, 120e3, 1, 0.5, { - dexterity_exp: 5, - agility_exp: 5, - charisma_exp: 10, - - charisma_success_weight: 3, - dexterity_success_weight: 2, - agility_success_weight: 1, - }), - - BondForgery: new Crime("Bond Forgery", CONSTANTS.CrimeBondForgery, 300e3, 4.5e6, 1/2, 0.1, { - hacking_exp: 100, - dexterity_exp: 150, - charisma_exp: 15, - - hacking_success_weight: 0.05, - dexterity_success_weight: 1.25, - - intelligence_exp: 2 * CONSTANTS.IntelligenceCrimeBaseExpGain, - }), - - TraffickArms: new Crime("Traffick Arms", CONSTANTS.CrimeTraffickArms, 40e3, 600e3, 2, 1, { - strength_exp: 20, - defense_exp: 20, - dexterity_exp: 20, - agility_exp: 20, - charisma_exp: 40, - - charisma_success_weight: 1, - strength_success_weight: 1, - defense_success_weight: 1, - dexterity_success_weight: 1, - agility_success_weight: 1, - }), - - Homicide: new Crime("Homicide", CONSTANTS.CrimeHomicide, 3e3, 45e3, 1, 3, { - strength_exp: 2, - defense_exp: 2, - dexterity_exp: 2, - agility_exp: 2, - - strength_success_weight: 2, - defense_success_weight: 2, - dexterity_success_weight: 0.5, - agility_success_weight: 0.5, - - kills: 1, - }), - - GrandTheftAuto: new Crime("Grand Theft Auto", CONSTANTS.CrimeGrandTheftAuto, 80e3, 1.6e6, 8, 5, { - strength_exp: 20, - defense_exp: 20, - dexterity_exp: 20, - agility_exp: 80, - charisma_exp: 40, - - hacking_success_weight: 1, - strength_success_weight: 1, - dexterity_success_weight: 4, - agility_success_weight: 2, - charisma_success_weight: 2, - - intelligence_exp: CONSTANTS.IntelligenceCrimeBaseExpGain, - }), - - Kidnap: new Crime("Kidnap", CONSTANTS.CrimeKidnap, 120e3, 3.6e6, 5, 6, { - strength_exp: 80, - defense_exp: 80, - dexterity_exp: 80, - agility_exp: 80, - charisma_exp: 80, - - charisma_success_weight: 1, - strength_success_weight: 1, - dexterity_success_weight: 1, - agility_success_weight: 1, - - intelligence_exp: 2 * CONSTANTS.IntelligenceCrimeBaseExpGain, - }), - - Assassination: new Crime("Assassination", CONSTANTS.CrimeAssassination, 300e3, 12e6, 8, 10, { - strength_exp: 300, - defense_exp: 300, - dexterity_exp: 300, - agility_exp: 300, - - strength_success_weight: 1, - dexterity_success_weight: 2, - agility_success_weight: 1, - - intelligence_exp: 5 * CONSTANTS.IntelligenceCrimeBaseExpGain, - - kills: 1, - }), - - Heist: new Crime("Heist", CONSTANTS.CrimeHeist, 600e3, 120e6, 18, 15, { - hacking_exp: 450, - strength_exp: 450, - defense_exp: 450, - dexterity_exp: 450, - agility_exp: 450, - charisma_exp: 450, - - hacking_success_weight: 1, - strength_success_weight: 1, - defense_success_weight: 1, - dexterity_success_weight: 1, - agility_success_weight: 1, - charisma_success_weight: 1, - - intelligence_exp: 10 * CONSTANTS.IntelligenceCrimeBaseExpGain, - }), -}; - -function determineCrimeSuccess(type, moneyGained) { - var chance = 0; - var found = false; - for(const i in Crimes) { - const crime = Crimes[i]; - if(crime.type == type) { - chance = crime.successRate(); - found = true; - break; - } - } - if(!found) { - console.log(crime); - dialogBoxCreate("ERR: Unrecognized crime type. This is probably a bug please contact the developer"); - return; - } - - if (Math.random() <= chance) { - //Success - Player.gainMoney(moneyGained); - return true; - } else { - //Failure - return false; - } -} - -function findCrime(roughName) { - if (roughName.includes("shoplift")) { - return Crimes.Shoplift; - } else if (roughName.includes("rob") && roughName.includes("store")) { - return Crimes.RobStore; - } else if (roughName.includes("mug")) { - return Crimes.Mug; - } else if (roughName.includes("larceny")) { - return Crimes.Larceny; - } else if (roughName.includes("drugs")) { - return Crimes.DealDrugs; - } else if (roughName.includes("bond") && roughName.includes("forge")) { - return Crimes.BondForgery; - } else if (roughName.includes("traffick") && roughName.includes("arms")) { - return Crimes.TraffickArms; - } else if (roughName.includes("homicide")) { - return Crimes.Homicide; - } else if (roughName.includes("grand") && roughName.includes("auto")) { - return Crimes.GrandTheftAuto; - } else if (roughName.includes("kidnap")) { - return Crimes.Kidnap; - } else if (roughName.includes("assassinate")) { - return Crimes.Assassination; - } else if (roughName.includes("heist")) { - return Crimes.Heist; - } - return null; -} - -export {determineCrimeSuccess,findCrime,Crimes}; diff --git a/src/Location.js b/src/Location.js index cb6084610..c1b15cc57 100644 --- a/src/Location.js +++ b/src/Location.js @@ -5,7 +5,7 @@ import {getJobRequirementText} from "./Company/GetJobRequiremen import * as posNames from "./Company/data/CompanyPositionNames"; import { Corporation } from "./Corporation/Corporation"; import {CONSTANTS} from "./Constants"; -import {Crimes} from "./Crimes"; +import { Crimes } from "./Crime/Crimes"; import {Engine} from "./engine"; import {beginInfiltration} from "./Infiltration"; import {hasBladeburnerSF} from "./NetscriptFunctions"; @@ -240,7 +240,7 @@ function displayLocationContent() { //Check if the player is employed at this Location. If he is, display the "Work" button, //update the job title, etc. - if (loc != "" && loc === Player.companyName) { + if (loc != "" && Object.keys(Player.jobs).includes(loc)) { let company = Companies[loc]; jobTitle.style.display = "block"; @@ -249,7 +249,7 @@ function displayLocationContent() { locationTxtDiv1.style.display = "block"; locationTxtDiv2.style.display = "block"; locationTxtDiv3.style.display = "block"; - jobTitle.innerHTML = "Job Title: " + Player.companyPosition; + jobTitle.innerHTML = `Job Title: ${Player.jobs[loc]}`; let repGain = company.getFavorGain(); if (repGain.length != 2) {repGain = 0;} repGain = repGain[0]; @@ -264,7 +264,7 @@ function displayLocationContent() { "favor you gain depends on how much reputation you have with the company"; work.style.display = "block"; - let currPos = CompanyPositions[Player.companyPosition]; + let currPos = CompanyPositions[Player.jobs[loc]]; if (currPos == null) { throw new Error("Player's companyPosition property has an invalid value"); } @@ -1043,8 +1043,8 @@ function displayLocationContent() { // Make the "Apply to be Employee and Waiter" texts disappear if you already hold the job // Includes part-time stuff - if (loc == Player.companyName) { - var currPos = Player.companyPosition; + if (Object.keys(Player.jobs).includes(loc)) { + var currPos = Player.jobs[loc]; if (currPos == "Employee") { employeeJob.style.display = "none"; @@ -1874,73 +1874,73 @@ function initLocationButtons() { slumsShoplift.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.Shoplift.commit(); + Crimes.Shoplift.commit(Player); return false; }); slumsRobStore.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.RobStore.commit(); + Crimes.RobStore.commit(Player); return false; }); slumsMug.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.Mug.commit(); + Crimes.Mug.commit(Player); return false; }); slumsLarceny.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.Larceny.commit(); + Crimes.Larceny.commit(Player); return false; }); slumsDealDrugs.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.DealDrugs.commit(); + Crimes.DealDrugs.commit(Player); return false; }); slumsBondForgery.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.BondForgery.commit(); + Crimes.BondForgery.commit(Player); return false; }); slumsTrafficArms.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.TraffickArms.commit(); + Crimes.TraffickArms.commit(Player); return false; }); slumsHomicide.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.Homicide.commit(); + Crimes.Homicide.commit(Player); return false; }); slumsGta.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.GrandTheftAuto.commit(); + Crimes.GrandTheftAuto.commit(Player); return false; }); slumsKidnap.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.Kidnap.commit(); + Crimes.Kidnap.commit(Player); return false; }); slumsAssassinate.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.Assassination.commit(); + Crimes.Assassination.commit(Player); return false; }); slumsHeist.addEventListener("click", function(e) { if (!e.isTrusted) {return false;} - Crimes.Heist.commit(); + Crimes.Heist.commit(Player); return false; }); diff --git a/src/NetscriptFunctions.js b/src/NetscriptFunctions.js index 6f90ddde0..67e17e482 100644 --- a/src/NetscriptFunctions.js +++ b/src/NetscriptFunctions.js @@ -6,7 +6,7 @@ import {Augmentations, Augmentation, augmentationExists, installAugmentations, AugmentationNames} from "./Augmentations"; import {BitNodeMultipliers} from "./BitNodeMultipliers"; -import {determineCrimeSuccess, findCrime} from "./Crimes"; +import { determineCrimeSuccess, findCrime } from "./Crime/CrimeHelpers"; import {Bladeburner} from "./Bladeburner"; import {Company} from "./Company/Company"; import {Companies, companyExists} from "./Company/Companies"; @@ -2902,16 +2902,12 @@ function NetscriptFunctions(workerScript) { } } - var companyPositionTitle = ""; - if (CompanyPositions[Player.companyPosition] instanceof CompanyPosition) { - companyPositionTitle = Player.companyPosition; - } return { bitnode: Player.bitNodeN, city: Player.city, - company: Player.companyName, factions: Player.factions.slice(), - jobTitle: companyPositionTitle, + jobs: Object.keys(Player.jobs), + jobTitles: Object.values(Player.jobs), mult: { agility: Player.agility_mult, agilityExp: Player.agility_exp_mult, @@ -3030,7 +3026,7 @@ function NetscriptFunctions(workerScript) { return Player.getUpgradeHomeRamCost(); }, - workForCompany : function() { + workForCompany : function(companyName) { var ramCost = CONSTANTS.ScriptSingularityFn2RamCost; if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;} if (workerScript.checkingRam) { @@ -3044,13 +3040,33 @@ function NetscriptFunctions(workerScript) { } } - if (inMission) { - workerScript.scriptRef.log("ERROR: workForCompany() failed because you are in the middle of a mission."); - return; + // Sanitize input + if (companyName == null) { + companyName = Player.companyName; } - const companyPosition = CompanyPositions[Player.companyPosition]; - if (Player.companyPosition === "" || !(companyPosition instanceof CompanyPosition)) { + // Make sure its a valid company + if (companyName == null || companyName === "" || !(Companies[companyName] instanceof Company)) { + workerScript.scriptRef.log(`ERROR: workForCompany() failed because of an invalid company specified: ${companyName}`); + return false; + } + + // Make sure player is actually employed at the comapny + if (!Object.keys(Player.jobs).includes(companyName)) { + workerScript.scriptRef.log(`ERROR: workForCompany() failed because you do not have a job at ${companyName}`); + return false; + } + + // Cant work while in a mission + if (inMission) { + workerScript.scriptRef.log("ERROR: workForCompany() failed because you are in the middle of a mission."); + return false; + } + + // Check to make sure company position data is valid + const companyPositionName = Player.jobs[companyName]; + const companyPosition = CompanyPositions[companyPositionName]; + if (companyPositionName === "" || !(companyPosition instanceof CompanyPosition)) { workerScript.scriptRef.log("ERROR: workForCompany() failed because you do not have a job"); return false; } @@ -3062,13 +3078,14 @@ function NetscriptFunctions(workerScript) { } } + Player.companyName = companyName; if (companyPosition.isPartTimeJob()) { Player.startWorkPartTime(); } else { Player.startWork(); } if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.workForCompany == null) { - workerScript.log(`Began working at ${Player.companyName} as a ${Player.companyPosition}`); + workerScript.log(`Began working at ${Player.companyName} as a ${companyPositionName}`); } return true; }, @@ -3144,7 +3161,7 @@ function NetscriptFunctions(workerScript) { } if (res) { if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.applyToCompany == null) { - workerScript.log(`You were offered a new job at ${companyName} as a ${Player.companyPosition}`); + workerScript.log(`You were offered a new job at ${companyName} as a ${Player.jobs[companyName]}`); } } else { if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.applyToCompany == null) { @@ -3582,7 +3599,7 @@ function NetscriptFunctions(workerScript) { if(workerScript.disableLogs.ALL == null && workerScript.disableLogs.commitCrime == null) { workerScript.scriptRef.log("Attempting to commit crime: "+crime.name+"..."); } - return crime.commit(1, {workerscript: workerScript}); + return crime.commit(Player, 1, {workerscript: workerScript}); }, getCrimeChance : function(crimeRoughName) { var ramCost = CONSTANTS.ScriptSingularityFn3RamCost; diff --git a/src/PersonObjects/Person.ts b/src/PersonObjects/Person.ts index 527ade7be..45d2250c7 100644 --- a/src/PersonObjects/Person.ts +++ b/src/PersonObjects/Person.ts @@ -2,6 +2,7 @@ import { BitNodeMultipliers } from "../BitNodeMultipliers"; import { Cities } from "../Locations/Cities"; import { CONSTANTS } from "../Constants"; +import { IMap } from "../types"; // Interface for an object that represents the player (PlayerObject) // Used because at the time of implementation, the PlayerObject @@ -11,7 +12,19 @@ import { CONSTANTS } from "../Constants"; export interface IPlayer { companyName: string; factions: string[]; + jobs: IMap; money: any; + + hacking_skill: number; + strength: number; + defense: number; + dexterity: number; + agility: number; + charisma: number; + intelligence: number; + + crime_success_mult: number; + gainHackingExp(exp: number): void; gainStrengthExp(exp: number): void; gainDefenseExp(exp: number): void; @@ -20,35 +33,16 @@ export interface IPlayer { gainCharismaExp(exp: number): void; gainMoney(money: number): void; loseMoney(money: number): void; -} - -// Interface for a Crime object -// Used because at the time of implementation, the Crime object has not been converted -// to Typescript -export interface ICrime { - name: string; - type: string; - time: number; - money: number; - difficulty: number; - karma: number; - - hacking_success_weight: number; - strength_success_weight: number; - defense_success_weight: number; - dexterity_success_weight: number; - agility_success_weight: number; - charisma_success_weight: number; - - hacking_exp: number; - strength_exp: number; - defense_exp: number; - dexterity_exp: number; - agility_exp: number; - charisma_exp: number; - intelligence_exp: number; - - kills: number; + startCrime(crimeType: string, + hackExp: number, + strExp: number, + defExp: number, + dexExp: number, + agiExp: number, + chaExp: number, + money: number, + time: number, + singParams: any): void; } // Interface that defines a generic object used to track experience/money @@ -79,102 +73,60 @@ export abstract class Person { /** * Stats */ - hacking_skill: number; - strength: number; - defense: number; - dexterity: number; - agility: number; - charisma: number; - hp: number; - max_hp: number; + hacking_skill: number = 1; + strength: number = 1; + defense: number = 1; + dexterity: number = 1; + agility: number = 1; + charisma: number = 1; + hp: number = 10; + max_hp: number = 10; /** * Multipliers */ - hacking_exp: number; - strength_exp: number; - defense_exp: number; - dexterity_exp: number; - agility_exp: number; - charisma_exp: number; - intelligence_exp: number; + hacking_exp: number = 0; + strength_exp: number = 0; + defense_exp: number = 0; + dexterity_exp: number = 0; + agility_exp: number = 0; + charisma_exp: number = 0; + intelligence_exp: number = 0; - hacking_mult: number; - strength_mult: number; - defense_mult: number; - dexterity_mult: number; - agility_mult: number; - charisma_mult: number; + hacking_mult: number = 1; + strength_mult: number = 1; + defense_mult: number = 1; + dexterity_mult: number = 1; + agility_mult: number = 1; + charisma_mult: number = 1; - hacking_exp_mult: number; - strength_exp_mult: number; - defense_exp_mult: number; - dexterity_exp_mult: number; - agility_exp_mult: number; - charisma_exp_mult: number; + hacking_exp_mult: number = 1; + strength_exp_mult: number = 1; + defense_exp_mult: number = 1; + dexterity_exp_mult: number = 1; + agility_exp_mult: number = 1; + charisma_exp_mult: number = 1; - company_rep_mult: number; - faction_rep_mult: number; + company_rep_mult: number = 1; + faction_rep_mult: number = 1; - crime_money_mult: number; - crime_success_mult: number; + crime_money_mult: number = 1; + crime_success_mult: number = 1; - work_money_mult: number; + work_money_mult: number = 1; /** * Augmentations */ - this.augmentations = []; - this.queuedAugmentations = []; + augmentations: string[] = []; + queuedAugmentations: string[] = []; /** * City that the person is in */ - city: string; + city: string = Cities.Sector12; - constructor() { - this.hacking_skill = 1; - this.strength = 1; - this.defense = 1; - this.dexterity = 1; - this.agility = 1; - this.charisma = 1; - this.hp = 10; - this.max_hp = 10; - - // Multipliers - this.hacking_exp = 0; - this.strength_exp = 0; - this.defense_exp = 0; - this.dexterity_exp = 0; - this.agility_exp = 0; - this.charisma_exp = 0; - this.intelligence_exp = 0; - - this.hacking_mult = 1; - this.strength_mult = 1; - this.defense_mult = 1; - this.dexterity_mult = 1; - this.agility_mult = 1; - this.charisma_mult = 1; - - this.hacking_exp_mult = 1; - this.strength_exp_mult = 1; - this.defense_exp_mult = 1; - this.dexterity_exp_mult = 1; - this.agility_exp_mult = 1; - this.charisma_exp_mult = 1; - - this.company_rep_mult = 1; - this.faction_rep_mult = 1; - - this.crime_money_mult = 1; - this.crime_success_mult = 1; - - this.work_money_mult = 1; - - this.city = Cities.Sector12; - } + constructor() {} /** * Given an experience amount and stat multiplier, calculates the diff --git a/src/PersonObjects/Sleeve/Sleeve.ts b/src/PersonObjects/Sleeve/Sleeve.ts index 9d2daf79c..763f46c78 100644 --- a/src/PersonObjects/Sleeve/Sleeve.ts +++ b/src/PersonObjects/Sleeve/Sleeve.ts @@ -10,18 +10,26 @@ import { SleeveTaskType } from "./SleeveTaskTypesEnum"; import { Person, IPlayer, - ICrime, ITaskTracker, createTaskTracker } from "../Person"; import { BitNodeMultipliers } from "../../BitNodeMultipliers"; + +import { Crime } from "../../Crime/Crime"; + import { Cities } from "../../Locations/Cities"; + import { Companies } from "../../Company/Companies"; import { Company } from "../../Company/Company"; +import { CompanyPosition } from "../../Company/CompanyPosition"; +import { CompanyPositions } from "../../Company/CompanyPositions"; + import { CONSTANTS } from "../../Constants"; + import { Faction } from "../../Faction/Faction"; import { Factions } from "../../Faction/Factions"; import { FactionWorkType } from "../../Faction/FactionWorkTypeEnum"; + import { Locations } from "../../Locations"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../../utils/JSONReviver"; @@ -45,8 +53,11 @@ export class Sleeve extends Person { currentTaskDescription: string = ""; /** - * For what company/faction the current task is assigned to. - * Only applicable when working for faction or company, obviously + * Contains details about the sleeve's current task. The info stored + * in this depends on the task type + * + * Faction/Company Work: Name of Faction/Company + * Crime: Success rate of current crime, in decimal form */ currentTaskLocation: string = ""; @@ -119,29 +130,14 @@ export class Sleeve extends Person { constructor() { super(); - /* - this.currentTask = SleeveTaskType.Idle; - this.currentTaskDescription = ""; - this.currentTaskTime = 0; - this.currentTaskMaxTime = 0; - this.earningsForSleeves = createTaskTracker(); - this.earningsForPlayer = createTaskTracker(); - this.earningsForTask = createTaskTracker(); - this.gainRatesForTask = createTaskTracker(); - this.logs = []; - this.memory = 0; - this.shock = 1; - this.storedCycles = 0; - this.sync = 1; - */ } /** * Commit crimes */ - commitCrime(p: IPlayer, crime: ICrime): void { + commitCrime(p: IPlayer, crime: Crime): void { if (this.currentTask !== SleeveTaskType.Idle) { - this.finishTask(); + this.finishTask(p); } else { this.resetTaskStatus(); } @@ -152,19 +148,38 @@ export class Sleeve extends Person { this.gainRatesForTask.dex = crime.dexterity_exp * this.dexterity_exp_mult * BitNodeMultipliers.CrimeExpGain; this.gainRatesForTask.agi = crime.agility_exp * this.agility_exp_mult * BitNodeMultipliers.CrimeExpGain; this.gainRatesForTask.cha = crime.charisma_exp * this.charisma_exp_mult * BitNodeMultipliers.CrimeExpGain; + this.gainRatesForTask.money = crime.money * this.crime_money_mult * BitNodeMultipliers.CrimeMoney; + + // We'll determine success now and adjust the earnings accordingly + if (Math.random() < crime.successRate(p)) { + this.gainRatesForTask.hack *= 2; + this.gainRatesForTask.str *= 2; + this.gainRatesForTask.def *= 2; + this.gainRatesForTask.dex *= 2; + this.gainRatesForTask.agi *= 2; + this.gainRatesForTask.cha *= 2; + } else { + this.gainRatesForTask.money = 0; + } this.currentTaskMaxTime = crime.time; - this.currentTask = SleeveTaskType.Crime; } /** * Called to stop the current task */ - finishTask(): void { + finishTask(p: IPlayer): void { if (this.currentTask === SleeveTaskType.Crime) { - } else { + // For crimes, all experience and money is gained at the end + if (this.currentTaskTime >= this.currentTaskMaxTime) { + let retValue: ITaskTracker = createTaskTracker(); // Amount of exp to be gained by other sleeves + retValue = this.gainExperience(p, this.gainRatesForTask); + this.gainMoney(p, this.gainRatesForTask); + } + } else { + // For other crimes... I dont think anything else needs to be done } this.resetTaskStatus(); @@ -336,14 +351,19 @@ export class Sleeve extends Person { break; } - const repGainPerCycle: number = this.getRepGain(); - fac.playerReputation += (repGainPerCycle * cyclesUsed); + fac.playerReputation += (this.getRepGain() * cyclesUsed); break; case SleeveTaskType.Company: retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed); this.gainMoney(p, this.gainRatesForTask, cyclesUsed); - // TODO Rep gain for this + const company: Company = Companies[this.currentTaskLocation]; + if (!(company instanceof Company)) { + console.error(`Invalid company for Sleeve task: ${this.currentTaskLocation}`); + break; + } + + company.playerReputation *= (this.getRepGain() * cyclesUsed); break; case SleeveTaskType.Recovery: this.shock = Math.max(100, this.shock + (0.001 * this.storedCycles)); @@ -356,9 +376,11 @@ export class Sleeve extends Person { } if (this.currentTaskMaxTime !== 0 && this.currentTaskTime >= this.currentTaskMaxTime) { - this.finishTask(); + this.finishTask(p); } + this.updateStatLevels(); + this.storedCycles -= cyclesUsed; // TODO Finish this @@ -382,7 +404,7 @@ export class Sleeve extends Person { */ takeUniversityCourse(p: IPlayer, universityName: string, className: string): boolean { if (this.currentTask !== SleeveTaskType.Idle) { - this.finishTask(); + this.finishTask(p); } else { this.resetTaskStatus(); } @@ -471,22 +493,68 @@ export class Sleeve extends Person { } /** - * Work for a company + * Start work for one of the player's companies + * Returns boolean indicating success */ - workForCompany(p: IPlayer): boolean { - return true; - } - - /** - * Work for one of the player's factions - */ - workForFaction(p: IPlayer, factionName: string, workType: string): boolean { - if (!(Factions[factionName] instanceof Faction) || !p.factions.includes(factionName)) { + workForCompany(p: IPlayer, companyName: string): boolean { + if (!(Companies[companyName] instanceof Company) || p.jobs[companyName] == null) { return false; } if (this.currentTask !== SleeveTaskType.Idle) { - this.finishTask(); + this.finishTask(p); + } else { + this.resetTaskStatus(); + } + + const company: Company | null = Companies[companyName]; + const companyPosition: CompanyPosition | null = CompanyPositions[p.jobs[companyName]]; + if (company == null) { throw new Error(`Invalid company name specified in Sleeve.workForCompany(): ${companyName}`); } + if (companyPosition == null) { throw new Error(`Invalid CompanyPosition data in Sleeve.workForCompany(): ${companyName}`); } + + this.gainRatesForTask.hack = companyPosition.hackingExpGain * + company.expMultiplier * + this.hacking_exp_mult * + BitNodeMultipliers.FactionWorkExpGain; + this.gainRatesForTask.str = companyPosition.strengthExpGain * + company.expMultiplier * + this.strength_exp_mult * + BitNodeMultipliers.FactionWorkExpGain; + this.gainRatesForTask.def = companyPosition.defenseExpGain * + company.expMultiplier * + this.defense_exp_mult * + BitNodeMultipliers.FactionWorkExpGain; + this.gainRatesForTask.dex = companyPosition.dexterityExpGain * + company.expMultiplier * + this.dexterity_exp_mult * + BitNodeMultipliers.FactionWorkExpGain; + this.gainRatesForTask.agi = companyPosition.agilityExpGain * + company.expMultiplier * + this.agility_exp_mult * + BitNodeMultipliers.FactionWorkExpGain; + this.gainRatesForTask.cha = companyPosition.charismaExpGain * + company.expMultiplier * + this.charisma_exp_mult * + BitNodeMultipliers.FactionWorkExpGain; + + this.currentTaskLocation = companyName; + this.currentTask = SleeveTaskType.Company; + + return true; + } + + /** + * Start work for one of the player's factions + * Returns boolean indicating success + */ + workForFaction(p: IPlayer, factionName: string, workType: string): boolean { + if (!(Factions[factionName] instanceof Faction) || !p.factions.includes(factionName)) { + throw new Error(`Invalid Faction specified for Sleeve.workForFaction(): ${factionName}`); + return false; + } + + if (this.currentTask !== SleeveTaskType.Idle) { + this.finishTask(p); } else { this.resetTaskStatus(); } @@ -526,7 +594,7 @@ export class Sleeve extends Person { */ workoutAtGym(p: IPlayer, gymName: string, stat: string): boolean { if (this.currentTask !== SleeveTaskType.Idle) { - this.finishTask(); + this.finishTask(p); } else { this.resetTaskStatus(); } diff --git a/src/PersonObjects/Sleeve/SleeveUI.ts b/src/PersonObjects/Sleeve/SleeveUI.ts index e048e01b3..beef9b14e 100644 --- a/src/PersonObjects/Sleeve/SleeveUI.ts +++ b/src/PersonObjects/Sleeve/SleeveUI.ts @@ -4,31 +4,45 @@ import { Sleeve } from "./Sleeve"; import { SleeveTaskType } from "./SleeveTaskTypesEnum"; +import { IPlayer } from "../Person"; + +import { Locations } from "../../Locations"; + +import { Cities } from "../../Locations/Cities"; +import { Crimes } from "../../Crime/Crimes"; + import { IMap } from "../../types"; +import { numeralWrapper } from "../../ui/numeralFormat"; import { Page, routing } from "../../ui/navigationTracking"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; + import { exceptionAlert } from "../../../utils/helpers/exceptionAlert"; import { createElement } from "../../../utils/uiHelpers/createElement"; import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement"; +import { getSelectValue } from "../../../utils/uiHelpers/getSelectData"; +import { removeChildrenFromElement } from "../../../utils/uiHelpers/removeChildrenFromElement"; import { removeElement } from "../../../utils/uiHelpers/removeElement"; import { removeElementById } from "../../../utils/uiHelpers/removeElementById"; // Object that keeps track of all DOM elements for the UI for a single Sleeve interface ISleeveUIElems { - container: Element | null, - statsPanel: Element | null, - stats: Element | null, - statsTooltip: Element | null, - taskPanel: Element | null, - taskSelector: Element | null, - taskDetailsSelector: Element | null, - taskDescription: Element | null, - earningsPanel: Element | null, - currentEarningsInfo: Element | null, - totalEarningsInfo: Element | null, + container: HTMLElement | null, + statsPanel: HTMLElement | null, + stats: HTMLElement | null, + moreStatsButton: HTMLElement | null, + taskPanel: HTMLElement | null, + taskSelector: HTMLSelectElement | null, + taskDetailsSelector: HTMLSelectElement | null, + taskDetailsSelector2: HTMLSelectElement | null, + taskDescription: HTMLElement | null, + taskSetButton: HTMLElement | null, + earningsPanel: HTMLElement | null, + currentEarningsInfo: HTMLElement | null, + totalEarningsButton: HTMLElement | null, } // Object that keeps track of all DOM elements for the entire Sleeve UI @@ -47,15 +61,18 @@ const UIElems: IPageUIElems = { } // Interface for Player object -interface IPlayer { +interface ISleeveUiPlayer extends IPlayer { sleeves: Sleeve[]; } // Creates the UI for the entire Sleeves page -export function createSleevesPage(p: IPlayer) { +let playerRef: ISleeveUiPlayer | null; +export function createSleevesPage(p: ISleeveUiPlayer) { if (!routing.isOn(Page.Sleeves)) { return; } try { + playerRef = p; + UIElems.container = createElement("div", { class: "generic-menupage-container", id: "sleeves-container", @@ -73,8 +90,11 @@ export function createSleevesPage(p: IPlayer) { UIElems.sleeveList = createElement("ul"); UIElems.sleeves = []; + // Create UI modules for all Sleeve for (const sleeve of p.sleeves) { - UIElems.sleeves.push(this.createSleeveUi(sleeve, p.sleeves)); + const sleeveUi = createSleeveUi(sleeve, p.sleeves); + UIElems.sleeveList.appendChild(sleeveUi.container!); + UIElems.sleeves.push(sleeveUi); } UIElems.container.appendChild(UIElems.info); @@ -94,29 +114,33 @@ export function updateSleevesPage() { export function clearSleevesPage() { removeElement(UIElems.container); for (const prop in UIElems) { - UIElems[prop] = null; + (UIElems)[prop] = null; } + + playerRef = null; } // Creates the UI for a single Sleeve // Returns an object containing the DOM elements in the UI (ISleeveUIElems) -function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]) { - if (!routing.isOn(Page.Sleeves)) { return; } - +function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems { const elems: ISleeveUIElems = { container: null, statsPanel: null, stats: null, - statsTooltip: null, + moreStatsButton: null, taskPanel: null, taskSelector: null, taskDetailsSelector: null, + taskDetailsSelector2: null, taskDescription: null, + taskSetButton: null, earningsPanel: null, currentEarningsInfo: null, totalEarningsButton: null, } + if (!routing.isOn(Page.Sleeves)) { return elems; } + elems.container = createElement("div", { class: "sleeve-container", display: "block", @@ -124,12 +148,46 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]) { elems.statsPanel = createElement("div", { class: "sleeve-panel" }); elems.stats = createElement("p", { class: "sleeve-stats-text tooltip" }); - elems.statsTooltip = createElement("span", { class: "tooltiptext" }); - elems.stats.appendChild(elems.statsTooltip); + elems.moreStatsButton = createElement("button", { + class: "std-button", + innerText: "More Stats", + clickListener: () => { + dialogBoxCreate( + [ + "

Stats:

", + `Hacking: ${sleeve.hacking_skill} (${numeralWrapper.formatBigNumber(sleeve.hacking_exp)} exp)`, + `Strength: ${sleeve.strength} (${numeralWrapper.formatBigNumber(sleeve.strength_exp)} exp)`, + `Defense: ${sleeve.defense} (${numeralWrapper.formatBigNumber(sleeve.defense_exp)} exp)`, + `Dexterity: ${sleeve.dexterity} (${numeralWrapper.formatBigNumber(sleeve.dexterity_exp)} exp)`, + `Agility: ${sleeve.agility} (${numeralWrapper.formatBigNumber(sleeve.agility_exp)} exp)`, + `Charisma: ${sleeve.charisma} (${numeralWrapper.formatBigNumber(sleeve.charisma_exp)} exp)
`, + "

Multipliers:

", + `Hacking Level multiplier: ${numeralWrapper.formatPercentage(sleeve.hacking_mult)}`, + `Hacking Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.hacking_exp_mult)}`, + `Strength Level multiplier: ${numeralWrapper.formatPercentage(sleeve.strength_mult)}`, + `Strength Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.strength_exp_mult)}`, + `Defense Level multiplier: ${numeralWrapper.formatPercentage(sleeve.defense_mult)}`, + `Defense Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.defense_exp_mult)}`, + `Dexterity Level multiplier: ${numeralWrapper.formatPercentage(sleeve.dexterity_mult)}`, + `Dexterity Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.dexterity_exp_mult)}`, + `Agility Level multiplier: ${numeralWrapper.formatPercentage(sleeve.agility_mult)}`, + `Agility Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.agility_exp_mult)}`, + `Charisma Level multiplier: ${numeralWrapper.formatPercentage(sleeve.charisma_mult)}`, + `Charisma Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.charisma_exp_mult)}`, + `Faction Reputation Gain multiplier: ${numeralWrapper.formatPercentage(sleeve.faction_rep_mult)}`, + `Company Reputation Gain multiplier: ${numeralWrapper.formatPercentage(sleeve.company_rep_mult)}`, + `Salary multiplier: ${numeralWrapper.formatPercentage(sleeve.work_money_mult)}`, + `Crime Money multiplier: ${numeralWrapper.formatPercentage(sleeve.crime_money_mult)}`, + `Crime Success multiplier: ${numeralWrapper.formatPercentage(sleeve.crime_success_mult)}`, + ].join("
"), false + ); + } + }); elems.statsPanel.appendChild(elems.stats); + elems.statsPanel.appendChild(elems.moreStatsButton); elems.taskPanel = createElement("div", { class: "sleeve-panel" }); - elems.taskSelector = createElement("select"); + elems.taskSelector = createElement("select") as HTMLSelectElement; elems.taskSelector.add(createOptionElement("------")); elems.taskSelector.add(createOptionElement("Work for Company")); elems.taskSelector.add(createOptionElement("Work for Faction")); @@ -142,25 +200,290 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]) { updateSleeveTaskSelector(sleeve, elems, allSleeves); }); // TODO Set initial value for task selector - elems.taskDetailsSelector = createElement("select"); + elems.taskDetailsSelector = createElement("select") as HTMLSelectElement; + elems.taskDetailsSelector2 = createElement("select") as HTMLSelectElement; elems.taskDescription = createElement("p"); + elems.taskSetButton = createElement("button", { + class: "std-button", + innerText: "Set Task", + clickListener: () => { + setSleeveTask(sleeve, elems); + } + }); elems.taskPanel.appendChild(elems.taskSelector); elems.taskPanel.appendChild(elems.taskDetailsSelector); + elems.taskPanel.appendChild(elems.taskSetButton); elems.taskPanel.appendChild(elems.taskDescription); elems.earningsPanel = createElement("div", { class: "sleeve-panel" }); elems.currentEarningsInfo = createElement("p"); - elems.totalEarningsButton = createElement("button", { class: "std-button" }); + elems.totalEarningsButton = createElement("button", { + class: "std-button", + innerText: "More Earnings Info", + clickListener: () => { + dialogBoxCreate( + [ + "

Total Earnings for Current Task:

", + `Money: ${numeralWrapper.formatMoney(sleeve.earningsForTask.money)}`, + `Hacking Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.hack)}`, + `Strength Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.str)}`, + `Defense Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.def)}`, + `Dexterity Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.dex)}`, + `Agility Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.agi)}`, + `Charisma Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.cha)}`, + "

Earnings for Host Consciousness:

", + `Money: ${numeralWrapper.formatMoney(sleeve.earningsForPlayer.money)}`, + `Hacking Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.hack)}`, + `Strength Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.str)}`, + `Defense Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.def)}`, + `Dexterity Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.dex)}`, + `Agility Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.agi)}`, + `Charisma Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.cha)}`, + "

Earnings for Other Sleeves:

", + `Money: ${numeralWrapper.formatMoney(sleeve.earningsForSleeves.money)}`, + `Hacking Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.hack)}`, + `Strength Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.str)}`, + `Defense Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.def)}`, + `Dexterity Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.dex)}`, + `Agility Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.agi)}`, + `Charisma Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.cha)}`, + ].join("
"), false + ); + } + }); return elems; } // Updates the UI for a single Sleeve -function updateSleeveUi() { +function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) { if (!routing.isOn(Page.Sleeves)) { return; } + + elems.stats!.innerHTML = [`Hacking: ${numeralWrapper.format(sleeve.hacking_skill, "0,0")}`, + `Strength: ${numeralWrapper.format(sleeve.strength, "0,0")}`, + `Defense: ${numeralWrapper.format(sleeve.defense, "0,0")}`, + `Dexterity: ${numeralWrapper.format(sleeve.dexterity, "0,0")}`, + `Agility: ${numeralWrapper.format(sleeve.agility, "0,0")}`, + `Charisma: ${numeralWrapper.format(sleeve.charisma, "0,0")}`, + `HP: ${numeralWrapper.format(sleeve.hp, "0,0")} / ${numeralWrapper.format(sleeve.max_hp, "0,0")}
`, + `Shock: ${numeralWrapper.format(100 - sleeve.shock, "0,0")}`, + `Synchronization: ${numeralWrapper.format(sleeve.sync, "0,0")}`].join("
"); + + if (sleeve.currentTask === SleeveTaskType.Crime) { + elems.currentEarningsInfo!.innerHTML = [ + `Money: ${numeralWrapper.formatMoney(sleeve.gainRatesForTask.money)} if successful`, + `Hacking Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.hack, "0.00")} (2x if successful)`, + `Strength Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.str, "0.00")} (2x if successful)`, + `Defense Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.def, "0.00")} (2x if successful)`, + `Dexterity Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.dex, "0.00")} (2x if successful)`, + `Agility Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.agi, "0.00")} (2x if successful)`, + `Charisma Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.cha, "0.00")} (2x if successful)` + ].join("
"); + } else { + elems.currentEarningsInfo!.innerHTML = [ + `Money: ${numeralWrapper.formatMoney(sleeve.gainRatesForTask.money)} / s`, + `Hacking Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.hack, "0.00")} / s`, + `Strength Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.str, "0.00")} / s`, + `Defense Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.def, "0.00")} / s`, + `Dexterity Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.dex, "0.00")} / s`, + `Agility Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.agi, "0.00")} / s`, + `Charisma Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.cha, "0.00")} / s` + ].join("
"); + } } +const factionWorkTypeSelectorOptions: string[] = [ + "Hacking Contracts", + "Security Work", + "Field Work" +]; + +const universitySelectorOptions: string[] = [ + "Study Computer Science", + "Data Structures", + "Networks", + "Algorithms", + "Management", + "Leadership" +]; + +const gymSelectorOptions: string[] = [ + "Train Strength", + "Train Defense", + "Train Dexterity", + "Train Agility" +]; + // Whenever a new task is selected, the "details" selector must update accordingly function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSleeves: Sleeve[]) { - const value: string = + if (playerRef == null) { + throw new Error(`playerRef is null in updateSleeveTaskSelector()`); + } + + // Array of all companies that other sleeves are working at + const forbiddenCompanies: string[] = []; + for (const otherSleeve of allSleeves) { + if (sleeve === otherSleeve) { continue; } + if (otherSleeve.currentTask === SleeveTaskType.Company) { + forbiddenCompanies.push(otherSleeve.currentTaskLocation); + } + } + + // Array of all factions that other sleeves are working for + const forbiddenFactions: string[] = []; + for (const otherSleeve of allSleeves) { + if (sleeve === otherSleeve) { continue; } + if (otherSleeve.currentTask === SleeveTaskType.Faction) { + forbiddenFactions.push(otherSleeve.currentTaskLocation); + } + } + + removeChildrenFromElement(elems.taskDetailsSelector); + + const value: string = getSelectValue(elems.taskSelector); + switch(value) { + case "Work for Company": + const allJobs: string[] = Object.keys(playerRef!.jobs!); + for (let i = 0; i < allJobs.length; ++i) { + if (!forbiddenCompanies.includes(allJobs[i])) { + elems.taskDetailsSelector!.add(createOptionElement(allJobs[i])); + } + } + break; + case "Work for Faction": + for (let i = 0; i < playerRef!.factions!.length; ++i) { + const fac: string = playerRef!.factions[i]!; + if (!forbiddenFactions.includes(fac)) { + elems.taskDetailsSelector!.add(createOptionElement(fac)); + } + } + for (let i = 0; i < factionWorkTypeSelectorOptions.length; ++i) { + elems.taskDetailsSelector2!.add(createOptionElement(factionWorkTypeSelectorOptions[i])); + } + break; + case "Commit Crime": + for (const crimeLabel in Crimes) { + const name: string = Crimes[crimeLabel].name; + elems.taskDetailsSelector!.add(createOptionElement(name, crimeLabel)); + } + break; + case "Take University Course": + // First selector has class type + for (let i = 0; i < universitySelectorOptions.length; ++i) { + elems.taskDetailsSelector!.add(createOptionElement(universitySelectorOptions[i])); + } + + // Second selector has which university + switch (sleeve.city) { + case Cities.Aevum: + elems.taskDetailsSelector2!.add(createOptionElement(Locations.AevumSummitUniversity)); + break; + case Cities.Sector12: + elems.taskDetailsSelector2!.add(createOptionElement(Locations.Sector12RothmanUniversity)); + break; + case Cities.Volhaven: + elems.taskDetailsSelector2!.add(createOptionElement(Locations.VolhavenZBInstituteOfTechnology)); + break; + default: + elems.taskDetailsSelector2!.add(createOptionElement("No university available in city!")); + break; + } + break; + case "Workout at Gym": + // First selector has what stat is being trained + for (let i = 0; i < gymSelectorOptions.length; ++i) { + elems.taskDetailsSelector!.add(createOptionElement(gymSelectorOptions[i])); + } + + // Second selector has gym + switch (sleeve.city) { + case Cities.Aevum: + elems.taskDetailsSelector2!.add(createOptionElement(Locations.AevumCrushFitnessGym)); + elems.taskDetailsSelector2!.add(createOptionElement(Locations.AevumSnapFitnessGym)); + break; + case Cities.Sector12: + elems.taskDetailsSelector2!.add(createOptionElement(Locations.Sector12IronGym)); + elems.taskDetailsSelector2!.add(createOptionElement(Locations.Sector12PowerhouseGym)); + break; + case Cities.Volhaven: + elems.taskDetailsSelector2!.add(createOptionElement(Locations.VolhavenMilleniumFitnessGym)); + break; + default: + elems.taskDetailsSelector2!.add(createOptionElement("No gym available in city!")); + break; + } + + break; + case "Shock Recovery": + // No options in "Details" selector + return; + case "Synchronize": + // No options in "Details" selector + return; + default: + break; + } +} + +function setSleeveTask(sleeve: Sleeve, elems: ISleeveUIElems): void { + try { + if (playerRef == null) { + throw new Error("playerRef is null in Sleeve UI's setSleeveTask()"); + } + + const taskValue: string = getSelectValue(elems.taskSelector); + const detailValue: string = getSelectValue(elems.taskDetailsSelector); + const detailValue2: string = getSelectValue(elems.taskDetailsSelector); + + let res: boolean = false; + switch(taskValue) { + case "Work for Company": + res = sleeve.workForCompany(playerRef!, detailValue); + if (res) { + elems.taskDescription!.innerText = `This sleeve is currently working your ` + + `job at ${sleeve.currentTaskLocation}.`; + } else { + elems.taskDescription!.innerText = "Failed to assign sleeve to task. Invalid choice(s)."; + } + break; + case "Work for Faction": + res = sleeve.workForFaction(playerRef!, detailValue, detailValue2); + if (res) { + elems.taskDescription!.innerText = `This sleeve is currently doing ${detailValue2} for ` + + `${sleeve.currentTaskLocation}.`; + } else { + elems.taskDescription!.innerText = "Failed to assign sleeve to task. Invalid choice(s)."; + } + break; + case "Commit Crime": + sleeve.commitCrime(playerRef!, Crimes[detailValue]); + elems.taskDescription!.innerText = `This sleeve is currently attempting to ` + + `${Crimes[detailValue]}.`; + break; + case "Take University Course": + res = sleeve.takeUniversityCourse(playerRef!, detailValue2, detailValue); + break; + case "Workout at Gym": + res = sleeve.workoutAtGym(playerRef!, detailValue2, detailValue); + break; + case "Shock Recovery": + sleeve.currentTask = SleeveTaskType.Recovery; + elems.taskDescription!.innerText = "This sleeve is currently set to focus on shock recovery. This causes " + + "the Sleeve's shock to decrease at a faster rate."; + break; + case "Synchronize": + sleeve.currentTask = SleeveTaskType.Sync; + elems.taskDescription!.innerText = "This sleeve is currently set to synchronize with the original consciousness. " + + "This causes the Sleeve's synchronization to increase." + break; + default: + console.error(`Invalid/Unrecognized taskValue in setSleeveTask(): ${taskValue}`); + } + + if (routing.isOn(Page.Sleeves)) { + updateSleevesPage(); + } + } catch(e) { + exceptionAlert(e); + } } diff --git a/src/Player.js b/src/Player.js index 32da31c88..6c2ac9b14 100644 --- a/src/Player.js +++ b/src/Player.js @@ -13,7 +13,8 @@ import * as posNames from "./Company/data/CompanyPosi import {CONSTANTS} from "./Constants"; import { Corporation } from "./Corporation/Corporation"; import { Programs } from "./Programs/Programs"; -import {determineCrimeSuccess, Crimes} from "./Crimes"; +import { determineCrimeSuccess } from "./Crime/CrimeHelpers"; +import { Crimes } from "./Crime/Crimes"; import {Engine} from "./engine"; import { Faction } from "./Faction/Faction"; import { Factions } from "./Faction/Factions"; @@ -98,9 +99,13 @@ function PlayerObject() { this.city = Locations.Sector12; this.location = ""; - //Company Information + // Jobs that the player holds + // Map of company name (key) -> name of company position (value. Just the name, not the CompanyPosition object) + // The CompanyPosition name must match a key value in CompanyPositions + this.jobs = {}; + + // Company at which player is CURRENTLY working (only valid when the player is actively working) this.companyName = ""; // Name of Company. Must match a key value in Companies map - this.companyPosition = ""; // Name of Company Position. Must match a key value in CompanyPositions map //Servers this.currentServer = ""; //IP address of Server currently being accessed through terminal @@ -259,7 +264,7 @@ PlayerObject.prototype.prestigeAugmentation = function() { this.location = ""; this.companyName = ""; - this.companyPosition = ""; + this.jobs = {}; this.purchasedServers = []; @@ -339,7 +344,7 @@ PlayerObject.prototype.prestigeSourceFile = function() { this.location = ""; this.companyName = ""; - this.companyPosition = ""; + this.jobs = {}; this.purchasedServers = []; @@ -721,8 +726,10 @@ PlayerObject.prototype.work = function(numCycles) { companyRep = comp.playerReputation; } + const position = this.jobs[this.companyName]; + var txt = document.getElementById("work-in-progress-text"); - txt.innerHTML = "You are currently working as a " + this.companyPosition + + txt.innerHTML = "You are currently working as a " + position + " at " + this.companyName + " (Current Company Reputation: " + numeralWrapper.format(companyRep, '0,0') + ")

" + "You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "

" + @@ -841,9 +848,11 @@ PlayerObject.prototype.workPartTime = function(numCycles) { companyRep = comp.playerReputation; } + const position = this.jobs[this.companyName]; + var txt = document.getElementById("work-in-progress-text"); - txt.innerHTML = "You are currently working as a " + this.companyPosition + - " at " + Player.companyName + " (Current Company Reputation: " + + txt.innerHTML = "You are currently working as a " + position + + " at " + this.companyName + " (Current Company Reputation: " + numeralWrapper.format(companyRep, '0,0') + ")

" + "You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "

" + "You have earned:

" + @@ -1076,9 +1085,10 @@ PlayerObject.prototype.getWorkMoneyGain = function() { if (hasBn11SF) { bn11Mult = 1 + (company.favor / 100); } // Get base salary - const companyPosition = CompanyPositions[this.companyPosition]; + const companyPositionName = this.jobs[this.companyName]; + const companyPosition = CompanyPositions[companyPositionName]; if (companyPosition == null) { - console.error(`Could not find CompanyPosition object for ${this.companyPosition}. Work salary will be 0`); + console.error(`Could not find CompanyPosition object for ${companyPositionName}. Work salary will be 0`); return 0; } @@ -1088,10 +1098,11 @@ PlayerObject.prototype.getWorkMoneyGain = function() { //Hack exp gained per game cycle PlayerObject.prototype.getWorkHackExpGain = function() { const company = Companies[this.companyName]; - const companyPosition = CompanyPositions[this.companyPosition]; + const companyPositionName = this.jobs[this.companyName]; + const companyPosition = CompanyPositions[companyPositionName]; if (company == null || companyPosition == null) { console.error([`Could not find Company object for ${this.companyName}`, - `or CompanyPosition object for ${this.companyPosition}.`, + `or CompanyPosition object for ${companyPositionName}.`, `Work hack exp gain will be 0`].join(" ")); return 0; } @@ -1102,10 +1113,11 @@ PlayerObject.prototype.getWorkHackExpGain = function() { //Str exp gained per game cycle PlayerObject.prototype.getWorkStrExpGain = function() { const company = Companies[this.companyName]; - const companyPosition = CompanyPositions[this.companyPosition]; + const companyPositionName = this.jobs[this.companyName]; + const companyPosition = CompanyPositions[companyPositionName]; if (company == null || companyPosition == null) { console.error([`Could not find Company object for ${this.companyName}`, - `or CompanyPosition object for ${this.companyPosition}.`, + `or CompanyPosition object for ${companyPositionName}.`, `Work str exp gain will be 0`].join(" ")); return 0; } @@ -1116,10 +1128,11 @@ PlayerObject.prototype.getWorkStrExpGain = function() { //Def exp gained per game cycle PlayerObject.prototype.getWorkDefExpGain = function() { const company = Companies[this.companyName]; - const companyPosition = CompanyPositions[this.companyPosition]; + const companyPositionName = this.jobs[this.companyName]; + const companyPosition = CompanyPositions[companyPositionName]; if (company == null || companyPosition == null) { console.error([`Could not find Company object for ${this.companyName}`, - `or CompanyPosition object for ${this.companyPosition}.`, + `or CompanyPosition object for ${companyPositionName}.`, `Work def exp gain will be 0`].join(" ")); return 0; } @@ -1130,10 +1143,11 @@ PlayerObject.prototype.getWorkDefExpGain = function() { //Dex exp gained per game cycle PlayerObject.prototype.getWorkDexExpGain = function() { const company = Companies[this.companyName]; - const companyPosition = CompanyPositions[this.companyPosition]; + const companyPositionName = this.jobs[this.companyName]; + const companyPosition = CompanyPositions[companyPositionName]; if (company == null || companyPosition == null) { console.error([`Could not find Company object for ${this.companyName}`, - `or CompanyPosition object for ${this.companyPosition}.`, + `or CompanyPosition object for ${companyPositionName}.`, `Work dex exp gain will be 0`].join(" ")); return 0; } @@ -1144,10 +1158,11 @@ PlayerObject.prototype.getWorkDexExpGain = function() { //Agi exp gained per game cycle PlayerObject.prototype.getWorkAgiExpGain = function() { const company = Companies[this.companyName]; - const companyPosition = CompanyPositions[this.companyPosition]; + const companyPositionName = this.jobs[this.companyName]; + const companyPosition = CompanyPositions[companyPositionName]; if (company == null || companyPosition == null) { console.error([`Could not find Company object for ${this.companyName}`, - `or CompanyPosition object for ${this.companyPosition}.`, + `or CompanyPosition object for ${companyPositionName}.`, `Work agi exp gain will be 0`].join(" ")); return 0; } @@ -1158,10 +1173,11 @@ PlayerObject.prototype.getWorkAgiExpGain = function() { //Charisma exp gained per game cycle PlayerObject.prototype.getWorkChaExpGain = function() { const company = Companies[this.companyName]; - const companyPosition = CompanyPositions[this.companyPosition]; + const companyPositionName = this.jobs[this.companyName]; + const companyPosition = CompanyPositions[companyPositionName]; if (company == null || companyPosition == null) { console.error([`Could not find Company object for ${this.companyName}`, - `or CompanyPosition object for ${this.companyPosition}.`, + `or CompanyPosition object for ${companyPositionName}.`, `Work cha exp gain will be 0`].join(" ")); return 0; } @@ -1172,10 +1188,11 @@ PlayerObject.prototype.getWorkChaExpGain = function() { //Reputation gained per game cycle PlayerObject.prototype.getWorkRepGain = function() { const company = Companies[this.companyName]; - const companyPosition = CompanyPositions[this.companyPosition]; + const companyPositionName = this.jobs[this.companyName]; + const companyPosition = CompanyPositions[companyPositionName]; if (company == null || companyPosition == null) { console.error([`Could not find Company object for ${this.companyName}`, - `or CompanyPosition object for ${this.companyPosition}.`, + `or CompanyPosition object for ${companyPositionName}.`, `Work rep gain will be 0`].join(" ")); return 0; } @@ -1458,7 +1475,9 @@ PlayerObject.prototype.finishClass = function(sing=false) { } //The EXP and $ gains are hardcoded. Time is in ms -PlayerObject.prototype.startCrime = function(hackExp, strExp, defExp, dexExp, agiExp, chaExp, money, time, singParams=null) { +PlayerObject.prototype.startCrime = function(crimeType, hackExp, strExp, defExp, dexExp, agiExp, chaExp, money, time, singParams=null) { + this.crimeType = crimeType; + this.resetWorkStatus(); this.isWorking = true; this.workType = CONSTANTS.WorkTypeCrime; @@ -1672,7 +1691,7 @@ PlayerObject.prototype.applyForJob = function(entryPosType, sing=false) { if (this.companyName !== "") { currCompany = Companies[this.companyName]; } - const currPositionName = this.companyPosition; + const currPositionName = this.jobs[this.companyName]; // Get company that's being applied to const company = Companies[this.location]; //Company being applied to @@ -1729,33 +1748,14 @@ PlayerObject.prototype.applyForJob = function(entryPosType, sing=false) { } } - - //Lose reputation from a Company if you are leaving it for another job - let leaveCompany = false; - let oldCompanyName = ""; - if (currCompany != null) { - if (currCompany.name != company.name) { - leaveCompany = true; - oldCompanyName = currCompany.name; - currCompany.playerReputation -= 1000; - if (currCompany.playerReputation < 0) { currCompany.playerReputation = 0; } - } - } - this.companyName = company.name; - this.companyPosition = pos.name; + this.jobs[company.name] = pos.name; document.getElementById("world-menu-header").click(); document.getElementById("world-menu-header").click(); - if (leaveCompany) { - if (sing) { return true; } - dialogBoxCreate([`Congratulations! You were offered a new job at ${this.companyName} as a ${pos.name}!`, - `You lost 1000 reputation at your old company ${oldCompanyName} because you left.`].join("
")); - } else { - if (sing) { return true; } - dialogBoxCreate("Congratulations! You were offered a new job at " + this.companyName + " as a " + pos.name + "!"); - } + if (sing) { return true; } + dialogBoxCreate("Congratulations! You were offered a new job at " + this.companyName + " as a " + pos.name + "!"); Engine.loadLocationContent(); } @@ -1775,7 +1775,8 @@ PlayerObject.prototype.getNextCompanyPosition = function(company, entryPosType) //If the entry pos type and the player's current position have the same type, //return the player's "nextCompanyPosition". Otherwise return the entryposType //Employed at this company, so just return the next position if it exists. - const currentPosition = CompanyPositions[this.companyPosition]; + const currentPositionName = this.jobs[this.companyName]; + const currentPosition = CompanyPositions[currentPositionName]; if ((currentPosition.isSoftwareJob() && entryPosType.isSoftwareJob()) || (currentPosition.isITJob() && entryPosType.isITJob()) || (currentPosition.isBusinessJob() && entryPosType.isBusinessJob()) || @@ -1852,7 +1853,7 @@ PlayerObject.prototype.applyForEmployeeJob = function(sing=false) { var company = Companies[this.location]; //Company being applied to if (this.isQualified(company, CompanyPositions[posNames.MiscCompanyPositions[1]])) { this.companyName = company.name; - this.companyPosition = posNames.MiscCompanyPositions[1]; + this.jobs[company.name] = posNames.MiscCompanyPositions[1]; document.getElementById("world-menu-header").click(); document.getElementById("world-menu-header").click(); if (sing) {return true;} @@ -1868,7 +1869,7 @@ PlayerObject.prototype.applyForPartTimeEmployeeJob = function(sing=false) { var company = Companies[this.location]; //Company being applied to if (this.isQualified(company, CompanyPositions[posNames.PartTimeCompanyPositions[1]])) { this.companyName = company.name; - this.companyPosition = posNames.PartTimeCompanyPositions[1]; + this.jobs[company.name] = posNames.PartTimeCompanyPositions[1]; document.getElementById("world-menu-header").click(); document.getElementById("world-menu-header").click(); if (sing) {return true;} @@ -1884,7 +1885,7 @@ PlayerObject.prototype.applyForWaiterJob = function(sing=false) { var company = Companies[this.location]; //Company being applied to if (this.isQualified(company, CompanyPositions[posNames.MiscCompanyPositions[0]])) { this.companyName = company.name; - this.companyPosition = posNames.MiscCompanyPositions[0]; + this.jobs[company.name] = posNames.MiscCompanyPositions[0]; document.getElementById("world-menu-header").click(); document.getElementById("world-menu-header").click(); if (sing) {return true;} @@ -1900,7 +1901,7 @@ PlayerObject.prototype.applyForPartTimeWaiterJob = function(sing=false) { var company = Companies[this.location]; //Company being applied to if (this.isQualified(company, CompanyPositions[posNames.PartTimeCompanyPositions[0]])) { this.companyName = company.name; - this.companyPosition = posNames.PartTimeCompanyPositions[0]; + this.jobs[company.name] = posNames.PartTimeCompanyPositions[0]; document.getElementById("world-menu-header").click(); document.getElementById("world-menu-header").click(); if (sing) {return true;} @@ -1995,6 +1996,9 @@ PlayerObject.prototype.checkForFactionInvitations = function() { companyRep = company.playerReputation; } + const allCompanies = Object.keys(this.jobs); + const allPositions = Object.values(this.jobs); + //Illuminati var illuminatiFac = Factions["Illuminati"]; if (!illuminatiFac.isBanned && !illuminatiFac.isMember && !illuminatiFac.alreadyInvited && @@ -2033,14 +2037,14 @@ PlayerObject.prototype.checkForFactionInvitations = function() { //ECorp var ecorpFac = Factions["ECorp"]; if (!ecorpFac.isBanned && !ecorpFac.isMember && !ecorpFac.alreadyInvited && - this.companyName == Locations.AevumECorp && companyRep >= CONSTANTS.CorpFactionRepRequirement) { + allCompanies.includes(Locations.AevumECorp) && companyRep >= CONSTANTS.CorpFactionRepRequirement) { invitedFactions.push(ecorpFac); } //MegaCorp var megacorpFac = Factions["MegaCorp"]; if (!megacorpFac.isBanned && !megacorpFac.isMember && !megacorpFac.alreadyInvited && - this.companyName == Locations.Sector12MegaCorp && companyRep >= CONSTANTS.CorpFactionRepRequirement) { + allCompanies.includes(Locations.Sector12MegaCorp) && companyRep >= CONSTANTS.CorpFactionRepRequirement) { invitedFactions.push(megacorpFac); } @@ -2048,42 +2052,42 @@ PlayerObject.prototype.checkForFactionInvitations = function() { var bachmanandassociatesFac = Factions["Bachman & Associates"]; if (!bachmanandassociatesFac.isBanned && !bachmanandassociatesFac.isMember && !bachmanandassociatesFac.alreadyInvited && - this.companyName == Locations.AevumBachmanAndAssociates && companyRep >= CONSTANTS.CorpFactionRepRequirement) { + allCompanies.includes(Locations.AevumBachmanAndAssociates) && companyRep >= CONSTANTS.CorpFactionRepRequirement) { invitedFactions.push(bachmanandassociatesFac); } //Blade Industries var bladeindustriesFac = Factions["Blade Industries"]; if (!bladeindustriesFac.isBanned && !bladeindustriesFac.isMember && !bladeindustriesFac.alreadyInvited && - this.companyName == Locations.Sector12BladeIndustries && companyRep >= CONSTANTS.CorpFactionRepRequirement) { + allCompanies.includes(Locations.Sector12BladeIndustries) && companyRep >= CONSTANTS.CorpFactionRepRequirement) { invitedFactions.push(bladeindustriesFac); } //NWO var nwoFac = Factions["NWO"]; if (!nwoFac.isBanned && !nwoFac.isMember && !nwoFac.alreadyInvited && - this.companyName == Locations.VolhavenNWO && companyRep >= CONSTANTS.CorpFactionRepRequirement) { + allCompanies.includes(Locations.VolhavenNWO) && companyRep >= CONSTANTS.CorpFactionRepRequirement) { invitedFactions.push(nwoFac); } //Clarke Incorporated var clarkeincorporatedFac = Factions["Clarke Incorporated"]; if (!clarkeincorporatedFac.isBanned && !clarkeincorporatedFac.isMember && !clarkeincorporatedFac.alreadyInvited && - this.companyName == Locations.AevumClarkeIncorporated && companyRep >= CONSTANTS.CorpFactionRepRequirement) { + allCompanies.includes(Locations.AevumClarkeIncorporated) && companyRep >= CONSTANTS.CorpFactionRepRequirement) { invitedFactions.push(clarkeincorporatedFac); } //OmniTek Incorporated var omnitekincorporatedFac = Factions["OmniTek Incorporated"]; if (!omnitekincorporatedFac.isBanned && !omnitekincorporatedFac.isMember && !omnitekincorporatedFac.alreadyInvited && - this.companyName == Locations.VolhavenOmniTekIncorporated && companyRep >= CONSTANTS.CorpFactionRepRequirement) { + allCompanies.includes(Locations.VolhavenOmniTekIncorporated) && companyRep >= CONSTANTS.CorpFactionRepRequirement) { invitedFactions.push(omnitekincorporatedFac); } //Four Sigma var foursigmaFac = Factions["Four Sigma"]; if (!foursigmaFac.isBanned && !foursigmaFac.isMember && !foursigmaFac.alreadyInvited && - this.companyName == Locations.Sector12FourSigma && companyRep >= CONSTANTS.CorpFactionRepRequirement) { + allCompanies.includes(Locations.Sector12FourSigma) && companyRep >= CONSTANTS.CorpFactionRepRequirement) { invitedFactions.push(foursigmaFac); } @@ -2091,7 +2095,7 @@ PlayerObject.prototype.checkForFactionInvitations = function() { var kuaigonginternationalFac = Factions["KuaiGong International"]; if (!kuaigonginternationalFac.isBanned && !kuaigonginternationalFac.isMember && !kuaigonginternationalFac.alreadyInvited && - this.companyName == Locations.ChongqingKuaiGongInternational && companyRep >= CONSTANTS.CorpFactionRepRequirement) { + allCompanies.includes(Locations.ChongqingKuaiGongInternational) && companyRep >= CONSTANTS.CorpFactionRepRequirement) { invitedFactions.push(kuaigonginternationalFac); } @@ -2104,7 +2108,7 @@ PlayerObject.prototype.checkForFactionInvitations = function() { if (!fulcrumsecrettechonologiesFac.isBanned && !fulcrumsecrettechonologiesFac.isMember && !fulcrumsecrettechonologiesFac.alreadyInvited && fulcrumSecretServer.manuallyHacked && - this.companyName == Locations.AevumFulcrumTechnologies && companyRep >= 250000) { + allCompanies.includes(Locations.AevumFulcrumTechnologies) && companyRep >= 250000) { invitedFactions.push(fulcrumsecrettechonologiesFac); } } @@ -2187,8 +2191,8 @@ PlayerObject.prototype.checkForFactionInvitations = function() { if (!speakersforthedeadFac.isBanned && !speakersforthedeadFac.isMember && !speakersforthedeadFac.alreadyInvited && this.hacking_skill >= 100 && this.strength >= 300 && this.defense >= 300 && this.dexterity >= 300 && this.agility >= 300 && this.numPeopleKilled >= 30 && - this.karma <= -45 && this.companyName != Locations.Sector12CIA && - this.companyName != Locations.Sector12NSA) { + this.karma <= -45 && !allCompanies.includes(Locations.Sector12CIA) && + !allCompanies.includes(Locations.Sector12NSA)) { invitedFactions.push(speakersforthedeadFac); } @@ -2197,8 +2201,8 @@ PlayerObject.prototype.checkForFactionInvitations = function() { if (!thedarkarmyFac.isBanned && !thedarkarmyFac.isMember && !thedarkarmyFac.alreadyInvited && this.hacking_skill >= 300 && this.strength >= 300 && this.defense >= 300 && this.dexterity >= 300 && this.agility >= 300 && this.city == Locations.Chongqing && - this.numPeopleKilled >= 5 && this.karma <= -45 && this.companyName != Locations.Sector12CIA && - this.companyName != Locations.Sector12NSA) { + this.numPeopleKilled >= 5 && this.karma <= -45 && !allCompanies.includes(Locations.Sector12CIA) && + !allCompanies.includes(Locations.Sector12NSA)) { invitedFactions.push(thedarkarmyFac); } @@ -2209,18 +2213,16 @@ PlayerObject.prototype.checkForFactionInvitations = function() { this.dexterity >= 200 && this.agility >= 200 && (this.city == Locations.Aevum || this.city == Locations.Sector12) && this.money.gte(10000000) && this.karma <= -90 && - this.companyName != Locations.Sector12CIA && this.companyName != Locations.Sector12NSA) { + !allCompanies.includes(Locations.Sector12CIA) && !allCompanies.includes(Locations.Sector12NSA)) { invitedFactions.push(thesyndicateFac); } //Silhouette var silhouetteFac = Factions["Silhouette"]; - const companyPosition = CompanyPositions[this.companyPosition]; if (!silhouetteFac.isBanned && !silhouetteFac.isMember && !silhouetteFac.alreadyInvited && - companyPosition != null && - (companyPosition.name == "Chief Technology Officer" || - companyPosition.name == "Chief Financial Officer" || - companyPosition.name == "Chief Executive Officer") && + (allPositions.includes("Chief Technology Officer") || + allPositions.includes("Chief Financial Officer") || + allPositions.includes("Chief Executive Officer")) && this.money.gte(15000000) && this.karma <= -22) { invitedFactions.push(silhouetteFac); } diff --git a/src/SaveObject.js b/src/SaveObject.js index adc53f308..267072cfa 100755 --- a/src/SaveObject.js +++ b/src/SaveObject.js @@ -151,6 +151,16 @@ function evaluateVersionCompatibility(ver) { } } } + + // This version allowed players to hold multiple jobs + if (ver <= "0.43.0") { + if (Player.companyName !== "" && Player.companyPosition !== "") { + console.log("Copied player's companyName and companyPosition properties to the Player.jobs map for v0.43.0"); + Player.jobs[Player.companyName] = Player.companyPosition; + } + + delete Player.companyPosition; + } } function loadGame(saveString) { diff --git a/src/engine.js b/src/engine.js index c06ad983b..9a54c8df4 100644 --- a/src/engine.js +++ b/src/engine.js @@ -557,9 +557,9 @@ const Engine = { displayCharacterInfo: function() { removeChildrenFromElement(Engine.Display.characterInfo); - var companyPosition = ""; - if (Player.companyPosition !== "") { - companyPosition = Player.companyPosition; + let companyPosition = ""; + if (Player.companyName !== "") { + companyPosition = Player.jobs[Player.companyName]; } var intText = ""; @@ -576,8 +576,9 @@ const Engine = { innerHTML: 'General

' + 'Current City: ' + Player.city + '

' + - 'Employer: ' + Player.companyName + '
' + - 'Job Title: ' + companyPosition + '

' + + `Employer at which you last worked: ${Player.companyName}
` + + `Job you last worked: ${companyPosition}
` + + `All Employers: ${Object.keys(Player.jobs).join(", ")}

` + 'Money: $' + formatNumber(Player.money.toNumber(), 2) + '


' + 'Stats

' + 'Hacking Level: ' + (Player.hacking_skill).toLocaleString() + @@ -1306,7 +1307,7 @@ const Engine = { else {factions.style.display = "none";} if (Player.firstAugPurchased) {visibleMenuTabs.push(augmentations);} else {augmentations.style.display = "none";} - if (Player.companyPosition !== "") {visibleMenuTabs.push(job);} + if (Player.companyName !== "") {visibleMenuTabs.push(job);} else {job.style.display = "none";} if (Player.firstTimeTraveled) {visibleMenuTabs.push(travel);} else {travel.style.display = "none";} @@ -1589,7 +1590,7 @@ const Engine = { var gangLink = document.getElementById("gang-menu-link"); // Determine whether certain links should show up - job.style.display = Player.companyPosition !== "" ? "list-item" : "none"; + job.style.display = Player.companyName !== "" ? "list-item" : "none"; stockmarket.style.display = Player.hasWseAccount ? "list-item" : "none"; bladeburner.style.display = Player.bladeburner instanceof Bladeburner ? "list-item" : "none"; corporation.style.display = Player.corporation instanceof Corporation ? "list-item" : "none"; diff --git a/src/ui/numeralFormat.ts b/src/ui/numeralFormat.ts index 10cf9ea9b..67430636c 100644 --- a/src/ui/numeralFormat.ts +++ b/src/ui/numeralFormat.ts @@ -40,13 +40,20 @@ class NumeralFormatter { return numeral(n).format(format); } + formatBigNumber(n: number): string { + return this.format(n, "0.000a"); + } + formatMoney(n: number): string { return this.format(n, "$0.000a"); } - formatBigNumber(n: number): string { - return this.format(n, "0.000a"); + formatPercentage(n: number, decimalPlaces: number=2): string { + const formatter: string = "0." + "0".repeat(decimalPlaces) + "%"; + return this.format(n, formatter); } + + } export const numeralWrapper = new NumeralFormatter(); diff --git a/utils/uiHelpers/createOptionElement.ts b/utils/uiHelpers/createOptionElement.ts index c1c56347f..cbd09f8ba 100644 --- a/utils/uiHelpers/createOptionElement.ts +++ b/utils/uiHelpers/createOptionElement.ts @@ -1,11 +1,11 @@ import { createElement } from "./createElement"; -export function createOptionElement(text: string, value: string="") { - const sanitizedValue: string = value; +export function createOptionElement(text: string, value: string=""): HTMLOptionElement { + let sanitizedValue: string = value; if (sanitizedValue === "") { sanitizedValue = text; } return createElement("option", { text: text, value: sanitizedValue, - }); + }) as HTMLOptionElement; } diff --git a/utils/uiHelpers/getSelectData.ts b/utils/uiHelpers/getSelectData.ts new file mode 100644 index 000000000..ff460632e --- /dev/null +++ b/utils/uiHelpers/getSelectData.ts @@ -0,0 +1,9 @@ +export function getSelectValue(selector: HTMLSelectElement | null): string { + if (selector == null) { return ""; } + return selector[selector.selectedIndex].value; +} + +export function getSelectText(selector: HTMLSelectElement | null): string { + if (selector == null) { return ""; } + return selector[selector.selectedIndex].text; +}