Convert Locations and Faction implementations to TypeScript. Also restructed directory. Implemented Sleeve, but untested

This commit is contained in:
danielyxie 2018-12-07 02:54:26 -08:00
parent 8fcb1706cc
commit 48c694c9c1
29 changed files with 171104 additions and 487 deletions

58892
dist/engine.bundle.js vendored

File diff suppressed because one or more lines are too long

111048
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Bitburner</title> <title>Bitburner - development</title>
<link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png">
@ -474,7 +474,8 @@
<!-- Tutorial content --> <!-- Tutorial content -->
<div id="tutorial-container" class="generic-menupage-container"> <div id="tutorial-container" class="generic-menupage-container">
<a id="tutorial-getting-started-link" class="a-link-button" href="http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner" target="_blank"> Getting Started </a> <a id="tutorial-getting-started-link" class="a-link-button"
href="http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner" target="_blank"> Getting Started </a>
<a id="tutorial-networking-link" class="a-link-button"> Servers & Networking </a> <a id="tutorial-networking-link" class="a-link-button"> Servers & Networking </a>
<a id="tutorial-hacking-link" class="a-link-button"> Hacking </a> <a id="tutorial-hacking-link" class="a-link-button"> Hacking </a>
<a id="tutorial-scripts-link" class="a-link-button"> Scripts </a> <a id="tutorial-scripts-link" class="a-link-button"> Scripts </a>
@ -483,7 +484,8 @@
<a id="tutorial-jobs-link" class="a-link-button"> Companies and Infiltration </a> <a id="tutorial-jobs-link" class="a-link-button"> Companies and Infiltration </a>
<a id="tutorial-factions-link" class="a-link-button"> Factions </a> <a id="tutorial-factions-link" class="a-link-button"> Factions </a>
<a id="tutorial-augmentations-link" class="a-link-button"> Augmentations </a> <a id="tutorial-augmentations-link" class="a-link-button"> Augmentations </a>
<a id="tutorial-shortcuts-link" class="a-link-button" href="https://bitburner.wikia.com/wiki/Shortcuts" target="_blank"> Keyboard Shortcuts </a> <a id="tutorial-shortcuts-link" class="a-link-button"
href="https://bitburner.wikia.com/wiki/Shortcuts" target="_blank"> Keyboard Shortcuts </a>
<a id="tutorial-back-button" class="a-link-button"> Back </a> <a id="tutorial-back-button" class="a-link-button"> Back </a>
<p id="tutorial-text"> </p> <p id="tutorial-text"> </p>

@ -1,7 +1,8 @@
import { BitNodeMultipliers } from "./BitNodeMultipliers"; import { BitNodeMultipliers } from "./BitNodeMultipliers";
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { Engine } from "./engine"; import { Engine } from "./engine";
import {Factions, factionExists} from "./Faction"; import { Factions,
factionExists } from "./Faction/Factions";
import { hasBladeburnerSF } from "./NetscriptFunctions"; import { hasBladeburnerSF } from "./NetscriptFunctions";
import { addWorkerScript } from "./NetscriptWorker"; import { addWorkerScript } from "./NetscriptWorker";
import { Player } from "./Player"; import { Player } from "./Player";
@ -1569,13 +1570,13 @@ function initAugmentations() {
//Do this before adding special Augmentations that become available in later BitNodes //Do this before adding special Augmentations that become available in later BitNodes
if (Player.bitNodeN === 2) { if (Player.bitNodeN === 2) {
console.log("Adding all augmentations to crime factions for Bit node 2"); console.log("Adding all augmentations to crime factions for Bit node 2");
Factions["Slum Snakes"].addAllAugmentations(); Factions["Slum Snakes"].addAllAugmentations(Augmentations);
Factions["Tetrads"].addAllAugmentations(); Factions["Tetrads"].addAllAugmentations(Augmentations);
Factions["The Syndicate"].addAllAugmentations(); Factions["The Syndicate"].addAllAugmentations(Augmentations);
Factions["The Dark Army"].addAllAugmentations(); Factions["The Dark Army"].addAllAugmentations(Augmentations);
Factions["Speakers for the Dead"].addAllAugmentations(); Factions["Speakers for the Dead"].addAllAugmentations(Augmentations);
Factions["NiteSec"].addAllAugmentations(); Factions["NiteSec"].addAllAugmentations(Augmentations);
Factions["The Black Hand"].addAllAugmentations(); Factions["The Black Hand"].addAllAugmentations(Augmentations);
} }
//Special Bladeburner Augmentations //Special Bladeburner Augmentations

@ -2,8 +2,9 @@ import {Augmentations, AugmentationNames} from "./Augmentations";
import { BitNodeMultipliers } from "./BitNodeMultipliers"; import { BitNodeMultipliers } from "./BitNodeMultipliers";
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { Engine } from "./engine"; import { Engine } from "./engine";
import {Faction, Factions, factionExists, import { Faction } from "./Faction/Faction";
joinFaction, displayFactionContent} from "./Faction"; import { Factions, factionExists } from "./Faction/Factions";
import { joinFaction, displayFactionContent } from "./Faction/FactionHelpers";
import { Locations } from "./Locations"; import { Locations } from "./Locations";
import { Player } from "./Player"; import { Player } from "./Player";
import { hackWorldDaemon, redPillFlag } from "./RedPill"; import { hackWorldDaemon, redPillFlag } from "./RedPill";

@ -1,7 +1,7 @@
import { CodingContract, import { CodingContract,
CodingContractRewardType, CodingContractRewardType,
CodingContractTypes } from "./CodingContracts"; CodingContractTypes } from "./CodingContracts";
import {Factions} from "./Faction"; import { Factions } from "./Faction/Factions";
import { Player } from "./Player"; import { Player } from "./Player";
import { GetServerByHostname, import { GetServerByHostname,
AllServers } from "./Server"; AllServers } from "./Server";

@ -125,7 +125,7 @@ export class Company {
} }
/** /**
* Serialize the current file to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): any {
return Generic_toJSON("Company", this); return Generic_toJSON("Company", this);

@ -1,5 +1,5 @@
import { BitNodeMultipliers } from "./BitNodeMultipliers"; import { BitNodeMultipliers } from "./BitNodeMultipliers";
import {Factions} from "./Faction"; import { Factions } from "./Faction/Factions";
import { showLiterature } from "./Literature"; import { showLiterature } from "./Literature";
import { Locations } from "./Locations"; import { Locations } from "./Locations";
import { Player } from "./Player"; import { Player } from "./Player";
@ -19,11 +19,16 @@ import {getRandomInt} from "../utils/helpers/g
import { isString } from "../utils/helpers/isString"; import { isString } from "../utils/helpers/isString";
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement"; import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById"; import { removeElementById } from "../utils/uiHelpers/removeElementById";
import {yesNoBoxCreate, yesNoTxtInpBoxCreate, import { yesNoBoxCreate,
yesNoBoxGetYesButton, yesNoBoxGetNoButton, yesNoTxtInpBoxCreate,
yesNoTxtInpBoxGetYesButton, yesNoTxtInpBoxGetNoButton, yesNoBoxGetYesButton,
yesNoTxtInpBoxGetInput, yesNoBoxClose, yesNoBoxGetNoButton,
yesNoTxtInpBoxClose, yesNoBoxOpen} from "../utils/YesNoBox"; yesNoTxtInpBoxGetYesButton,
yesNoTxtInpBoxGetNoButton,
yesNoTxtInpBoxGetInput,
yesNoBoxClose,
yesNoTxtInpBoxClose,
yesNoBoxOpen } from "../utils/YesNoBox";
/* State */ /* State */
var companyStates = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"]; var companyStates = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"];

