mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-25 10:42:51 +02:00
More bug fixes for Sleeve/Resleeve features. Rebalancing for Sleeve/Resleeve and stock market. Added an option to remove source files in the dev menu
This commit is contained in:
@@ -6,7 +6,6 @@ import { Person } from "../Person";
|
||||
|
||||
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../../utils/JSONReviver";
|
||||
|
||||
@@ -24,10 +23,10 @@ export class Resleeve extends Person {
|
||||
|
||||
getCost(): number {
|
||||
// Each experience point adds this to the cost
|
||||
const CostPerExp: number = 4;
|
||||
const CostPerExp: number = 10e3;
|
||||
|
||||
// Final cost is multiplied by # Augs ^ this constant
|
||||
const NumAugsExponent: number = 1.05;
|
||||
// Final cost is multiplied by this constant ^ # Augs
|
||||
const NumAugsExponent: number = 1.12;
|
||||
|
||||
// Get total exp in this re-sleeve
|
||||
let totalExp: number = this.hacking_exp +
|
||||
@@ -48,7 +47,7 @@ export class Resleeve extends Person {
|
||||
totalAugmentationCost += aug!.baseCost;
|
||||
}
|
||||
|
||||
return (totalExp * CostPerExp) + (totalAugmentationCost * Math.pow(this.augmentations.length, NumAugsExponent));
|
||||
return (totalExp * CostPerExp) + (totalAugmentationCost * Math.pow(NumAugsExponent, this.augmentations.length));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,7 +64,7 @@ export function purchaseResleeve(r: Resleeve, p: IPlayer): boolean {
|
||||
for (let i = p.queuedAugmentations.length - 1; i >= 0; --i) {
|
||||
const name: string = p.queuedAugmentations[i].name;
|
||||
|
||||
if (p.augmentations.filter((e: IPlayerOwnedAugmentation) => {e.name !== AugmentationNames.NeuroFluxGovernor && e.name === name}).length >= 1) {
|
||||
if (p.augmentations.filter((e: IPlayerOwnedAugmentation) => {return e.name !== AugmentationNames.NeuroFluxGovernor && e.name === name}).length >= 1) {
|
||||
p.queuedAugmentations.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export function createResleevesPage(p: IPlayer) {
|
||||
"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 " +
|
||||
"Re-sleeving will change 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 " +
|
||||
|
||||
@@ -92,6 +92,11 @@ export class Sleeve extends Person {
|
||||
*/
|
||||
gainRatesForTask: ITaskTracker = createTaskTracker();
|
||||
|
||||
/**
|
||||
* String that stores what stat the sleeve is training at the gym
|
||||
*/
|
||||
gymStatType: string = "";
|
||||
|
||||
/**
|
||||
* Keeps track of events/notifications for this sleeve
|
||||
*/
|
||||
@@ -196,7 +201,37 @@ export class Sleeve extends Person {
|
||||
* Earn experience for any stats (supports multiple)
|
||||
* This function also handles experience propogating to Player and other sleeves
|
||||
*/
|
||||
gainExperience(p: IPlayer, exp: ITaskTracker, numCycles: number=1): ITaskTracker {
|
||||
gainExperience(p: IPlayer, exp: ITaskTracker, numCycles: number=1, fromOtherSleeve: boolean=false): ITaskTracker {
|
||||
// If the experience is coming from another sleeve, it is not multiplied by anything.
|
||||
// Also the player does not earn anything
|
||||
if (fromOtherSleeve) {
|
||||
if (exp.hack > 0) {
|
||||
this.hacking_exp += exp.hack;
|
||||
}
|
||||
|
||||
if (exp.str > 0) {
|
||||
this.strength_exp += exp.str;
|
||||
}
|
||||
|
||||
if (exp.def > 0) {
|
||||
this.defense_exp += exp.def;
|
||||
}
|
||||
|
||||
if (exp.dex > 0) {
|
||||
this.dexterity_exp += exp.dex;
|
||||
}
|
||||
|
||||
if (exp.agi > 0) {
|
||||
this.agility_exp += exp.agi;
|
||||
}
|
||||
|
||||
if (exp.cha > 0) {
|
||||
this.charisma_exp += exp.cha;
|
||||
}
|
||||
|
||||
return createTaskTracker();
|
||||
}
|
||||
|
||||
// Experience is first multiplied by shock. Then 'synchronization'
|
||||
// is accounted for
|
||||
const multFac = (this.shock / 100) * (this.sync / 100) * numCycles;
|
||||
@@ -696,6 +731,7 @@ export class Sleeve extends Person {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.gymStatType = stat;
|
||||
this.currentTask = SleeveTaskType.Gym;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -7,8 +7,10 @@ import { SleeveFaq } from "./data/SleeveFaq";
|
||||
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { Locations } from "../../Locations";
|
||||
|
||||
import { FactionWorkType } from "../../Faction/FactionWorkTypeEnum";
|
||||
import { Cities } from "../../Locations/Cities";
|
||||
import { Crimes } from "../../Crime/Crimes";
|
||||
|
||||
@@ -23,9 +25,12 @@ import { exceptionAlert } from "../../../utils/helpers/exceptionAlert";
|
||||
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement";
|
||||
import { createPopup } from "../../../utils/uiHelpers/createPopup";
|
||||
import { createPopupCloseButton } from "../../../utils/uiHelpers/createPopupCloseButton";
|
||||
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 {
|
||||
@@ -33,6 +38,7 @@ interface ISleeveUIElems {
|
||||
statsPanel: HTMLElement | null;
|
||||
stats: HTMLElement | null;
|
||||
moreStatsButton: HTMLElement | null;
|
||||
travelButton: HTMLElement | null;
|
||||
taskPanel: HTMLElement | null;
|
||||
taskSelector: HTMLSelectElement | null;
|
||||
taskDetailsSelector: HTMLSelectElement | null;
|
||||
@@ -156,6 +162,7 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
statsPanel: null,
|
||||
stats: null,
|
||||
moreStatsButton: null,
|
||||
travelButton: null,
|
||||
taskPanel: null,
|
||||
taskSelector: null,
|
||||
taskDetailsSelector: null,
|
||||
@@ -212,8 +219,49 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
);
|
||||
}
|
||||
});
|
||||
elems.travelButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Travel",
|
||||
clickListener: () => {
|
||||
const popupId: string = "sleeve-travel-popup";
|
||||
const popupArguments: HTMLElement[] = [];
|
||||
popupArguments.push(createPopupCloseButton(popupId, { class: "std-button" }));
|
||||
popupArguments.push(createElement("p", {
|
||||
innerText: "Have this sleeve travel to a different city. This affects " +
|
||||
"the gyms and universities at which this sleeve can study. " +
|
||||
`Traveling to a different city costs ${numeralWrapper.formatMoney(CONSTANTS.TravelCost)}.` +
|
||||
"It will also CANCEL the sleeve's current task (setting it to idle)",
|
||||
}));
|
||||
for (const label in Cities) {
|
||||
if (sleeve.city === Cities[label]) { continue; }
|
||||
(function(sleeve, label) {
|
||||
popupArguments.push(createElement("div", {
|
||||
// Reusing this css class. It adds a border and makes it so that
|
||||
// the background color changes when you hover
|
||||
class: "cmpy-mgmt-find-employee-option",
|
||||
innerText: Cities[label],
|
||||
clickListener: () => {
|
||||
if (!playerRef!.canAfford(CONSTANTS.TravelCost)) {
|
||||
dialogBoxCreate("You cannot afford to have this sleeve travel to another city", false);
|
||||
return false;
|
||||
}
|
||||
sleeve.city = Cities[label];
|
||||
playerRef!.loseMoney(CONSTANTS.TravelCost);
|
||||
sleeve.resetTaskStatus();
|
||||
removeElementById(popupId);
|
||||
updateSleeveUi(sleeve, elems);
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
})(sleeve, label);
|
||||
}
|
||||
|
||||
createPopup(popupId, popupArguments);
|
||||
}
|
||||
})
|
||||
elems.statsPanel.appendChild(elems.stats);
|
||||
elems.statsPanel.appendChild(elems.moreStatsButton);
|
||||
elems.statsPanel.appendChild(elems.travelButton);
|
||||
|
||||
elems.taskPanel = createElement("div", { class: "sleeve-panel", width: "40%" });
|
||||
elems.taskSelector = createElement("select") as HTMLSelectElement;
|
||||
@@ -225,13 +273,13 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
elems.taskSelector.add(createOptionElement("Workout at Gym"));
|
||||
elems.taskSelector.add(createOptionElement("Shock Recovery"));
|
||||
elems.taskSelector.add(createOptionElement("Synchronize"));
|
||||
elems.taskSelector.addEventListener("change", () => {
|
||||
updateSleeveTaskSelector(sleeve, elems, allSleeves);
|
||||
});
|
||||
elems.taskDetailsSelector = createElement("select") as HTMLSelectElement;
|
||||
elems.taskDetailsSelector2 = createElement("select") as HTMLSelectElement;
|
||||
elems.taskDescription = createElement("p");
|
||||
elems.taskProgressBar = createElement("p");
|
||||
elems.taskSelector.addEventListener("change", () => {
|
||||
updateSleeveTaskSelector(sleeve, elems, allSleeves);
|
||||
});
|
||||
elems.taskSelector.selectedIndex = sleeve.currentTask; // Set initial value for Task Selector
|
||||
elems.taskSelector.dispatchEvent(new Event('change'));
|
||||
updateSleeveTaskDescription(sleeve, elems);
|
||||
@@ -308,7 +356,8 @@ function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) {
|
||||
`Dexterity: ${numeralWrapper.format(sleeve.dexterity, "0,0")}`,
|
||||
`Agility: ${numeralWrapper.format(sleeve.agility, "0,0")}`,
|
||||
`Charisma: ${numeralWrapper.format(sleeve.charisma, "0,0")}`,
|
||||
`HP: ${numeralWrapper.format(sleeve.hp, "0,0")} / ${numeralWrapper.format(sleeve.max_hp, "0,0")}<br>`,
|
||||
`HP: ${numeralWrapper.format(sleeve.hp, "0,0")} / ${numeralWrapper.format(sleeve.max_hp, "0,0")}`,
|
||||
`City: ${sleeve.city}`,
|
||||
`Shock: ${numeralWrapper.format(100 - sleeve.shock, "0,0.000")}`,
|
||||
`Sync: ${numeralWrapper.format(sleeve.sync, "0,0.000")}`].join("<br>");
|
||||
|
||||
@@ -405,6 +454,7 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
|
||||
const value: string = getSelectValue(elems.taskSelector);
|
||||
switch(value) {
|
||||
case "Work for Company":
|
||||
let companyCount: number = 0;
|
||||
const allJobs: string[] = Object.keys(playerRef!.jobs!);
|
||||
for (let i = 0; i < allJobs.length; ++i) {
|
||||
if (!forbiddenCompanies.includes(allJobs[i])) {
|
||||
@@ -412,14 +462,17 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
|
||||
|
||||
// Set initial value of the 'Details' selector
|
||||
if (sleeve.currentTaskLocation === allJobs[i]) {
|
||||
elems.taskDetailsSelector!.selectedIndex = i;
|
||||
elems.taskDetailsSelector!.selectedIndex = companyCount;
|
||||
}
|
||||
|
||||
++companyCount;
|
||||
}
|
||||
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("------"));
|
||||
}
|
||||
break;
|
||||
case "Work for Faction":
|
||||
let factionCount: number = 0;
|
||||
for (let i = 0; i < playerRef!.factions!.length; ++i) {
|
||||
const fac: string = playerRef!.factions[i]!;
|
||||
if (!forbiddenFactions.includes(fac)) {
|
||||
@@ -427,13 +480,30 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
|
||||
|
||||
// Set initial value of the 'Details' Selector
|
||||
if (sleeve.currentTaskLocation === fac) {
|
||||
elems.taskDetailsSelector!.selectedIndex = i;
|
||||
elems.taskDetailsSelector!.selectedIndex = factionCount;
|
||||
}
|
||||
|
||||
++factionCount;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < factionWorkTypeSelectorOptions.length; ++i) {
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(factionWorkTypeSelectorOptions[i]));
|
||||
}
|
||||
|
||||
// Set initial value for faction work type
|
||||
switch (sleeve.factionWorkType) {
|
||||
case FactionWorkType.Hacking:
|
||||
elems.taskDetailsSelector2!.selectedIndex = 0;
|
||||
break;
|
||||
case FactionWorkType.Security:
|
||||
elems.taskDetailsSelector2!.selectedIndex = 0;
|
||||
break;
|
||||
case FactionWorkType.Field:
|
||||
elems.taskDetailsSelector2!.selectedIndex = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "Commit Crime":
|
||||
for (const crimeLabel in Crimes) {
|
||||
@@ -469,17 +539,37 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
|
||||
// First selector has what stat is being trained
|
||||
for (let i = 0; i < gymSelectorOptions.length; ++i) {
|
||||
elems.taskDetailsSelector!.add(createOptionElement(gymSelectorOptions[i]));
|
||||
|
||||
// Set initial value
|
||||
if (sleeve.gymStatType === gymSelectorOptions[i]) {
|
||||
elems.taskDetailsSelector!.selectedIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Second selector has gym
|
||||
// In this switch statement we also set the initial value of the second selector
|
||||
switch (sleeve.city) {
|
||||
case Cities.Aevum:
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.AevumCrushFitnessGym));
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.AevumSnapFitnessGym));
|
||||
|
||||
// Set initial value
|
||||
if (sleeve.currentTaskLocation === Locations.AevumCrushFitnessGym) {
|
||||
elems.taskDetailsSelector2!.selectedIndex = 0;
|
||||
} else if (sleeve.currentTaskLocation === Locations.AevumSnapFitnessGym) {
|
||||
elems.taskDetailsSelector2!.selectedIndex = 1;
|
||||
}
|
||||
break;
|
||||
case Cities.Sector12:
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.Sector12IronGym));
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.Sector12PowerhouseGym));
|
||||
|
||||
// Set initial value
|
||||
if (sleeve.currentTaskLocation === Locations.Sector12IronGym) {
|
||||
elems.taskDetailsSelector2!.selectedIndex = 0;
|
||||
} else if (sleeve.currentTaskLocation === Locations.Sector12PowerhouseGym) {
|
||||
elems.taskDetailsSelector2!.selectedIndex = 1;
|
||||
}
|
||||
break;
|
||||
case Cities.Volhaven:
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.VolhavenMilleniumFitnessGym));
|
||||
@@ -552,6 +642,11 @@ function setSleeveTask(sleeve: Sleeve, elems: ISleeveUIElems): boolean {
|
||||
|
||||
if (routing.isOn(Page.Sleeves)) {
|
||||
updateSleevesPage();
|
||||
|
||||
// Update the task selector for all sleeves by triggering a change event
|
||||
for (const e of UIElems.sleeves!) {
|
||||
e.taskSelector!.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
Reference in New Issue
Block a user