mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-25 02:32:55 +02:00
Fixed bugs with Sleeve mechanics. Updated documentation to use RTD theme
This commit is contained in:
@@ -47,6 +47,7 @@ export interface IPlayer {
|
||||
crime_success_mult: number;
|
||||
|
||||
// Methods
|
||||
canAfford(cost: number): boolean;
|
||||
gainHackingExp(exp: number): void;
|
||||
gainStrengthExp(exp: number): void;
|
||||
gainDefenseExp(exp: number): void;
|
||||
@@ -59,6 +60,7 @@ export interface IPlayer {
|
||||
inGang(): boolean;
|
||||
loseMoney(money: number): void;
|
||||
reapplyAllAugmentations(resetMultipliers: boolean): void;
|
||||
reapplyAllSourceFiles(): void;
|
||||
startCrime(crimeType: string,
|
||||
hackExp: number,
|
||||
strExp: number,
|
||||
|
||||
@@ -85,6 +85,17 @@ export abstract class Person {
|
||||
|
||||
work_money_mult: number = 1;
|
||||
|
||||
hacknet_node_money_mult: number = 1;
|
||||
hacknet_node_purchase_cost_mult: number = 1;
|
||||
hacknet_node_ram_cost_mult: number = 1;
|
||||
hacknet_node_core_cost_mult: number = 1;
|
||||
hacknet_node_level_cost_mult: number = 1;
|
||||
|
||||
bladeburner_max_stamina_mult: number = 1;
|
||||
bladeburner_stamina_gain_mult: number = 1;
|
||||
bladeburner_analysis_mult: number = 1;
|
||||
bladeburner_success_chance_mult : number = 1;
|
||||
|
||||
/**
|
||||
* Augmentations
|
||||
*/
|
||||
@@ -101,7 +112,7 @@ export abstract class Person {
|
||||
/**
|
||||
* Updates this object's multipliers for the given augmentation
|
||||
*/
|
||||
applyAugmentation(aug: Augmentation, reapply=false) {
|
||||
applyAugmentation(aug: Augmentation) {
|
||||
for (const mult in aug.mults) {
|
||||
if ((<any>this)[mult] == null) {
|
||||
console.warn(`Augmentation has unrecognized multiplier property: ${mult}`);
|
||||
@@ -188,11 +199,11 @@ export abstract class Person {
|
||||
*/
|
||||
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)));
|
||||
this.strength = Math.max(1, Math.floor(this.calculateStat(this.strength_exp, this.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier)));
|
||||
this.defense = Math.max(1, Math.floor(this.calculateStat(this.defense_exp, this.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier)));
|
||||
this.dexterity = Math.max(1, Math.floor(this.calculateStat(this.dexterity_exp, this.dexterity_mult * BitNodeMultipliers.DexterityLevelMultiplier)));
|
||||
this.agility = Math.max(1, Math.floor(this.calculateStat(this.agility_exp, this.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier)));
|
||||
this.charisma = Math.max(1, Math.floor(this.calculateStat(this.charisma_exp, this.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier)));
|
||||
|
||||
const ratio: number = this.hp / this.max_hp;
|
||||
this.max_hp = Math.floor(10 + this.defense / 10);
|
||||
|
||||
@@ -24,7 +24,7 @@ export class Resleeve extends Person {
|
||||
|
||||
getCost(): number {
|
||||
// Each experience point adds this to the cost
|
||||
const CostPerExp: number = 5;
|
||||
const CostPerExp: number = 4;
|
||||
|
||||
// Final cost is multiplied by # Augs ^ this constant
|
||||
const NumAugsExponent: number = 1.05;
|
||||
|
||||
@@ -23,7 +23,13 @@ import { getRandomInt } from "../../../utils/helpers/getRandomInt";
|
||||
|
||||
|
||||
// Executes the actual re-sleeve when one is purchased
|
||||
export function purchaseResleeve(r: Resleeve, p: IPlayer):void {
|
||||
export function purchaseResleeve(r: Resleeve, p: IPlayer): boolean {
|
||||
const cost: number = r.getCost();
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
p.loseMoney(cost);
|
||||
|
||||
// Set the player's exp
|
||||
p.hacking_exp = r.hacking_exp;
|
||||
p.strength_exp = r.strength_exp;
|
||||
@@ -32,16 +38,25 @@ export function purchaseResleeve(r: Resleeve, p: IPlayer):void {
|
||||
p.agility_exp = r.agility_exp;
|
||||
p.charisma_exp = r.charisma_exp;
|
||||
|
||||
// Reset Augmentation "owned" data
|
||||
for (const augKey in Augmentations) {
|
||||
Augmentations[augKey].owned = false;
|
||||
}
|
||||
|
||||
// Clear all of the player's augmentations, except the NeuroFlux Governor
|
||||
// which is kept
|
||||
for (let i = p.augmentations.length - 1; i >= 0; --i) {
|
||||
if (p.augmentations[i].name !== AugmentationNames.NeuroFluxGovernor) {
|
||||
p.augmentations.splice(i, 1);
|
||||
} else {
|
||||
// NeuroFlux Governor
|
||||
Augmentations[AugmentationNames.NeuroFluxGovernor].owned = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < r.augmentations.length; ++i) {
|
||||
p.augmentations.push(new PlayerOwnedAugmentation(r.augmentations[i].name));
|
||||
Augmentations[r.augmentations[i].name].owned = true;
|
||||
}
|
||||
|
||||
// The player's purchased Augmentations should remain the same, but any purchased
|
||||
@@ -55,6 +70,8 @@ export function purchaseResleeve(r: Resleeve, p: IPlayer):void {
|
||||
}
|
||||
|
||||
p.reapplyAllAugmentations(true);
|
||||
p.reapplyAllSourceFiles(); //Multipliers get reset, so have to re-process source files too
|
||||
return true;
|
||||
}
|
||||
|
||||
// Creates all of the Re-sleeves that will be available for purchase at VitaLife
|
||||
@@ -67,23 +84,33 @@ export function generateResleeves(): Resleeve[] {
|
||||
let r: Resleeve = new Resleeve();
|
||||
|
||||
// Generate experience
|
||||
const expMult: number = i + 1;
|
||||
r.hacking_exp = expMult * getRandomInt(500, 1500);
|
||||
r.strength_exp = expMult * getRandomInt(500, 1500);
|
||||
r.defense_exp = expMult * getRandomInt(500, 1500);
|
||||
r.dexterity_exp = expMult * getRandomInt(500, 1500);
|
||||
r.agility_exp = expMult * getRandomInt(500, 1500);
|
||||
r.charisma_exp = expMult * getRandomInt(500, 1500);
|
||||
const expMult: number = (5 * i) + 1;
|
||||
r.hacking_exp = expMult * getRandomInt(1000, 5000);
|
||||
r.strength_exp = expMult * getRandomInt(1000, 5000);
|
||||
r.defense_exp = expMult * getRandomInt(1000, 5000);
|
||||
r.dexterity_exp = expMult * getRandomInt(1000, 5000);
|
||||
r.agility_exp = expMult * getRandomInt(1000, 5000);
|
||||
r.charisma_exp = expMult * getRandomInt(1000, 5000);
|
||||
|
||||
// Generate Augs
|
||||
const baseNumAugs: number = Math.ceil((i + 1) / 2);
|
||||
// Augmentation prequisites will be ignored for this
|
||||
const baseNumAugs: number = Math.max(2, Math.ceil((i + 3) / 2));
|
||||
const numAugs: number = getRandomInt(baseNumAugs, baseNumAugs + 2);
|
||||
const augKeys: string[] = Object.keys(Augmentations);
|
||||
for (let a = 0; a < numAugs; ++a) {
|
||||
// We'll ignore Aug prerequisites for this
|
||||
const augKeys: string[] = Object.keys(Augmentations);
|
||||
const randKey: string = augKeys[getRandomInt(0, augKeys.length - 1)];
|
||||
// Get a random aug
|
||||
const randIndex: number = getRandomInt(0, augKeys.length - 1)
|
||||
const randKey: string = augKeys[randIndex];
|
||||
if (randKey === AugmentationNames.TheRedPill) {
|
||||
continue; // A sleeve can't have The Red Pill
|
||||
}
|
||||
const randAug: Augmentation | null = Augmentations[randKey];
|
||||
r.augmentations.push({name: randAug!.name, level: 1});
|
||||
r.applyAugmentation(Augmentations[randKey]);
|
||||
r.updateStatLevels();
|
||||
|
||||
// Remove Augmentation so that there are no duplicates
|
||||
augKeys.splice(randIndex, 1);
|
||||
}
|
||||
|
||||
ret.push(r);
|
||||
|
||||
@@ -7,8 +7,6 @@ import { generateResleeves,
|
||||
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
import { IMap } from "../../types";
|
||||
|
||||
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
|
||||
@@ -23,9 +21,7 @@ import { exceptionAlert } from "../../../utils/helpers/exceptionAlert";
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement";
|
||||
import { getSelectValue } from "../../../utils/uiHelpers/getSelectData";
|
||||
import { removeChildrenFromElement } from "../../../utils/uiHelpers/removeChildrenFromElement";
|
||||
import { removeElement } from "../../../utils/uiHelpers/removeElement";
|
||||
import { removeElementById } from "../../../utils/uiHelpers/removeElementById";
|
||||
|
||||
interface IResleeveUIElems {
|
||||
container: HTMLElement | null;
|
||||
@@ -70,7 +66,19 @@ export function createResleevesPage(p: IPlayer) {
|
||||
|
||||
UIElems.info = createElement("p", {
|
||||
display: "inline-block",
|
||||
innerText: "TOODOOO",
|
||||
innerHTML: "Re-sleeving is the process of digitizing and transferring your consciousness " +
|
||||
"into a new human body, or 'sleeve'. Here at VitaLife, you can purchase new " +
|
||||
"specially-engineered bodies for the re-sleeve process. Many of these bodies " +
|
||||
"even come with genetic and cybernetic Augmentations!<br><br>" +
|
||||
"Re-sleeving will chance your experience for every stat. It will also REMOVE " +
|
||||
"all of your currently-installed Augmentations, and replace " +
|
||||
"them with the ones provided by the purchased sleeve. However, Augmentations that you have " +
|
||||
"purchased but not installed will NOT be removed. If you have purchased an " +
|
||||
"Augmentation and then re-sleeve into a body which already has that Augmentation, " +
|
||||
"it will be removed (since you cannot have duplicate Augmentations).<br><br>" +
|
||||
"NOTE: The stats and multipliers displayed on this page do NOT include your bonuses from " +
|
||||
"Source-File.",
|
||||
width: "75%",
|
||||
});
|
||||
|
||||
UIElems.resleeveList = createElement("ul");
|
||||
@@ -96,7 +104,10 @@ export function createResleevesPage(p: IPlayer) {
|
||||
}
|
||||
|
||||
export function clearResleevesPage() {
|
||||
removeElement(UIElems.container);
|
||||
if (UIElems.container instanceof HTMLElement) {
|
||||
removeElement(UIElems.container);
|
||||
}
|
||||
|
||||
for (const prop in UIElems) {
|
||||
(<any>UIElems)[prop] = null;
|
||||
}
|
||||
@@ -125,8 +136,17 @@ function createResleeveUi(resleeve: Resleeve): IResleeveUIElems {
|
||||
display: "block",
|
||||
});
|
||||
|
||||
elems.statsPanel = createElement("div", { class: "resleeve-panel" });
|
||||
elems.stats = createElement("p", { class: "resleeve-stats-text" });
|
||||
elems.statsPanel = createElement("div", { class: "resleeve-panel", width: "30%" });
|
||||
elems.stats = createElement("p", {
|
||||
class: "resleeve-stats-text",
|
||||
innerHTML:
|
||||
`Hacking: ${numeralWrapper.format(resleeve.hacking_skill, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.hacking_exp)} exp)<br>` +
|
||||
`Strength: ${numeralWrapper.format(resleeve.strength, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.strength_exp)} exp)<br>` +
|
||||
`Defense: ${numeralWrapper.format(resleeve.defense, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.defense_exp)} exp)<br>` +
|
||||
`Dexterity: ${numeralWrapper.format(resleeve.dexterity, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.dexterity_exp)} exp)<br>` +
|
||||
`Agility: ${numeralWrapper.format(resleeve.agility, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.agility_exp)} exp)<br>` +
|
||||
`Charisma: ${numeralWrapper.format(resleeve.charisma, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.charisma_exp)} exp)`,
|
||||
});
|
||||
elems.multipliersButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Multipliers",
|
||||
@@ -155,33 +175,58 @@ function createResleeveUi(resleeve: Resleeve): IResleeveUIElems {
|
||||
`Faction Reputation Gain multiplier: ${numeralWrapper.formatPercentage(resleeve.faction_rep_mult)}`,
|
||||
`Crime Money multiplier: ${numeralWrapper.formatPercentage(resleeve.crime_money_mult)}`,
|
||||
`Crime Success multiplier: ${numeralWrapper.formatPercentage(resleeve.crime_success_mult)}`,
|
||||
`Hacknet Income multiplier: ${numeralWrapper.formatPercentage(resleeve.hacknet_node_money_mult)}`,
|
||||
`Hacknet Purchase Cost multiplier: ${numeralWrapper.formatPercentage(resleeve.hacknet_node_purchase_cost_mult)}`,
|
||||
`Hacknet Level Upgrade Cost multiplier: ${numeralWrapper.formatPercentage(resleeve.hacknet_node_level_cost_mult)}`,
|
||||
`Hacknet Ram Upgrade Cost multiplier: ${numeralWrapper.formatPercentage(resleeve.hacknet_node_ram_cost_mult)}`,
|
||||
`Hacknet Core Upgrade Cost multiplier: ${numeralWrapper.formatPercentage(resleeve.hacknet_node_core_cost_mult)}`,
|
||||
`Bladeburner Max Stamina multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_max_stamina_mult)}`,
|
||||
`Bladeburner Stamina Gain multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_stamina_gain_mult)}`,
|
||||
`Bladeburner Field Analysis multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_analysis_mult)}`,
|
||||
`Bladeburner Success Chance multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_success_chance_mult)}`
|
||||
].join("<br>"), false
|
||||
)
|
||||
}
|
||||
});
|
||||
elems.statsPanel.appendChild(elems.stats);
|
||||
elems.statsPanel.appendChild(elems.multipliersButton);
|
||||
|
||||
elems.augPanel = createElement("div", { class: "resleeve-panel" });
|
||||
elems.augSelector = createElement("select") as HTMLSelectElement;
|
||||
elems.augPanel = createElement("div", { class: "resleeve-panel", width: "50%" });
|
||||
elems.augSelector = createElement("select", { class: "resleeve-aug-selector" }) as HTMLSelectElement;
|
||||
elems.augDescription = createElement("p");
|
||||
for (let i = 0; i < resleeve.augmentations.length; ++i) {
|
||||
elems.augSelector.add(createOptionElement(resleeve.augmentations[i].name));
|
||||
};
|
||||
elems.augSelector.addEventListener("change", () => {
|
||||
updateAugDescription(elems);
|
||||
});
|
||||
elems.augDescription = createElement("p");
|
||||
elems.augSelector.dispatchEvent(new Event('change')); // Set inital description by manually triggering change event
|
||||
elems.augPanel.appendChild(elems.augSelector);
|
||||
elems.augPanel.appendChild(elems.augDescription);
|
||||
|
||||
elems.costPanel = createElement("div", { class: "resleeve-panel" });
|
||||
const cost: number = resleeve.getCost();
|
||||
elems.costPanel = createElement("div", { class: "resleeve-panel", width: "20%" });
|
||||
elems.costText = createElement("p", {
|
||||
innerText: `It costs ${numeralWrapper.formatMoney(resleeve.getCost())} ` +
|
||||
innerText: `It costs ${numeralWrapper.formatMoney(cost)} ` +
|
||||
`to purchase this Sleeve.`,
|
||||
});
|
||||
elems.buyButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Purchase",
|
||||
clickListener: () => {
|
||||
purchaseResleeve(resleeve, playerRef!);
|
||||
if (purchaseResleeve(resleeve, playerRef!)) {
|
||||
dialogBoxCreate(`You re-sleeved for ${numeralWrapper.formatMoney(cost)}!`, false);
|
||||
} else {
|
||||
dialogBoxCreate(`You cannot afford to re-sleeve into this body`, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
elems.costPanel.appendChild(elems.costText);
|
||||
elems.costPanel.appendChild(elems.buyButton);
|
||||
|
||||
elems.container.appendChild(elems.statsPanel);
|
||||
elems.container.appendChild(elems.augPanel);
|
||||
elems.container.appendChild(elems.costPanel);
|
||||
|
||||
return elems;
|
||||
}
|
||||
|
||||
@@ -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