@ -1,7 +1,7 @@
import { AugmentationNames } from "./Augmentations"; import { AugmentationNames } from "./Augmentations";
import { generateRandomContract } from "./CodingContractGenerator"; import { generateRandomContract } from "./CodingContractGenerator";
import { Programs } from "./CreateProgram"; import { Programs } from "./CreateProgram";
import {Factions} from "./Faction"; import { Factions } from "./Faction/Factions";
import { Player } from "./Player"; import { Player } from "./Player";
import { AllServers } from "./Server"; import { AllServers } from "./Server";
import { hackWorldDaemon } from "./RedPill"; import { hackWorldDaemon } from "./RedPill";

118
src/Faction/Faction.ts Normal file

@ -0,0 +1,118 @@
import { CONSTANTS } from "../Constants";
import { FactionInfo,
FactionInfos } from "./FactionInfo";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
export class Faction {
/**
* Initiatizes a Faction object from a JSON save state.
*/
static fromJSON(value: any): Faction {
return Generic_fromJSON(Faction, value.data);
}
/**
* Flag signalling whether the player has already received an invitation
* to this faction
*/
alreadyInvited: boolean = false;
/**
* Holds names of all augmentations that this Faction offers
*/
augmentations: string[] = [];
/**
* Amount of favor the player has with this faction.
*/
favor: number = 0;
/**
* Flag signalling whether player has been banned from this faction
*/
isBanned: boolean = false;
/**
* Flag signalling whether player is a member of this faction
*/
isMember: boolean = false;
/**
* Name of faction
*/
name: string = "";
/**
* Amount of reputation player has with this faction
*/
playerReputation: number = 0;
/**
* Reputation from the last "prestige" that was not converted to favor.
* This reputation rolls over and is used for the next favor calculation
*/
rolloverRep: number = 0;
constructor(name: string="") {
this.name = name;
}
getInfo(): FactionInfo {
const info = FactionInfos[this.name];
if (info == null) {
throw new Error(`Missing faction from FactionInfos: ${this.name} this probably means the faction got corrupted somehow`);
}
return info;
}
gainFavor(): void {
if (this.favor == null) { this.favor = 0; }
if (this.rolloverRep == null) { this.rolloverRep = 0; }
const res = this.getFavorGain();
if (res.length !== 2) {
console.error("Invalid result from getFavorGain() function");
return;
}
this.favor += res[0];
this.rolloverRep = res[1];
}
//Returns an array with [How much favor would be gained, how much rep would be left over]
getFavorGain(): number[] {
if (this.favor == null) { this.favor = 0; }
if (this.rolloverRep == null) { this.rolloverRep = 0; }
var favorGain = 0, rep = this.playerReputation + this.rolloverRep;
let reqdRep = CONSTANTS.FactionReputationToFavorBase *
Math.pow(CONSTANTS.FactionReputationToFavorMult, this.favor);
while(rep > 0) {
if (rep >= reqdRep) {
++favorGain;
rep -= reqdRep;
} else {
break;
}
reqdRep *= CONSTANTS.FactionReputationToFavorMult;
}
return [favorGain, rep];
}
//Adds all Augmentations to this faction.
addAllAugmentations(augs: object): void {
this.augmentations.length = 0;
for (const name in augs) {
if (augs.hasOwnProperty(name)) {
this.augmentations.push(name);
}
}
}
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): any {
return Generic_toJSON("Faction", this);
}
}
Reviver.constructors.Faction = Faction;

