Fixed bugs with Sleeve mechanics. Updated documentation to use RTD theme

This commit is contained in:
danielyxie
2019-01-20 14:57:38 -08:00
parent 17bfbfeb80
commit 5573e778bb
43 changed files with 4064 additions and 2491 deletions
+73 -34
View File
@@ -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,
}
+196 -68
View File
@@ -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(" ");