mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-05-02 13:57:05 +02:00
Fixed bugs with Sleeve mechanics. Updated documentation to use RTD theme
This commit is contained in:
@@ -47,17 +47,13 @@ export class Sleeve extends Person {
|
||||
*/
|
||||
currentTask: SleeveTaskType = SleeveTaskType.Idle;
|
||||
|
||||
/**
|
||||
* Description of current task. Used only for logging purposes
|
||||
*/
|
||||
currentTaskDescription: string = "";
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Crime: Money earned if successful
|
||||
* Class/Gym: Name of university/gym
|
||||
*/
|
||||
currentTaskLocation: string = "";
|
||||
|
||||
@@ -135,7 +131,8 @@ export class Sleeve extends Person {
|
||||
/**
|
||||
* Commit crimes
|
||||
*/
|
||||
commitCrime(p: IPlayer, crime: Crime): void {
|
||||
commitCrime(p: IPlayer, crime: Crime): boolean {
|
||||
if (!(crime instanceof Crime)) { return false; }
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
@@ -150,6 +147,8 @@ export class Sleeve extends Person {
|
||||
this.gainRatesForTask.cha = crime.charisma_exp * this.charisma_exp_mult * BitNodeMultipliers.CrimeExpGain;
|
||||
this.gainRatesForTask.money = crime.money * this.crime_money_mult * BitNodeMultipliers.CrimeMoney;
|
||||
|
||||
this.currentTaskLocation = String(this.gainRatesForTask.money);
|
||||
|
||||
// We'll determine success now and adjust the earnings accordingly
|
||||
if (Math.random() < crime.successRate(p)) {
|
||||
this.gainRatesForTask.hack *= 2;
|
||||
@@ -162,27 +161,35 @@ export class Sleeve extends Person {
|
||||
this.gainRatesForTask.money = 0;
|
||||
}
|
||||
|
||||
|
||||
this.currentTaskMaxTime = crime.time;
|
||||
this.currentTask = SleeveTaskType.Crime;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to stop the current task
|
||||
*/
|
||||
finishTask(p: IPlayer): void {
|
||||
finishTask(p: IPlayer): ITaskTracker {
|
||||
let retValue: ITaskTracker = createTaskTracker(); // Amount of exp to be gained by other sleeves
|
||||
|
||||
if (this.currentTask === SleeveTaskType.Crime) {
|
||||
// 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);
|
||||
|
||||
// Do not reset task to IDLE
|
||||
this.currentTaskTime = 0;
|
||||
return retValue;
|
||||
}
|
||||
} else {
|
||||
// For other crimes... I dont think anything else needs to be done
|
||||
}
|
||||
|
||||
this.resetTaskStatus();
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,15 +274,17 @@ export class Sleeve extends Person {
|
||||
* Earn money for player
|
||||
*/
|
||||
gainMoney(p: IPlayer, task: ITaskTracker, numCycles: number=1): void {
|
||||
this.earningsForPlayer.money += (task.money * numCycles);
|
||||
p.gainMoney(task.money * numCycles);
|
||||
const gain: number = (task.money * numCycles);
|
||||
this.earningsForTask.money += gain;
|
||||
this.earningsForPlayer.money += gain;
|
||||
p.gainMoney(gain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets reputation gain for the current task
|
||||
* Only applicable when working for company or faction
|
||||
*/
|
||||
getRepGain(): number {
|
||||
getRepGain(p: IPlayer): number {
|
||||
if (this.currentTask === SleeveTaskType.Faction) {
|
||||
switch (this.factionWorkType) {
|
||||
case FactionWorkType.Hacking:
|
||||
@@ -289,7 +298,25 @@ export class Sleeve extends Person {
|
||||
return 0;
|
||||
}
|
||||
} else if (this.currentTask === SleeveTaskType.Company) {
|
||||
return 0; // TODO
|
||||
const companyName: string = this.currentTaskLocation;
|
||||
const company: Company | null = Companies[companyName];
|
||||
if (company == null) {
|
||||
console.error(`Invalid company found when trying to calculate rep gain: ${companyName}`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const companyPosition: CompanyPosition | null = CompanyPositions[p.jobs[companyName]];
|
||||
if (companyPosition == null) {
|
||||
console.error(`Invalid company position name found when trying to calculate rep gain: ${p.jobs[companyName]}`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const jobPerformance: number = companyPosition!.calculateJobPerformance(this.hacking_skill, this.strength,
|
||||
this.defense, this.dexterity,
|
||||
this.agility, this.charisma);
|
||||
const favorMult = 1 + (company!.favor / 100);
|
||||
|
||||
return jobPerformance * this.company_rep_mult * favorMult;
|
||||
} else {
|
||||
console.warn(`Sleeve.getRepGain() called for invalid task type: ${this.currentTask}`);
|
||||
return 0;
|
||||
@@ -315,14 +342,9 @@ export class Sleeve extends Person {
|
||||
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) {
|
||||
if (this.currentTaskMaxTime !== 0 && this.currentTaskTime + time > this.currentTaskMaxTime) {
|
||||
time = this.currentTaskMaxTime - this.currentTaskTime;
|
||||
cyclesUsed = Math.floor(time / CONSTANTS.MilliPerCycle);
|
||||
|
||||
@@ -334,9 +356,15 @@ export class Sleeve extends Person {
|
||||
}
|
||||
this.currentTaskTime += time;
|
||||
|
||||
// Shock gradually goes towards 100
|
||||
this.shock = Math.min(100, this.shock + (0.0001 * this.storedCycles));
|
||||
|
||||
let retValue: ITaskTracker = createTaskTracker();
|
||||
switch (this.currentTask) {
|
||||
case SleeveTaskType.Idle:
|
||||
break;
|
||||
case SleeveTaskType.Class:
|
||||
case SleeveTaskType.Gym:
|
||||
retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
|
||||
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
|
||||
break;
|
||||
@@ -351,7 +379,7 @@ export class Sleeve extends Person {
|
||||
break;
|
||||
}
|
||||
|
||||
fac.playerReputation += (this.getRepGain() * cyclesUsed);
|
||||
fac.playerReputation += (this.getRepGain(p) * cyclesUsed);
|
||||
break;
|
||||
case SleeveTaskType.Company:
|
||||
retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
|
||||
@@ -363,27 +391,31 @@ export class Sleeve extends Person {
|
||||
break;
|
||||
}
|
||||
|
||||
company.playerReputation *= (this.getRepGain() * cyclesUsed);
|
||||
company!.playerReputation += (this.getRepGain(p) * cyclesUsed);
|
||||
break;
|
||||
case SleeveTaskType.Recovery:
|
||||
this.shock = Math.max(100, this.shock + (0.001 * this.storedCycles));
|
||||
this.shock = Math.min(100, this.shock + (0.0001 * cyclesUsed));
|
||||
break;
|
||||
case SleeveTaskType.Sync:
|
||||
this.sync = Math.max(100, this.sync + (0.001 * this.storedCycles));
|
||||
this.sync = Math.min(100, this.sync + (0.0001 * cyclesUsed));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.currentTaskMaxTime !== 0 && this.currentTaskTime >= this.currentTaskMaxTime) {
|
||||
this.finishTask(p);
|
||||
if (this.currentTask === SleeveTaskType.Crime) {
|
||||
retValue = this.finishTask(p);
|
||||
} else {
|
||||
this.finishTask(p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.updateStatLevels();
|
||||
|
||||
this.storedCycles -= cyclesUsed;
|
||||
|
||||
// TODO Finish this
|
||||
return retValue;
|
||||
}
|
||||
|
||||
@@ -416,16 +448,19 @@ export class Sleeve extends Person {
|
||||
switch (universityName.toLowerCase()) {
|
||||
case Locations.AevumSummitUniversity.toLowerCase():
|
||||
if (this.city !== Cities.Aevum) { return false; }
|
||||
this.currentTaskLocation = Locations.AevumSummitUniversity;
|
||||
costMult = 4;
|
||||
expMult = 3;
|
||||
break;
|
||||
case Locations.Sector12RothmanUniversity.toLowerCase():
|
||||
if (this.city !== Cities.Sector12) { return false; }
|
||||
this.currentTaskLocation = Locations.Sector12RothmanUniversity;
|
||||
costMult = 3;
|
||||
expMult = 2;
|
||||
break;
|
||||
case Locations.VolhavenZBInstituteOfTechnology.toLowerCase():
|
||||
if (this.city !== Cities.Volhaven) { return false; }
|
||||
this.currentTaskLocation = Locations.VolhavenZBInstituteOfTechnology;
|
||||
costMult = 5;
|
||||
expMult = 4;
|
||||
break;
|
||||
@@ -433,9 +468,6 @@ export class Sleeve extends Person {
|
||||
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;
|
||||
@@ -511,7 +543,10 @@ export class Sleeve extends Person {
|
||||
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.money = companyPosition.baseSalary *
|
||||
company.salaryMultiplier *
|
||||
this.work_money_mult *
|
||||
BitNodeMultipliers.CompanyWorkMoney;
|
||||
this.gainRatesForTask.hack = companyPosition.hackingExpGain *
|
||||
company.expMultiplier *
|
||||
this.hacking_exp_mult *
|
||||
@@ -539,6 +574,7 @@ export class Sleeve extends Person {
|
||||
|
||||
this.currentTaskLocation = companyName;
|
||||
this.currentTask = SleeveTaskType.Company;
|
||||
this.currentTaskMaxTime = CONSTANTS.MillisecondsPer8Hours;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -585,6 +621,7 @@ export class Sleeve extends Person {
|
||||
|
||||
this.currentTaskLocation = factionName;
|
||||
this.currentTask = SleeveTaskType.Faction;
|
||||
this.currentTaskMaxTime = CONSTANTS.MillisecondsPer20Hours;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -606,26 +643,31 @@ export class Sleeve extends Person {
|
||||
switch (gymName.toLowerCase()) {
|
||||
case Locations.AevumCrushFitnessGym.toLowerCase():
|
||||
if (this.city != Cities.Aevum) { return false; }
|
||||
this.currentTaskLocation = Locations.AevumCrushFitnessGym;
|
||||
costMult = 3;
|
||||
expMult = 2;
|
||||
break;
|
||||
case Locations.AevumSnapFitnessGym.toLowerCase():
|
||||
if (this.city != Cities.Aevum) { return false; }
|
||||
this.currentTaskLocation = Locations.AevumSnapFitnessGym;
|
||||
costMult = 10;
|
||||
expMult = 5;
|
||||
break;
|
||||
case Locations.Sector12IronGym.toLowerCase():
|
||||
if (this.city != Cities.Sector12) { return false; }
|
||||
this.currentTaskLocation = Locations.Sector12IronGym;
|
||||
costMult = 1;
|
||||
expMult = 1;
|
||||
break;
|
||||
case Locations.Sector12PowerhouseGym.toLowerCase():
|
||||
if (this.city != Cities.Sector12) { return false; }
|
||||
this.currentTaskLocation = Locations.Sector12PowerhouseGym;
|
||||
costMult = 20;
|
||||
expMult = 10;
|
||||
break;
|
||||
case Locations.VolhavenMilleniumFitnessGym:
|
||||
if (this.city != Cities.Volhaven) { return false; }
|
||||
this.currentTaskLocation = Locations.VolhavenMilleniumFitnessGym;
|
||||
costMult = 7;
|
||||
expMult = 4;
|
||||
break;
|
||||
@@ -633,9 +675,6 @@ export class Sleeve extends Person {
|
||||
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;
|
||||
@@ -657,7 +696,7 @@ export class Sleeve extends Person {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.currentTask = SleeveTaskType.Class;
|
||||
this.currentTask = SleeveTaskType.Gym;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
* Enum for different types of tasks that a Sleeve can perform
|
||||
*/
|
||||
export enum SleeveTaskType {
|
||||
Class,
|
||||
Company,
|
||||
Crime,
|
||||
Faction,
|
||||
// Same Order as selectable order in UI
|
||||
Idle,
|
||||
Company,
|
||||
Faction,
|
||||
Crime,
|
||||
Class,
|
||||
Gym,
|
||||
Recovery,
|
||||
Sync,
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
import { Sleeve } from "./Sleeve";
|
||||
import { SleeveTaskType } from "./SleeveTaskTypesEnum";
|
||||
import { SleeveFaq } from "./data/SleeveFaq";
|
||||
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
@@ -11,14 +12,13 @@ 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 { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||
import { exceptionAlert } from "../../../utils/helpers/exceptionAlert";
|
||||
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
@@ -26,35 +26,39 @@ import { createOptionElement } from "../../../utils/uiHelpers/createOptionElemen
|
||||
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: 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,
|
||||
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;
|
||||
taskProgressBar: HTMLElement | null;
|
||||
earningsPanel: HTMLElement | null;
|
||||
currentEarningsInfo: HTMLElement | null;
|
||||
totalEarningsButton: HTMLElement | null;
|
||||
}
|
||||
|
||||
// Object that keeps track of all DOM elements for the entire Sleeve UI
|
||||
interface IPageUIElems {
|
||||
container: HTMLElement | null;
|
||||
info: HTMLElement | null,
|
||||
sleeveList: HTMLElement | null,
|
||||
sleeves: ISleeveUIElems[] | null,
|
||||
docButton: HTMLElement | null;
|
||||
faqButton: HTMLElement | null;
|
||||
info: HTMLElement | null;
|
||||
sleeveList: HTMLElement | null;
|
||||
sleeves: ISleeveUIElems[] | null;
|
||||
}
|
||||
|
||||
const UIElems: IPageUIElems = {
|
||||
container: null,
|
||||
docButton: null,
|
||||
faqButton: null,
|
||||
info: null,
|
||||
sleeveList: null,
|
||||
sleeves: null,
|
||||
@@ -75,11 +79,26 @@ export function createSleevesPage(p: IPlayer) {
|
||||
});
|
||||
|
||||
UIElems.info = createElement("p", {
|
||||
display: "inline-block",
|
||||
innerText: "Duplicate Sleeves are MK-V Synthoids (synthetic androids) into which your " +
|
||||
"consciousness has copied. In other words, these Synthoids contain " +
|
||||
class: "sleeves-page-info",
|
||||
innerHTML: "Duplicate Sleeves are MK-V Synthoids (synthetic androids) into which your " +
|
||||
"consciousness has been copied. In other words, these Synthoids contain " +
|
||||
"a perfect duplicate of your mind.<br><br>" +
|
||||
"Sleeves can be used to perform different tasks synchronously.",
|
||||
"Sleeves can be used to perform different tasks synchronously.<br><br>",
|
||||
});
|
||||
|
||||
UIElems.faqButton = createElement("button", {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
innerText: "FAQ",
|
||||
clickListener: () => {
|
||||
dialogBoxCreate(SleeveFaq, false);
|
||||
}
|
||||
});
|
||||
|
||||
UIElems.docButton = createElement("button", {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
innerText: "Documentation",
|
||||
});
|
||||
|
||||
UIElems.sleeveList = createElement("ul");
|
||||
@@ -93,6 +112,7 @@ export function createSleevesPage(p: IPlayer) {
|
||||
}
|
||||
|
||||
UIElems.container.appendChild(UIElems.info);
|
||||
UIElems.container.appendChild(UIElems.faqButton);
|
||||
UIElems.container.appendChild(UIElems.sleeveList);
|
||||
|
||||
document.getElementById("entire-game-container")!.appendChild(UIElems.container);
|
||||
@@ -104,10 +124,23 @@ export function createSleevesPage(p: IPlayer) {
|
||||
// Updates the UI for the entire Sleeves page
|
||||
export function updateSleevesPage() {
|
||||
if (!routing.isOn(Page.Sleeves)) { return; }
|
||||
|
||||
try {
|
||||
for (let i = 0; i < playerRef!.sleeves.length; ++i) {
|
||||
const sleeve: Sleeve = playerRef!.sleeves[i];
|
||||
const elems: ISleeveUIElems = UIElems.sleeves![i];
|
||||
updateSleeveUi(sleeve!, elems!);
|
||||
}
|
||||
} catch(e) {
|
||||
exceptionAlert(e);
|
||||
}
|
||||
}
|
||||
|
||||
export function clearSleevesPage() {
|
||||
removeElement(UIElems.container);
|
||||
if (UIElems.container instanceof HTMLElement) {
|
||||
removeElement(UIElems.container);
|
||||
}
|
||||
|
||||
for (const prop in UIElems) {
|
||||
(<any>UIElems)[prop] = null;
|
||||
}
|
||||
@@ -129,6 +162,7 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
taskDetailsSelector2: null,
|
||||
taskDescription: null,
|
||||
taskSetButton: null,
|
||||
taskProgressBar: null,
|
||||
earningsPanel: null,
|
||||
currentEarningsInfo: null,
|
||||
totalEarningsButton: null,
|
||||
@@ -141,7 +175,7 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
display: "block",
|
||||
});
|
||||
|
||||
elems.statsPanel = createElement("div", { class: "sleeve-panel" });
|
||||
elems.statsPanel = createElement("div", { class: "sleeve-panel", width: "25%" });
|
||||
elems.stats = createElement("p", { class: "sleeve-stats-text" });
|
||||
elems.moreStatsButton = createElement("button", {
|
||||
class: "std-button",
|
||||
@@ -181,7 +215,7 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
elems.statsPanel.appendChild(elems.stats);
|
||||
elems.statsPanel.appendChild(elems.moreStatsButton);
|
||||
|
||||
elems.taskPanel = createElement("div", { class: "sleeve-panel" });
|
||||
elems.taskPanel = createElement("div", { class: "sleeve-panel", width: "40%" });
|
||||
elems.taskSelector = createElement("select") as HTMLSelectElement;
|
||||
elems.taskSelector.add(createOptionElement("------"));
|
||||
elems.taskSelector.add(createOptionElement("Work for Company"));
|
||||
@@ -194,10 +228,13 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
elems.taskSelector.addEventListener("change", () => {
|
||||
updateSleeveTaskSelector(sleeve, elems, allSleeves);
|
||||
});
|
||||
// TODO Set initial value for task selector
|
||||
elems.taskDetailsSelector = createElement("select") as HTMLSelectElement;
|
||||
elems.taskDetailsSelector2 = createElement("select") as HTMLSelectElement;
|
||||
elems.taskDescription = createElement("p");
|
||||
elems.taskProgressBar = createElement("p");
|
||||
elems.taskSelector.selectedIndex = sleeve.currentTask; // Set initial value for Task Selector
|
||||
elems.taskSelector.dispatchEvent(new Event('change'));
|
||||
updateSleeveTaskDescription(sleeve, elems);
|
||||
elems.taskSetButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Set Task",
|
||||
@@ -207,10 +244,12 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
});
|
||||
elems.taskPanel.appendChild(elems.taskSelector);
|
||||
elems.taskPanel.appendChild(elems.taskDetailsSelector);
|
||||
elems.taskPanel.appendChild(elems.taskDetailsSelector2);
|
||||
elems.taskPanel.appendChild(elems.taskSetButton);
|
||||
elems.taskPanel.appendChild(elems.taskDescription);
|
||||
elems.taskPanel.appendChild(elems.taskProgressBar);
|
||||
|
||||
elems.earningsPanel = createElement("div", { class: "sleeve-panel" });
|
||||
elems.earningsPanel = createElement("div", { class: "sleeve-panel", width: "35%" });
|
||||
elems.currentEarningsInfo = createElement("p");
|
||||
elems.totalEarningsButton = createElement("button", {
|
||||
class: "std-button",
|
||||
@@ -218,23 +257,23 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
clickListener: () => {
|
||||
dialogBoxCreate(
|
||||
[
|
||||
"<h2><u>Total Earnings for Current Task:</u></h2>",
|
||||
"<h2><u>Earnings for Current Task:</u></h2>",
|
||||
`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)}`,
|
||||
"<h2><u>Earnings for Host Consciousness:</u></h2>",
|
||||
`Charisma Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.cha)}<br>`,
|
||||
"<h2><u>Total Earnings for Host Consciousness:</u></h2>",
|
||||
`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)}`,
|
||||
"<h2><u>Earnings for Other Sleeves:</u></h2>",
|
||||
`Charisma Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.cha)}<br>`,
|
||||
"<h2><u>Total Earnings for Other Sleeves:</u></h2>",
|
||||
`Money: ${numeralWrapper.formatMoney(sleeve.earningsForSleeves.money)}`,
|
||||
`Hacking Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.hack)}`,
|
||||
`Strength Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.str)}`,
|
||||
@@ -247,6 +286,15 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
}
|
||||
});
|
||||
|
||||
elems.earningsPanel.appendChild(elems.currentEarningsInfo);
|
||||
elems.earningsPanel.appendChild(elems.totalEarningsButton);
|
||||
|
||||
updateSleeveUi(sleeve, elems);
|
||||
|
||||
elems.container.appendChild(elems.statsPanel);
|
||||
elems.container.appendChild(elems.taskPanel);
|
||||
elems.container.appendChild(elems.earningsPanel);
|
||||
|
||||
return elems;
|
||||
}
|
||||
|
||||
@@ -261,12 +309,19 @@ function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) {
|
||||
`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")}<br>`,
|
||||
`Shock: ${numeralWrapper.format(100 - sleeve.shock, "0,0")}`,
|
||||
`Synchronization: ${numeralWrapper.format(sleeve.sync, "0,0")}`].join("<br>");
|
||||
`Shock: ${numeralWrapper.format(100 - sleeve.shock, "0,0.000")}`,
|
||||
`Sync: ${numeralWrapper.format(sleeve.sync, "0,0.000")}`].join("<br>");
|
||||
|
||||
let repGainText: string = "";
|
||||
if (sleeve.currentTask === SleeveTaskType.Company || sleeve.currentTask === SleeveTaskType.Faction) {
|
||||
const repGain: number = sleeve.getRepGain(playerRef!);
|
||||
repGainText = `Reputation: ${numeralWrapper.format(5 * repGain, "0.00")} / s`
|
||||
}
|
||||
|
||||
if (sleeve.currentTask === SleeveTaskType.Crime) {
|
||||
elems.currentEarningsInfo!.innerHTML = [
|
||||
`Money: ${numeralWrapper.formatMoney(sleeve.gainRatesForTask.money)} if successful`,
|
||||
`Earnings (Pre-Synchronization):`,
|
||||
`Money: ${numeralWrapper.formatMoney(parseFloat(sleeve.currentTaskLocation))} 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)`,
|
||||
@@ -274,16 +329,26 @@ function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) {
|
||||
`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("<br>");
|
||||
|
||||
elems.taskProgressBar!.innerText = createProgressBarText({
|
||||
progress: sleeve.currentTaskTime / sleeve.currentTaskMaxTime,
|
||||
totalTicks: 25,
|
||||
});
|
||||
} 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("<br>");
|
||||
const lines = [
|
||||
`Earnings (Pre-Synchronization):`,
|
||||
`Money: ${numeralWrapper.formatMoney(5 * sleeve.gainRatesForTask.money)} / s`,
|
||||
`Hacking Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.hack, "0.00")} / s`,
|
||||
`Strength Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.str, "0.00")} / s`,
|
||||
`Defense Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.def, "0.00")} / s`,
|
||||
`Dexterity Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.dex, "0.00")} / s`,
|
||||
`Agility Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.agi, "0.00")} / s`,
|
||||
`Charisma Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.cha, "0.00")} / s`
|
||||
];
|
||||
if (repGainText !== "") { lines.push(repGainText); }
|
||||
elems.currentEarningsInfo!.innerHTML = lines.join("<br>");
|
||||
|
||||
elems.taskProgressBar!.innerText = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +398,9 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
|
||||
}
|
||||
}
|
||||
|
||||
// Reset Selectors
|
||||
removeChildrenFromElement(elems.taskDetailsSelector);
|
||||
removeChildrenFromElement(elems.taskDetailsSelector2);
|
||||
|
||||
const value: string = getSelectValue(elems.taskSelector);
|
||||
switch(value) {
|
||||
@@ -342,7 +409,14 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
|
||||
for (let i = 0; i < allJobs.length; ++i) {
|
||||
if (!forbiddenCompanies.includes(allJobs[i])) {
|
||||
elems.taskDetailsSelector!.add(createOptionElement(allJobs[i]));
|
||||
|
||||
// Set initial value of the 'Details' selector
|
||||
if (sleeve.currentTaskLocation === allJobs[i]) {
|
||||
elems.taskDetailsSelector!.selectedIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("------"));
|
||||
}
|
||||
break;
|
||||
case "Work for Faction":
|
||||
@@ -350,6 +424,11 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
|
||||
const fac: string = playerRef!.factions[i]!;
|
||||
if (!forbiddenFactions.includes(fac)) {
|
||||
elems.taskDetailsSelector!.add(createOptionElement(fac));
|
||||
|
||||
// Set initial value of the 'Details' Selector
|
||||
if (sleeve.currentTaskLocation === fac) {
|
||||
elems.taskDetailsSelector!.selectedIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < factionWorkTypeSelectorOptions.length; ++i) {
|
||||
@@ -361,6 +440,8 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
|
||||
const name: string = Crimes[crimeLabel].name;
|
||||
elems.taskDetailsSelector!.add(createOptionElement(name, crimeLabel));
|
||||
}
|
||||
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("------"));
|
||||
break;
|
||||
case "Take University Course":
|
||||
// First selector has class type
|
||||
@@ -410,17 +491,18 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
|
||||
|
||||
break;
|
||||
case "Shock Recovery":
|
||||
// No options in "Details" selector
|
||||
return;
|
||||
case "Synchronize":
|
||||
case "------":
|
||||
// No options in "Details" selector
|
||||
elems.taskDetailsSelector!.add(createOptionElement("------"));
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("------"));
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function setSleeveTask(sleeve: Sleeve, elems: ISleeveUIElems): void {
|
||||
function setSleeveTask(sleeve: Sleeve, elems: ISleeveUIElems): boolean {
|
||||
try {
|
||||
if (playerRef == null) {
|
||||
throw new Error("playerRef is null in Sleeve UI's setSleeveTask()");
|
||||
@@ -428,32 +510,21 @@ function setSleeveTask(sleeve: Sleeve, elems: ISleeveUIElems): void {
|
||||
|
||||
const taskValue: string = getSelectValue(elems.taskSelector);
|
||||
const detailValue: string = getSelectValue(elems.taskDetailsSelector);
|
||||
const detailValue2: string = getSelectValue(elems.taskDetailsSelector);
|
||||
const detailValue2: string = getSelectValue(elems.taskDetailsSelector2);
|
||||
|
||||
let res: boolean = false;
|
||||
switch(taskValue) {
|
||||
case "------":
|
||||
elems.taskDescription!.innerText = "This sleeve is currently idle";
|
||||
break;
|
||||
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]}.`;
|
||||
res = sleeve.commitCrime(playerRef!, Crimes[detailValue]);
|
||||
break;
|
||||
case "Take University Course":
|
||||
res = sleeve.takeUniversityCourse(playerRef!, detailValue2, detailValue);
|
||||
@@ -463,22 +534,79 @@ function setSleeveTask(sleeve: Sleeve, elems: ISleeveUIElems): void {
|
||||
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.";
|
||||
res = true;
|
||||
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."
|
||||
res = true;
|
||||
break;
|
||||
default:
|
||||
console.error(`Invalid/Unrecognized taskValue in setSleeveTask(): ${taskValue}`);
|
||||
}
|
||||
|
||||
if (res) {
|
||||
updateSleeveTaskDescription(sleeve, elems);
|
||||
} else {
|
||||
elems.taskDescription!.innerText = "Failed to assign sleeve to task. Invalid choice(s).";
|
||||
}
|
||||
|
||||
if (routing.isOn(Page.Sleeves)) {
|
||||
updateSleevesPage();
|
||||
}
|
||||
|
||||
return res;
|
||||
} catch(e) {
|
||||
console.error(`Exception caught in setSleeveTask(): ${e}`);
|
||||
exceptionAlert(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function updateSleeveTaskDescription(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.taskDetailsSelector2);
|
||||
|
||||
switch(taskValue) {
|
||||
case "------":
|
||||
elems.taskDescription!.innerText = "This sleeve is currently idle";
|
||||
break;
|
||||
case "Work for Company":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently working your ` +
|
||||
`job at ${sleeve.currentTaskLocation}.`;
|
||||
break;
|
||||
case "Work for Faction":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently doing ${detailValue2} for ` +
|
||||
`${sleeve.currentTaskLocation}.`;
|
||||
break;
|
||||
case "Commit Crime":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently attempting to ` +
|
||||
`${Crimes[detailValue].type}.`;
|
||||
break;
|
||||
case "Take University Course":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently studying/taking a course at ${sleeve.currentTaskLocation}.`;
|
||||
break;
|
||||
case "Workout at Gym":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently working out at ${sleeve.currentTaskLocation}.`;
|
||||
break;
|
||||
case "Shock 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":
|
||||
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 updateSleeveTaskDescription(): ${taskValue}`);
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(`Exception caught in updateSleeveTaskDescription(): ${e}`);
|
||||
exceptionAlert(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
export const SleeveFaq: string =
|
||||
[
|
||||
"<strong><u>How do sleeves work?</strong></u><br>",
|
||||
"Sleeves are essentially clones. You can use them to perform any work type",
|
||||
"action, such as working for a company/faction or committing a crime.",
|
||||
"Having sleeves perform these tasks earns you money, experience, and reputation.<br><br>",
|
||||
"Sleeves are their own individuals, which means they each have their own",
|
||||
"experience and stats.<br><br>",
|
||||
"When a sleeve earns experience, it earns experience for itself, the player's",
|
||||
"original 'consciousness', as well as all of the player's other sleeves.<br><br>",
|
||||
|
||||
"<strong><u>What is Synchronization (Sync)?</strong></u><br>",
|
||||
"Synchronization is a measure of how aligned your consciousness is with",
|
||||
"that of your Duplicate Sleeves. It is a numerical value between 1 and 100, and",
|
||||
"it affects how much experience is earned when the sleeve is performing a task.<br><br>",
|
||||
"Let N be the sleeve's synchronization. When the sleeve earns experience by performing a",
|
||||
"task, both the sleeve and the player's original host consciousness earn N%",
|
||||
"of the amount of experience normally earned by the task. All of the player's",
|
||||
"other sleeves earn ((N/100)^2 * 100)% of the experience.<br><br>",
|
||||
"Synchronization can be increased by assigning sleeves to the 'Synchronize' task.<br><br>",
|
||||
|
||||
"<strong><u>What is Shock?</u></strong><br>",
|
||||
"Sleeve shock is a measure of how much trauma the sleeve has due to being placed in a new",
|
||||
"body. It is a numerical value between 0 and 99, where 99 indicates full shock and 0 indicates",
|
||||
"no shock. Shock affects the amount of experience earned by the sleeve.<br><br>",
|
||||
"Sleeve shock slowly decreases over time. You can further increase the rate at which",
|
||||
"it decreases by assigning sleeves to the 'Shock Recovery' task.<br><br>",
|
||||
|
||||
"<strong><u>Why can't I work for this company or faction?</u></strong><br>",
|
||||
"Only one of your sleeves can work for a given company/faction a time.",
|
||||
"To clarify further, if you have two sleeves they can work for two different",
|
||||
"companies, but they cannot both work for the same company.<br><br>",
|
||||
|
||||
"<strong><u>Do sleeves get reset when installing Augmentations or switching BitNodes?</u></strong><br>",
|
||||
"Sleeves are reset when switching BitNodes, but not when installing Augmentations."
|
||||
].join(" ");
|
||||
Reference in New Issue
Block a user