@ -1,139 +1,26 @@
import {Augmentations, AugmentationNames, import {Augmentations, AugmentationNames,
PlayerOwnedAugmentation} from "./Augmentations"; PlayerOwnedAugmentation} from "../Augmentations";
import {BitNodeMultipliers} from "./BitNodeMultipliers"; import {BitNodeMultipliers} from "../BitNodeMultipliers";
import {CONSTANTS} from "./Constants"; import {CONSTANTS} from "../Constants";
import {Engine} from "./engine"; import {Engine} from "../engine";
import {FactionInfos} from "./FactionInfo"; import {FactionInfos} from "./FactionInfo";
import {Locations} from "./Location"; import {Locations} from "../Location";
import {HackingMission, setInMission} from "./Missions"; import {HackingMission, setInMission} from "../Missions";
import {Player} from "./Player"; import {Player} from "../Player";
import {PurchaseAugmentationsOrderSetting} from "./SettingEnums"; import {PurchaseAugmentationsOrderSetting} from "../SettingEnums";
import {Settings} from "./Settings"; import {Settings} from "../Settings";
import {Page, routing} from "./ui/navigationTracking"; import {Page, routing} from "../ui/navigationTracking";
import {numeralWrapper} from "./ui/numeralFormat"; import {numeralWrapper} from "../ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox"; import {dialogBoxCreate} from "../../utils/DialogBox";
import {factionInvitationBoxCreate} from "../utils/FactionInvitationBox"; import {factionInvitationBoxCreate} from "../../utils/FactionInvitationBox";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement"; import {removeChildrenFromElement} from "../../utils/uiHelpers/removeChildrenFromElement";
import {createElement} from "../utils/uiHelpers/createElement"; import {createElement} from "../../utils/uiHelpers/createElement";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver"; Generic_fromJSON} from "../../utils/JSONReviver";
import {formatNumber} from "../utils/StringHelperFunctions"; import {formatNumber} from "../../utils/StringHelperFunctions";
import {yesNoBoxCreate, yesNoBoxGetYesButton, import {yesNoBoxCreate, yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox"; yesNoBoxGetNoButton, yesNoBoxClose} from "../../utils/YesNoBox";
function Faction(name="") {
this.name = name;
this.augmentations = []; //Name of augmentation only
//Player-related properties for faction
this.isMember = false; //Whether player is member
this.isBanned = false; //Whether or not player is banned from joining this faction
this.playerReputation = 0; //"Reputation" within faction
this.alreadyInvited = false;
//Faction favor
this.favor = 0;
this.rolloverRep = 0;
};
Faction.prototype.getInfo = function() {
const info = FactionInfos[this.name];
if(info == null) {
throw new Error("Missing faction from FactionInfos: " + this.name+" this probably means the faction got corrupted somehow");
}
return info;
}
Faction.prototype.gainFavor = function() {
if (this.favor == null || this.favor == undefined) {this.favor = 0;}
if (this.rolloverRep == null || this.rolloverRep == undefined) {this.rolloverRep = 0;}
var res = this.getFavorGain();
if (res.length != 2) {
console.log("Error: invalid result from getFavorGain() function");
return;
}
this.favor += res[0];
this.rolloverRep = res[1];
}
//Returns an array with [How much favor would be gained, how much rep would be left over]
Faction.prototype.getFavorGain = function() {
if (this.favor == null || this.favor == undefined) {this.favor = 0;}
if (this.rolloverRep == null || this.rolloverRep == undefined) {this.rolloverRep = 0;}
var favorGain = 0, rep = this.playerReputation + this.rolloverRep;
var reqdRep = CONSTANTS.FactionReputationToFavorBase *
Math.pow(CONSTANTS.FactionReputationToFavorMult, this.favor);
while(rep > 0) {
if (rep >= reqdRep) {
++favorGain;
rep -= reqdRep;
} else {
break;
}
reqdRep *= CONSTANTS.FactionReputationToFavorMult;
}
return [favorGain, rep];
}
//Adds all Augmentations to this faction.
Faction.prototype.addAllAugmentations = function() {
this.augmentations.length = 0;
for (var name in Augmentations) {
if (Augmentations.hasOwnProperty(name)) {
this.augmentations.push(name);
}
}
}
Faction.prototype.toJSON = function() {
return Generic_toJSON("Faction", this);
}
Faction.fromJSON = function(value) {
return Generic_fromJSON(Faction, value.data);
}
Reviver.constructors.Faction = Faction;
//Map of factions indexed by faction name
let Factions = {}
function loadFactions(saveString) {
Factions = JSON.parse(saveString, Reviver);
}
function AddToFactions(faction) {
var name = faction.name;
Factions[name] = faction;
}
function factionExists(name) {
return Factions.hasOwnProperty(name);
}
//TODO Augmentation price and rep requirement mult are 1 for everything right now,
// This might change in the future for balance
function initFactions() {
for(const name in FactionInfos) {
resetFaction(new Faction(name));
}
}
//Resets a faction during (re-)initialization. Saves the favor in the new
//Faction object and deletes the old Faction Object from "Factions". Then
//reinserts the new Faction object
function resetFaction(newFactionObject) {
if (!(newFactionObject instanceof Faction)) {
throw new Error("Invalid argument 'newFactionObject' passed into resetFaction()");
}
var factionName = newFactionObject.name;
if (factionExists(factionName)) {
newFactionObject.favor = Factions[factionName].favor;
delete Factions[factionName];
}
AddToFactions(newFactionObject);
}
function inviteToFaction(faction) { function inviteToFaction(faction) {
if (Settings.SuppressFactionInvites) { if (Settings.SuppressFactionInvites) {
@ -783,6 +670,6 @@ function processPassiveFactionRepGain(numCycles) {
} }
} }
export {getNextNeurofluxLevel, Factions, initFactions, inviteToFaction, export {getNextNeurofluxLevel, inviteToFaction,
joinFaction, displayFactionContent, processPassiveFactionRepGain, joinFaction, displayFactionContent, processPassiveFactionRepGain,
loadFactions, Faction, purchaseAugmentation, factionExists}; purchaseAugmentation};

@ -1,9 +1,9 @@
import { IMap } from "./types"; import { IMap } from "../types";
/** /**
* Contains the "information" property for all the Factions, which is just a description of each faction * Contains the "information" property for all the Factions, which is just a description of each faction
*/ */
class FactionInfo { export class FactionInfo {
/** /**
* The multiplier to apply to augmentation base purchase price. * The multiplier to apply to augmentation base purchase price.
*/ */

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

47
src/Faction/Factions.ts Normal file

@ -0,0 +1,47 @@
/**
* Initialization and manipulation of the Factions object, which stores data
* about all Factions in the game
*/
import { Faction } from "./Faction";
import { FactionInfos } from "./FactionInfo";
import { IMap } from "../types";
import { Reviver } from "../../utils/JSONReviver";
export let Factions: IMap<Faction> = {};
export function loadFactions(saveString: string): void {
Factions = JSON.parse(saveString, Reviver);
}
export function AddToFactions(faction: Faction) {
const name: string = faction.name;
Factions[name] = faction;
}
export function factionExists(name: string): boolean {
return Factions.hasOwnProperty(name);
}
export function initFactions(bitNode: number=1) {
for (const name in FactionInfos) {
resetFaction(new Faction(name));
}
}
//Resets a faction during (re-)initialization. Saves the favor in the new
//Faction object and deletes the old Faction Object from "Factions". Then
//reinserts the new Faction object
export function resetFaction(newFactionObject: Faction) {
if (!(newFactionObject instanceof Faction)) {
throw new Error("Invalid argument 'newFactionObject' passed into resetFaction()");
}
const factionName: string = newFactionObject.name;
if (factionExists(factionName)) {
newFactionObject.favor = Factions[factionName].favor;
delete Factions[factionName];
}
AddToFactions(newFactionObject);
}

1
src/Faction/README.md Normal file

@ -0,0 +1 @@
Implementation of Faction-related mechanics

@ -7,8 +7,9 @@ import {gangMemberTasksMetadata} from "./data/gangmembertasks";
import { gangMemberUpgradesMetadata } from "./data/gangmemberupgrades"; import { gangMemberUpgradesMetadata } from "./data/gangmemberupgrades";
import { Engine } from "./engine"; import { Engine } from "./engine";
import {Faction, Factions, import { Faction } from "./Faction/Faction";
displayFactionContent} from "./Faction"; import { Factions } from "./Faction/Factions";
import { displayFactionContent } from "./Faction/FactionHelpers";
import { numeralWrapper } from "./ui/numeralFormat"; import { numeralWrapper } from "./ui/numeralFormat";
import { dialogBoxCreate } from "../utils/DialogBox"; import { dialogBoxCreate } from "../utils/DialogBox";
import { Reviver, Generic_toJSON, import { Reviver, Generic_toJSON,
@ -17,18 +18,24 @@ import {KEY} from "../utils/helpers/keyCodes"
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement"; import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
import { createElement } from "../utils/uiHelpers/createElement"; import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup"; import { createPopup } from "../utils/uiHelpers/createPopup";
import {Page, routing} from "./ui/navigationTracking"; import { Page,
routing } from "./ui/navigationTracking";
import { formatNumber } from "../utils/StringHelperFunctions"; import { formatNumber } from "../utils/StringHelperFunctions";
import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement"; import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
import { removeElement } from "../utils/uiHelpers/removeElement"; import { removeElement } from "../utils/uiHelpers/removeElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById"; import { removeElementById } from "../utils/uiHelpers/removeElementById";
import {yesNoBoxCreate, yesNoTxtInpBoxCreate, import { yesNoBoxCreate,
yesNoBoxGetYesButton, yesNoBoxGetNoButton, yesNoTxtInpBoxCreate,
yesNoTxtInpBoxGetYesButton, yesNoTxtInpBoxGetNoButton, yesNoBoxGetYesButton,
yesNoTxtInpBoxGetInput, yesNoBoxClose, yesNoBoxGetNoButton,
yesNoTxtInpBoxClose, yesNoBoxOpen} from "../utils/YesNoBox"; yesNoTxtInpBoxGetYesButton,
yesNoTxtInpBoxGetNoButton,
yesNoTxtInpBoxGetInput,
yesNoBoxClose,
yesNoTxtInpBoxClose,
yesNoBoxOpen } from "../utils/YesNoBox";
// Constants // Constants
const GangRespectToReputationRatio = 5; // Respect is divided by this to get rep gain const GangRespectToReputationRatio = 5; // Respect is divided by this to get rep gain

14
src/Locations/Cities.ts Normal file

@ -0,0 +1,14 @@
import { IMap } from "../types";
/**
* Display Location Content when visiting somewhere in the World
*/
// tslint:disable-next-line:variable-name
export const Cities: IMap<string> = {
Aevum: "Aevum",
Chongqing: "Chongqing",
Ishima: "Ishima",
NewTokyo: "New Tokyo",
Sector12: "Sector-12",
Volhaven: "Volhaven",
};

@ -1,6 +1,6 @@
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { Engine } from "./engine"; import { Engine } from "./engine";
import {displayFactionContent} from "./Faction"; import { displayFactionContent } from "./Faction/FactionHelpers";
import { Player } from "./Player"; import { Player } from "./Player";
import { dialogBoxCreate } from "../utils/DialogBox"; import { dialogBoxCreate } from "../utils/DialogBox";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners"; import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";

@ -22,9 +22,13 @@ import {calculateHackingChance,
calculateGrowTime, calculateGrowTime,
calculateWeakenTime} from "./Hacking"; calculateWeakenTime} from "./Hacking";
import {AllGangs} from "./Gang"; import {AllGangs} from "./Gang";
import {Factions, Faction, joinFaction, import { Faction } from "./Faction/Faction";
factionExists, purchaseAugmentation} from "./Faction"; import { Factions,
import {getCostOfNextHacknetNode, purchaseHacknet} from "./HacknetNode"; factionExists } from "./Faction/Factions";
import { joinFaction,
purchaseAugmentation } from "./Faction/FactionHelpers";
import { getCostOfNextHacknetNode,
purchaseHacknet } from "./HacknetNode";
import {Locations} from "./Locations"; import {Locations} from "./Locations";
import {Message, Messages} from "./Message"; import {Message, Messages} from "./Message";
import {inMission} from "./Missions"; import {inMission} from "./Missions";

268
src/PersonObjects/Person.ts Normal file

@ -0,0 +1,268 @@
// Base class representing a person-like object
import { BitNodeMultipliers } from "../BitNodeMultipliers";
import { Cities } from "../Locations/Cities";
import { CONSTANTS } from "../Constants";
// Interface for an object that represents the player (PlayerObject)
// Used because at the time of implementation, the PlayerObject
// cant be converted to TypeScript.
//
// Only contains the needed properties for Sleeve implementation
export interface IPlayer {
companyName: string;
factions: string[];
money: any;
gainHackingExp(exp: number): void;
gainStrengthExp(exp: number): void;
gainDefenseExp(exp: number): void;
gainDexterityExp(exp: number): void;
gainAgilityExp(exp: number): void;
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;
}
// Interface for Faction object
// Used because at the time of implementation, the Faction object has not been
// converted to TypeScript
export interface IFaction {
name: string;
playerReputation: number;
}
// Interface that defines a generic object used to track experience/money
// earnings for tasks
export interface ITaskTracker {
hack: number;
str: number;
def: number;
dex: number;
agi: number;
cha: number;
money: number;
}
export function createTaskTracker(): ITaskTracker {
return {
hack: 0,
str: 0,
def: 0,
dex: 0,
agi: 0,
cha: 0,
money: 0,
}
}
export abstract class Person {
/**
* Stats
*/
hacking_skill: number;
strength: number;
defense: number;
dexterity: number;
agility: number;
charisma: number;
hp: number;
max_hp: number;
/**
* Multipliers
*/
hacking_exp: number;
strength_exp: number;
defense_exp: number;
dexterity_exp: number;
agility_exp: number;
charisma_exp: number;
intelligence_exp: number;
hacking_mult: number;
strength_mult: number;
defense_mult: number;
dexterity_mult: number;
agility_mult: number;
charisma_mult: number;
hacking_exp_mult: number;
strength_exp_mult: number;
defense_exp_mult: number;
dexterity_exp_mult: number;
agility_exp_mult: number;
charisma_exp_mult: number;
company_rep_mult: number;
faction_rep_mult: number;
crime_money_mult: number;
crime_success_mult: number;
work_money_mult: number;
/**
* City that the person is in
*/
city: string;
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;
}
/**
* Given an experience amount and stat multiplier, calculates the
* stat level. Stat-agnostic (same formula for every stat)
*/
calculateStat(exp: number, mult: number=1): number {
return Math.max(Math.floor(mult*(32 * Math.log(exp + 534.5) - 200)), 1);
}
/**
* Calculate and return the amount of faction reputation earned per cycle
* when doing Field Work for a faction
*/
getFactionFieldWorkRepGain(): number {
const t = 0.9 * (this.hacking_skill / CONSTANTS.MaxSkillLevel +
this.strength / CONSTANTS.MaxSkillLevel +
this.defense / CONSTANTS.MaxSkillLevel +
this.dexterity / CONSTANTS.MaxSkillLevel +
this.agility / CONSTANTS.MaxSkillLevel +
this.charisma / CONSTANTS.MaxSkillLevel) / 5.5;
return t * this.faction_rep_mult;
}
/**
* Calculate and return the amount of faction reputation earned per cycle
* when doing Hacking Work for a faction
*/
getFactionHackingWorkRepGain(): number {
return this.hacking_skill / CONSTANTS.MaxSkillLevel * this.faction_rep_mult;
}
/**
* Calculate and return the amount of faction reputation earned per cycle
* when doing Security Work for a faction
*/
getFactionSecurityWorkRepGain(): number {
const t = 0.9 * (this.hacking_skill / 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;
}
/**
* Reset all multipliers to 1
*/
resetMultipliers(): void {
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;
}
/**
* Update all stat levels
*/
updateStatLevels(): void {
this.hacking_skill = Math.max(1, Math.floor(this.calculateStat(this.hacking_exp, this.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier)));
this.strength = Math.max(1, Math.floor(this.calculateStat(this.strength_exp, this.strength_mult)));
this.defense = Math.max(1, Math.floor(this.calculateStat(this.defense_exp, this.defense_mult)));
this.dexterity = Math.max(1, Math.floor(this.calculateStat(this.dexterity_exp, this.dexterity_mult)));
this.agility = Math.max(1, Math.floor(this.calculateStat(this.agility_exp, this.agility_mult)));
this.charisma = Math.max(1, Math.floor(this.calculateStat(this.charisma_exp, this.charisma_mult)));
const ratio: number = this.hp / this.max_hp;
this.max_hp = Math.floor(10 + this.defense / 10);
this.hp = Math.round(this.max_hp * ratio);
}
}

@ -0,0 +1,2 @@
Implementation of all Person-type objects, including but not limited to
the "PlayerObject" and Sleeves.

@ -0,0 +1,598 @@
/**
* Sleeves are clones of the player that can be used to perform
* different tasks synchronously.
*
* Each sleeve is its own individual, meaning it has its own stats/exp
*
* Sleeves are unlocked in BitNode-10.
*/
import { SleeveTaskType } from "./SleeveTaskTypesEnum";
import { Person,
IPlayer,
ICrime,
IFaction,
ITaskTracker,
createTaskTracker } from "../Person";
import { BitNodeMultipliers } from "../../BitNodeMultipliers";
import { Cities } from "../../Locations/Cities";
import { Companies } from "../../Company/Companies";
import { Company } from "../../Company/Company";
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";
export class Sleeve extends Person {
/**
* Initiatizes a Sleeve object from a JSON save state.
*/
static fromJSON(value: any): Sleeve {
return Generic_fromJSON(Sleeve, value.data);
}
/**
* Enum value for current task
*/
currentTask: SleeveTaskType = SleeveTaskType.Idle;
/**
* Description of current task. Used only for logging purposes
*/
currentTaskDescription: string = "";
/**
* For what company/faction the current task is assigned to.
* Only applicable when working for faction or company, obviously
*/
currentTaskLocation: string = "";
/**
* Maximum amount of time (in milliseconds) that can be spent on current task.
*/
currentTaskMaxTime: number = 0;
/**
* Milliseconds spent on current task
*/
currentTaskTime: number = 0;
/**
* Keeps track of experience earned for other sleeves
*/
earningsForSleeves: ITaskTracker = createTaskTracker();
/**
* Keeps track of experience + money earned for player
*/
earningsForPlayer: ITaskTracker = createTaskTracker();
/**
* Keeps track of experienced earned in the current task/action
*/
earningsForTask: ITaskTracker = createTaskTracker();
/**
* Keeps track of what type of work sleeve is doing for faction, if applicable
*/
factionWorkType: FactionWorkType = FactionWorkType.None;
/**
* Records experience gain rate for the current task
*/
gainRatesForTask: ITaskTracker = createTaskTracker();
/**
* Keeps track of events/notifications for this sleeve
*/
logs: string[] = [];
/**
* Clone retains memory% of exp upon prestige. If exp would be lower than previously
* kept exp, nothing happens
*/
memory: number = 0;
/**
* Sleeve shock. Number between 1 and 100
* Trauma/shock that comes with being in a sleeve. Experience earned
* is multipled by shock%. This gets applied before synchronization
*/
shock: number = 1;
/**
* Stored number of game "loop" cycles
*/
storedCycles: number = 0;
/**
* Synchronization. Number between 0 and 100
* When experience is earned by sleeve, both the player and the sleeve get
* sync% of the experience earned. Other sleeves get sync^2% of exp
*/
sync: number = 1;
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 {
if (this.currentTask !== SleeveTaskType.Idle) {
this.finishTask();
} else {
this.resetTaskStatus();
}
this.gainRatesForTask.hack = crime.hacking_exp * this.hacking_exp_mult * BitNodeMultipliers.CrimeExpGain;
this.gainRatesForTask.str = crime.strength_exp * this.strength_exp_mult * BitNodeMultipliers.CrimeExpGain;
this.gainRatesForTask.def = crime.defense_exp * this.defense_exp_mult * BitNodeMultipliers.CrimeExpGain;
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.currentTaskMaxTime = crime.time;
this.currentTask = SleeveTaskType.Crime;
}
/**
* Called to stop the current task
*/
finishTask(): void {
if (this.currentTask === SleeveTaskType.Crime) {
} else {
}
this.resetTaskStatus();
}
/**
* Earn experience for any stats (supports multiple)
* This function also handles experience propogating to Player and other sleeves
*/
gainExperience(p: IPlayer, exp: ITaskTracker, numCycles: number=1): ITaskTracker {
// Experience is first multiplied by shock. Then 'synchronization'
// is accounted for
const multFac = (this.shock / 100) * (this.sync / 100) * numCycles;
const pHackExp = exp.hack * multFac;
const pStrExp = exp.str * multFac;
const pDefExp = exp.def * multFac;
const pDexExp = exp.dex * multFac;
const pAgiExp = exp.agi * multFac;
const pChaExp = exp.cha * multFac;
// Experience is gained by both this sleeve and player
if (pHackExp > 0) {
this.hacking_exp += pHackExp;
p.gainHackingExp(pHackExp);
this.earningsForPlayer.hack += pHackExp;
this.earningsForTask.hack += pHackExp;
}
if (pStrExp > 0) {
this.strength_exp += pStrExp;
p.gainStrengthExp(pStrExp);
this.earningsForPlayer.str += pStrExp;
this.earningsForTask.str += pStrExp;
}
if (pDefExp > 0) {
this.defense_exp += pDefExp;
p.gainDefenseExp(pDefExp);
this.earningsForPlayer.def += pDefExp;
this.earningsForTask.dex += pDefExp;
}
if (pDexExp > 0) {
this.dexterity_exp += pDexExp;
p.gainDexterityExp(pDexExp);
this.earningsForPlayer.dex += pDexExp;
this.earningsForTask.dex += pDexExp;
}
if (pAgiExp > 0) {
this.agility_exp += pAgiExp;
p.gainAgilityExp(pAgiExp);
this.earningsForPlayer.agi += pAgiExp;
this.earningsForTask.agi += pAgiExp;
}
if (pChaExp > 0) {
this.charisma_exp += pChaExp;
p.gainCharismaExp(pChaExp);
this.earningsForPlayer.cha += pChaExp;
this.earningsForTask.cha += pChaExp;
}
// Record earnings for other sleeves
this.earningsForSleeves.hack += (pHackExp * (this.sync / 100));
this.earningsForSleeves.str += (pStrExp * (this.sync / 100));
this.earningsForSleeves.def += (pDefExp * (this.sync / 100));
this.earningsForSleeves.dex += (pDexExp * (this.sync / 100));
this.earningsForSleeves.agi += (pAgiExp * (this.sync / 100));
this.earningsForSleeves.cha += (pChaExp * (this.sync / 100));
// Return the experience to be gained by other sleeves
return {
hack: pHackExp * (this.sync / 100),
str: pStrExp * (this.sync / 100),
def: pDefExp * (this.sync / 100),
dex: pDexExp * (this.sync / 100),
agi: pAgiExp * (this.sync / 100),
cha: pChaExp * (this.sync / 100),
money: 0,
}
}
/**
* Earn money for player
*/
gainMoney(p: IPlayer, task: ITaskTracker, numCycles: number=1): void {
p.gainMoney(task.money * numCycles);
}
/**
* Gets reputation gain for the current task
* Only applicable when working for company or faction
*/
getRepGain(): number {
if (this.currentTask === SleeveTaskType.Faction) {
switch (this.factionWorkType) {
case FactionWorkType.Hacking:
return this.getFactionHackingWorkRepGain();
case FactionWorkType.Field:
return this.getFactionFieldWorkRepGain();
case FactionWorkType.Security:
return this.getFactionSecurityWorkRepGain();
default:
console.warn(`Invalid Sleeve.factionWorkType property in Sleeve.getRepGain(): ${this.factionWorkType}`);
return 0;
}
} else if (this.currentTask === SleeveTaskType.Company) {
return 0;
} else {
console.warn(`Sleeve.getRepGain() called for invalid task type: ${this.currentTask}`);
return 0;
}
}
log(entry: string): void {
const MaxLogSize: number = 50;
this.logs.push(entry);
if (this.logs.length > MaxLogSize) {
this.logs.shift();
}
}
/**
* Process loop
* Returns an object containing the amount of experience that should be
* transferred to all other sleeves
*/
process(p: IPlayer, numCycles: number=1): ITaskTracker | null {
// Only process once every second (5 cycles)
const CyclesPerSecond = 1000 / CONSTANTS.MilliPerCycle;
this.storedCycles += numCycles;
if (this.storedCycles < CyclesPerSecond) { return null; }
// Shock gradually goes towards 100
this.shock = Math.max(100, this.shock + (0.0001 * this.storedCycles));
if (this.currentTask === SleeveTaskType.Idle) { return null; }
let time = this.storedCycles * CONSTANTS.MilliPerCycle;
let cyclesUsed = this.storedCycles;
if (this.currentTaskTime + time > this.currentTaskMaxTime) {
time = this.currentTaskMaxTime - this.currentTaskTime;
cyclesUsed = Math.floor(time / CONSTANTS.MilliPerCycle);
if (time < 0 || cyclesUsed < 0) {
console.warn(`Sleeve.process() calculated negative cycle usage`);
time = 0;
cyclesUsed = 0;
}
}
this.currentTaskTime += time;
let retValue: ITaskTracker = createTaskTracker();
switch (this.currentTask) {
case SleeveTaskType.Class:
retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
break;
case SleeveTaskType.Faction:
retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
// TODO REP for both this and company
const fac = Factions[this.currentTaskLocation];
if (!(fac instanceof Faction)) {
console.error(`Invalid faction for Sleeve task: ${this.currentTaskLocation}`);
break;
}
break;
case SleeveTaskType.Company:
retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
break;
case SleeveTaskType.Recovery:
this.shock = Math.max(100, this.shock + (0.001 * this.storedCycles));
break;
case SleeveTaskType.Sync:
this.sync = Math.max(100, this.sync + (0.001 * this.storedCycles));
break;
default:
break;
}
if (this.currentTaskMaxTime !== 0 && this.currentTaskTime >= this.currentTaskMaxTime) {
this.finishTask();
}
this.storedCycles -= cyclesUsed;
// TODO Finish this
return retValue;
}
/**
* Resets all parameters used to keep information about the current task
*/
resetTaskStatus(): void {
this.earningsForTask = createTaskTracker();
this.gainRatesForTask = createTaskTracker();
this.currentTask = SleeveTaskType.Idle;
this.currentTaskTime = 0;
this.currentTaskMaxTime = 0;
this.factionWorkType = FactionWorkType.None;
}
/**
* Take a course at a university
*/
takeUniversityCourse(p: IPlayer, universityName: string, className: string): boolean {
if (this.currentTask !== SleeveTaskType.Idle) {
this.finishTask();
} else {
this.resetTaskStatus();
}
// Set exp/money multipliers based on which university.
// Also check that the sleeve is in the right city
let costMult: number = 1;
let expMult: number = 1;
switch (universityName.toLowerCase()) {
case Locations.AevumSummitUniversity.toLowerCase():
if (this.city !== Cities.Aevum) { return false; }
costMult = 4;
expMult = 3;
break;
case Locations.Sector12RothmanUniversity.toLowerCase():
if (this.city !== Cities.Sector12) { return false; }
costMult = 3;
expMult = 2;
break;
case Locations.VolhavenZBInstituteOfTechnology.toLowerCase():
if (this.city !== Cities.Volhaven) { return false; }
costMult = 5;
expMult = 4;
break;
default:
return false;
}
// Number of game cycles in a second
const cps: number = 1000 / CONSTANTS.MilliPerCycle;
// Set experience/money gains based on class
// TODO Refactor University Courses into its own class or something
const baseStudyComputerScienceExp: number = 0.5;
const baseDataStructuresExp: number = 1;
const baseNetworksExp: number = 2;
const baseAlgorithmsExp: number = 4;
const baseManagementExp: number = 2;
const baseLeadershipExp: number = 4;
switch (className.toLowerCase()) {
case "study computer science":
this.gainRatesForTask.hack = (baseStudyComputerScienceExp * expMult * this.hacking_exp_mult);
break;
case "data structures":
this.gainRatesForTask.hack = (baseDataStructuresExp * expMult * this.hacking_exp_mult);
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassDataStructuresBaseCost * costMult);
break;
case "networks":
this.gainRatesForTask.hack = (baseNetworksExp * expMult * this.hacking_exp_mult);
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassNetworksBaseCost * costMult);
break;
case "algorithms":
this.gainRatesForTask.hack = (baseAlgorithmsExp * expMult * this.hacking_exp_mult);
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassAlgorithmsBaseCost * costMult);
break;
case "management":
this.gainRatesForTask.cha = (baseManagementExp * expMult * this.charisma_exp_mult);
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassManagementBaseCost * costMult);
break;
case "leadership":
this.gainRatesForTask.cha = (baseLeadershipExp * expMult * this.charisma_exp_mult);
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassLeadershipBaseCost * costMult);
break;
default:
return false;
}
this.currentTask = SleeveTaskType.Class;
return true;
}
/**
* Travel to another City. Costs money from player
*/
travel(p: IPlayer, newCity: string): boolean {
if (Cities[newCity] == null) {
console.error(`Invalid city ${newCity} passed into Sleeve.travel()`);
return false;
}
p.loseMoney(CONSTANTS.TravelCost);
this.city = newCity;
return true;
}
/**
* Work for a company
*/
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)) {
return false;
}
if (this.currentTask !== SleeveTaskType.Idle) {
this.finishTask();
} else {
this.resetTaskStatus();
}
// Set type of work (hacking/field/security), and the experience gains
const sanitizedWorkType: string = workType.toLowerCase();
if (sanitizedWorkType.includes("hack")) {
this.factionWorkType = FactionWorkType.Hacking;
this.gainRatesForTask.hack = .15 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
} else if (sanitizedWorkType.includes("field")) {
this.factionWorkType = FactionWorkType.Field;
this.gainRatesForTask.hack = .1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.str = .1 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.def = .1 * this.defense_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.dex = .1 * this.dexterity_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.agi = .1 * this.agility_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.cha = .1 * this.charisma_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
} else if (sanitizedWorkType.includes("security")) {
this.factionWorkType = FactionWorkType.Security;
this.gainRatesForTask.hack = .1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.str = .15 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.def = .15 * this.defense_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.dex = .15 * this.dexterity_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
this.gainRatesForTask.agi = .15 * this.agility_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
} else {
return false;
}
this.currentTaskLocation = factionName;
this.currentTask = SleeveTaskType.Faction;
return true;
}
/**
* Begin a gym workout task
*/
workoutAtGym(p: IPlayer, gymName: string, stat: string): boolean {
if (this.currentTask !== SleeveTaskType.Idle) {
this.finishTask();
} else {
this.resetTaskStatus();
}
// Set exp/money multipliers based on which university.
// Also check that the sleeve is in the right city
let costMult: number = 1;
let expMult: number = 1;
switch (gymName.toLowerCase()) {
case Locations.AevumCrushFitnessGym.toLowerCase():
if (this.city != Cities.Aevum) { return false; }
costMult = 3;
expMult = 2;
break;
case Locations.AevumSnapFitnessGym.toLowerCase():
if (this.city != Cities.Aevum) { return false; }
costMult = 10;
expMult = 5;
break;
case Locations.Sector12IronGym.toLowerCase():
if (this.city != Cities.Sector12) { return false; }
costMult = 1;
expMult = 1;
break;
case Locations.Sector12PowerhouseGym.toLowerCase():
if (this.city != Cities.Sector12) { return false; }
costMult = 20;
expMult = 10;
break;
case Locations.VolhavenMilleniumFitnessGym:
if (this.city != Cities.Volhaven) { return false; }
costMult = 7;
expMult = 4;
break;
default:
return false;
}
// Number of game cycles in a second
const cps = 1000 / CONSTANTS.MilliPerCycle;
// Set experience/money gains based on class
// TODO Refactor University Courses into its own class or something
const baseGymExp: number = 1;
const sanitizedStat: string = stat.toLowerCase();
// Set cost
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassGymBaseCost * costMult);
// Set stat gain rate
if (sanitizedStat.includes("str")) {
this.gainRatesForTask.str = (baseGymExp * expMult);
} else if (sanitizedStat.includes("def")) {
this.gainRatesForTask.def = (baseGymExp * expMult);
} else if (sanitizedStat.includes("dex")) {
this.gainRatesForTask.dex = (baseGymExp * expMult);
} else if (sanitizedStat.includes("agi")) {
this.gainRatesForTask.agi = (baseGymExp * expMult);
} else {
return false;
}
this.currentTask = SleeveTaskType.Class;
return true;
}
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): any {
return Generic_toJSON("Sleeve", this);
}
}
Reviver.constructors.Sleeve = Sleeve;

@ -0,0 +1,12 @@
/**
* Enum for different types of tasks that a Sleeve can perform
*/
export enum SleeveTaskType {
Class,
Company,
Crime,
Faction,
Idle,
Recovery,
Sync,
}

@ -14,8 +14,9 @@ import {Corporation} from "./CompanyManagement";
import {Programs} from "./CreateProgram"; import {Programs} from "./CreateProgram";
import {determineCrimeSuccess, Crimes} from "./Crimes"; import {determineCrimeSuccess, Crimes} from "./Crimes";
import {Engine} from "./engine"; import {Engine} from "./engine";
import {Factions, Faction, import { Faction } from "./Faction/Faction";
displayFactionContent} from "./Faction"; import { Factions } from "./Faction/Factions";
import { displayFactionContent } from "./Faction/FactionHelpers";
import {Gang, resetGangs} from "./Gang"; import {Gang, resetGangs} from "./Gang";
import {Locations} from "./Locations"; import {Locations} from "./Locations";
import {hasBn11SF, hasWallStreetSF,hasAISF} from "./NetscriptFunctions"; import {hasBn11SF, hasWallStreetSF,hasAISF} from "./NetscriptFunctions";
@ -41,10 +42,10 @@ function PlayerObject() {
//Combat stats //Combat stats
this.hp = 10; this.hp = 10;
this.max_hp = 10; this.max_hp = 10;
this.strength = 1; //Damage dealt this.strength = 1;
this.defense = 1; //Damage received this.defense = 1;
this.dexterity = 1; //Accuracy this.dexterity = 1;
this.agility = 1; //Dodge % this.agility = 1;
//Labor stats //Labor stats
this.charisma = 1; this.charisma = 1;
@ -594,7 +595,7 @@ PlayerObject.prototype.gainIntelligenceExp = function(exp) {
//Given a string expression like "str" or "strength", returns the given stat //Given a string expression like "str" or "strength", returns the given stat
PlayerObject.prototype.queryStatFromString = function(str) { PlayerObject.prototype.queryStatFromString = function(str) {
var tempStr = str.toLowerCase(); const tempStr = str.toLowerCase();
if (tempStr.includes("hack")) {return Player.hacking_skill;} if (tempStr.includes("hack")) {return Player.hacking_skill;}
if (tempStr.includes("str")) {return Player.strength;} if (tempStr.includes("str")) {return Player.strength;}
if (tempStr.includes("def")) {return Player.defense;} if (tempStr.includes("def")) {return Player.defense;}

@ -7,8 +7,10 @@ import {writeCinematicText} from "./CinematicText";
import {Companies, initCompanies} from "./Company/Companies"; import {Companies, initCompanies} from "./Company/Companies";
import {Programs} from "./CreateProgram"; import {Programs} from "./CreateProgram";
import {Engine} from "./engine"; import {Engine} from "./engine";
import {Factions, Faction, initFactions, import { Faction } from "./Faction/Faction";
joinFaction} from "./Faction"; import { Factions,
initFactions } from "./Faction/Factions";
import { joinFaction } from "./Faction/FactionHelpers";
import {deleteGangDisplayContent} from "./Gang"; import {deleteGangDisplayContent} from "./Gang";
import {Locations} from "./Location"; import {Locations} from "./Location";
import {initMessages, Messages, Message} from "./Message"; import {initMessages, Messages, Message} from "./Message";

@ -4,8 +4,9 @@ import {Companies, loadCompanies} from "./Company/Companies";
import {CompanyPosition} from "./Company/CompanyPosition"; import {CompanyPosition} from "./Company/CompanyPosition";
import {CONSTANTS} from "./Constants"; import {CONSTANTS} from "./Constants";
import {Engine} from "./engine"; import {Engine} from "./engine";
import {loadFactions, Factions, import { Factions,
processPassiveFactionRepGain} from "./Faction"; loadFactions } from "./Faction/Factions";
import { processPassiveFactionRepGain } from "./Faction/FactionHelpers";
import {FconfSettings, loadFconf} from "./Fconf"; import {FconfSettings, loadFconf} from "./Fconf";
import {loadAllGangs, AllGangs} from "./Gang"; import {loadAllGangs, AllGangs} from "./Gang";
import {processAllHacknetNodeEarnings} from "./HacknetNode"; import {processAllHacknetNodeEarnings} from "./HacknetNode";

@ -34,9 +34,10 @@ import {displayCreateProgramContent,
initCreateProgramButtons, initCreateProgramButtons,
Programs} from "./CreateProgram"; Programs} from "./CreateProgram";
import {createDevMenu, closeDevMenu} from "./DevMenu"; import {createDevMenu, closeDevMenu} from "./DevMenu";
import { Factions, initFactions } from "./Faction/Factions";
import { displayFactionContent, joinFaction, import { displayFactionContent, joinFaction,
processPassiveFactionRepGain, Factions, processPassiveFactionRepGain,
inviteToFaction, initFactions} from "./Faction"; inviteToFaction } from "./Faction/FactionHelpers";
import {FconfSettings} from "./Fconf"; import {FconfSettings} from "./Fconf";
import {displayLocationContent, import {displayLocationContent,
initLocationButtons} from "./Location"; initLocationButtons} from "./Location";

@ -1,4 +1,4 @@
import {joinFaction} from "../src/Faction"; import {joinFaction} from "../src/Faction/FactionHelpers";
import {Engine} from "../src/engine"; import {Engine} from "../src/engine";
import {Player} from "../src/Player"; import {Player} from "../src/Player";
import {clearEventListeners} from "./uiHelpers/clearEventListeners"; import {clearEventListeners} from "./uiHelpers/clearEventListeners";

@ -1,6 +1,7 @@
import { BitNodeMultipliers } from "../src/BitNodeMultipliers"; import { BitNodeMultipliers } from "../src/BitNodeMultipliers";
import { CONSTANTS } from "../src/Constants"; import { CONSTANTS } from "../src/Constants";
import {Factions, Faction} from "../src/Faction"; import { Faction } from "../src/Faction/Faction";
import { Factions } from "../src/Faction/Factions";
import { Player } from "../src/Player"; import { Player } from "../src/Player";
import { dialogBoxCreate } from "./DialogBox"; import { dialogBoxCreate } from "./DialogBox";
import { clearEventListeners } from "./uiHelpers/clearEventListeners"; import { clearEventListeners } from "./uiHelpers/clearEventListeners";