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:
danielyxie 2019-01-21 20:39:52 -08:00
parent 5573e778bb
commit a2237d4319
24 changed files with 2536 additions and 2145 deletions

@ -31,13 +31,12 @@
border: 1px solid #fff; border: 1px solid #fff;
margin: 2px; margin: 2px;
padding: 2px; padding: 2px;
color: #fff;
} }
.bladeburner-nav-button { .bladeburner-nav-button {
@extend %bladeburner-nav-button; @extend %bladeburner-nav-button;
color: #fff;
&:hover { &:hover {
background-color: #3d4044; background-color: #3d4044;
} }

3986
dist/engine.bundle.js vendored

File diff suppressed because it is too large Load Diff

9
dist/engine.css vendored

@ -2054,12 +2054,11 @@ button {
.bladeburner-nav-button, .bladeburner-nav-button-inactive { .bladeburner-nav-button, .bladeburner-nav-button-inactive {
border: 1px solid #fff; border: 1px solid #fff;
margin: 2px; margin: 2px;
padding: 2px; } padding: 2px;
.bladeburner-nav-button {
color: #fff; } color: #fff; }
.bladeburner-nav-button:hover {
background-color: #3d4044; } .bladeburner-nav-button:hover {
background-color: #3d4044; }
.bladeburner-nav-button-inactive { .bladeburner-nav-button-inactive {
text-decoration: none; text-decoration: none;

400
dist/vendor.bundle.js vendored

File diff suppressed because it is too large Load Diff

@ -52,3 +52,16 @@ it decreases by assigning sleeves to the 'Shock Recovery' task.
Re-sleeving Re-sleeving
^^^^^^^^^^^ ^^^^^^^^^^^
Re-sleeving is the process of digitizing and transferring your consciousness into a
new human body, or "sleeve". When you re-sleeve into a new body, your stat experience
and Augmentations get replaced with those of the new body.
In order to re-sleeve, you must purchase new bodies. This can be done at VitaLife in
New Tokyo. Once you purchase a body to re-sleeve into, the effects will take
place immediately.
Note that resleeving **REMOVES** all of your currently-installed Augmentations,
and replaces them with the ones provided by the purchased sleeve. However,
Augmentations that are 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.

@ -26,7 +26,7 @@ List of all Source-Files
| | * Increases the player's charisma and company salary multipliers by 8%/12%/14% | | | * Increases the player's charisma and company salary multipliers by 8%/12%/14% |
+------------------------------------+-------------------------------------------------------------------------------------+ +------------------------------------+-------------------------------------------------------------------------------------+
| BitNode-4: The Singularity | * Lets the player access and use Netscript Singularity Functions in other BitNodes. | | BitNode-4: The Singularity | * Lets the player access and use Netscript Singularity Functions in other BitNodes. |
| | Each level of this Source-File opens up more of the Singularity Functions to use | | | * Each level of this Source-File opens up more of the Singularity Functions to use |
+------------------------------------+-------------------------------------------------------------------------------------+ +------------------------------------+-------------------------------------------------------------------------------------+
| BitNode-5: Artificial Intelligence | * Unlocks :ref:`gameplay_intelligence` | | BitNode-5: Artificial Intelligence | * Unlocks :ref:`gameplay_intelligence` |
| | * Unlocks getBitNodeMultipliers() Netscript function | | | * Unlocks getBitNodeMultipliers() Netscript function |
@ -56,7 +56,7 @@ List of all Source-Files
+------------------------------------+-------------------------------------------------------------------------------------+ +------------------------------------+-------------------------------------------------------------------------------------+
| BitNode-12: The Recursion | * There is no maximum level for this Source-File | | BitNode-12: The Recursion | * There is no maximum level for this Source-File |
| | * Each level of this Source-File increases all of the player's multipliers by 1%. | | | * Each level of this Source-File increases all of the player's multipliers by 1%. |
| | This affect is multiplicative with itself. This means that level N of this | | | * This affect is multiplicative with itself. This means that level N of this |
| | Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers | | | Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers |
| | that decrease) | | | that decrease) |
+------------------------------------+-------------------------------------------------------------------------------------+ +------------------------------------+-------------------------------------------------------------------------------------+

@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '1.0' version = '0.43'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '1.0' release = '0.43.0'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
@ -94,6 +94,9 @@ todo_include_todos = True
# #
#html_theme = 'agogo' #html_theme = 'agogo'
html_theme = "sphinx_rtd_theme" html_theme = "sphinx_rtd_theme"
html_theme_options = {
"navigation_depth": 5,
}
# Theme options are theme-specific and customize the look and feel of a theme # Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the # further. For a list of options available for each theme, see the
@ -179,7 +182,10 @@ texinfo_documents = [
] ]
# Example configuration for intersphinx: refer to the Python standard library. # Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/': None} intersphinx_mapping = {'https://docs.python.org/': None}
def setup(app):
print("Initializing (setup())");
app.add_stylesheet('maxwidthoverride.css')

@ -0,0 +1,3 @@
.wy-nav-content {
max-width: none;
}

@ -94,8 +94,8 @@ let NetscriptFunctions =
"installAugmentations|" + "installAugmentations|" +
// TIX API // TIX API
"getStockPrice|getStockPosition|getStockSymbols|buyStock|sellStock|" + "getStockPrice|getStockPosition|getStockSymbols|getStockMaxShares|" +
"shortStock|sellShort|" + "buyStock|sellStock|shortStock|sellShort|" +
"placeOrder|cancelOrder|getOrders|getStockVolatility|getStockForecast|" + "placeOrder|cancelOrder|getOrders|getStockVolatility|getStockForecast|" +
"purchase4SMarketData|purchase4SMarketDataTixApi|" + "purchase4SMarketData|purchase4SMarketDataTixApi|" +

@ -1,5 +1,6 @@
import { Augmentation } from "./Augmentation"; import { Augmentation } from "./Augmentation";
import { Augmentations } from "./Augmentations"; import { Augmentations } from "./Augmentations";
import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { AugmentationNames } from "./data/AugmentationNames"; import { AugmentationNames } from "./data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
@ -11,7 +12,8 @@ import { addWorkerScript } from "../NetscriptWorker";
import { Player } from "../Player"; import { Player } from "../Player";
import { prestigeAugmentation } from "../Prestige"; import { prestigeAugmentation } from "../Prestige";
import { saveObject } from "../SaveObject"; import { saveObject } from "../SaveObject";
import { Script , RunningScript} from "../Script"; import { Script,
RunningScript} from "../Script";
import { Server } from "../Server"; import { Server } from "../Server";
import { OwnedAugmentationsOrderSetting } from "../SettingEnums"; import { OwnedAugmentationsOrderSetting } from "../SettingEnums";
import { Settings } from "../Settings"; import { Settings } from "../Settings";

@ -19,7 +19,7 @@ export let CONSTANTS: IMap<any> = {
BaseCostFor1GBOfRamServer: 55000, //1 GB of RAM BaseCostFor1GBOfRamServer: 55000, //1 GB of RAM
BaseCostFor1GBOfRamHacknetNode: 30000, BaseCostFor1GBOfRamHacknetNode: 30000,
TravelCost: 200000, TravelCost: 200e3,
BaseCostForHacknetNode: 1000, BaseCostForHacknetNode: 1000,
BaseCostForHacknetNodeCore: 500000, BaseCostForHacknetNodeCore: 500000,
@ -130,7 +130,7 @@ export let CONSTANTS: IMap<any> = {
WSEAccountCost: 200e6, WSEAccountCost: 200e6,
TIXAPICost: 5e9, TIXAPICost: 5e9,
MarketData4SCost: 1e9, MarketData4SCost: 1e9,
MarketDataTixApi4SCost: 20e9, MarketDataTixApi4SCost: 25e9,
StockMarketCommission: 100e3, StockMarketCommission: 100e3,
//Hospital/Health //Hospital/Health
@ -516,6 +516,7 @@ export let CONSTANTS: IMap<any> = {
* Stock Market Changes: * Stock Market Changes:
** Each stock now has a maximum number of shares you can purchase (both Long and Short positions combined) ** Each stock now has a maximum number of shares you can purchase (both Long and Short positions combined)
** Added getStockMaxShares() Netscript function to the TIX API ** Added getStockMaxShares() Netscript function to the TIX API
** The cost of 4S Market Data TIX API Access increased from $20b to $25b
* Job Changes: * Job Changes:
** You can now hold multiple jobs at once. This means you no longer lose reputation when leaving a company ** You can now hold multiple jobs at once. This means you no longer lose reputation when leaving a company

@ -9,10 +9,14 @@ import { StockMarket,
SymbolToStockMap } from "./StockMarket/StockMarket"; SymbolToStockMap } from "./StockMarket/StockMarket";
import { Stock } from "./StockMarket/Stock"; import { Stock } from "./StockMarket/Stock";
import { Terminal } from "./Terminal"; import { Terminal } from "./Terminal";
import { numeralWrapper } from "./ui/numeralFormat"; import { numeralWrapper } from "./ui/numeralFormat";
import { dialogBoxCreate } from "../utils/DialogBox"; import { dialogBoxCreate } from "../utils/DialogBox";
import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { createElement } from "../utils/uiHelpers/createElement"; import { createElement } from "../utils/uiHelpers/createElement";
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
import { getSelectText } from "../utils/uiHelpers/getSelectData";
import { removeElementById } from "../utils/uiHelpers/removeElementById"; import { removeElementById } from "../utils/uiHelpers/removeElementById";
const devMenuContainerId = "dev-menu-container"; const devMenuContainerId = "dev-menu-container";
@ -222,7 +226,7 @@ export function createDevMenu() {
innerText: "Receive Invite to Faction", innerText: "Receive Invite to Faction",
}); });
// Augmentations / Source Files // Augmentations
const augmentationsHeader = createElement("h2", {innerText: "Augmentations"}); const augmentationsHeader = createElement("h2", {innerText: "Augmentations"});
const augmentationsDropdown = createElement("select", { const augmentationsDropdown = createElement("select", {
@ -242,6 +246,32 @@ export function createDevMenu() {
innerText: "Queue Augmentation", innerText: "Queue Augmentation",
}) })
// Source Files
const sourceFilesHeader = createElement("h2", { innerText: "Source-Files" });
const removeSourceFileDropdown = createElement("select", {
class: "dropdown",
margin: "5px",
});
for (let i = 0; i < 24; ++i) {
removeSourceFileDropdown.add(createOptionElement(String(i)));
}
const removeSourceFileButton = createElement("button", {
class: "std-button",
clickListener: () => {
const numToRemove = parseInt(getSelectText(removeSourceFileDropdown));
for (let i = 0; i < Player.sourceFiles.length; ++i) {
if (Player.sourceFiles[i].n === numToRemove) {
Player.sourceFiles.splice(i, 1);
hackWorldDaemon(Player.bitNodeN, true);
return;
}
}
},
innerText: "Remove Source File and Trigger Bitflume",
});
// Programs // Programs
const programsHeader = createElement("h2", {innerText: "Programs"}); const programsHeader = createElement("h2", {innerText: "Programs"});
@ -508,6 +538,9 @@ export function createDevMenu() {
devMenuContainer.appendChild(augmentationsHeader); devMenuContainer.appendChild(augmentationsHeader);
devMenuContainer.appendChild(augmentationsDropdown); devMenuContainer.appendChild(augmentationsDropdown);
devMenuContainer.appendChild(augmentationsQueueButton); devMenuContainer.appendChild(augmentationsQueueButton);
devMenuContainer.appendChild(sourceFilesHeader);
devMenuContainer.appendChild(removeSourceFileDropdown);
devMenuContainer.appendChild(removeSourceFileButton);
devMenuContainer.appendChild(programsHeader); devMenuContainer.appendChild(programsHeader);
devMenuContainer.appendChild(programsAddDropdown); devMenuContainer.appendChild(programsAddDropdown);
devMenuContainer.appendChild(programsAddButton); devMenuContainer.appendChild(programsAddButton);

@ -347,6 +347,9 @@ function processNetscript1Imports(code, workerScript) {
ImportDeclaration: (node) => { ImportDeclaration: (node) => {
hasImports = true; hasImports = true;
let scriptName = node.source.value; let scriptName = node.source.value;
if (scriptName.startsWith("./")) {
scriptName = scriptName.slice(2);
}
let script = getScript(scriptName); let script = getScript(scriptName);
if (script == null) { if (script == null) {
throw new Error("'Import' failed due to invalid script: " + scriptName); throw new Error("'Import' failed due to invalid script: " + scriptName);

@ -6,7 +6,6 @@ import { Person } from "../Person";
import { Augmentation } from "../../Augmentation/Augmentation"; import { Augmentation } from "../../Augmentation/Augmentation";
import { Augmentations } from "../../Augmentation/Augmentations"; import { Augmentations } from "../../Augmentation/Augmentations";
import { CONSTANTS } from "../../Constants";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../../utils/JSONReviver";
@ -24,10 +23,10 @@ export class Resleeve extends Person {
getCost(): number { getCost(): number {
// Each experience point adds this to the cost // Each experience point adds this to the cost
const CostPerExp: number = 4; const CostPerExp: number = 10e3;
// Final cost is multiplied by # Augs ^ this constant // Final cost is multiplied by this constant ^ # Augs
const NumAugsExponent: number = 1.05; const NumAugsExponent: number = 1.12;
// Get total exp in this re-sleeve // Get total exp in this re-sleeve
let totalExp: number = this.hacking_exp + let totalExp: number = this.hacking_exp +
@ -48,7 +47,7 @@ export class Resleeve extends Person {
totalAugmentationCost += aug!.baseCost; 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) { for (let i = p.queuedAugmentations.length - 1; i >= 0; --i) {
const name: string = p.queuedAugmentations[i].name; 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); 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 " + "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 " + "specially-engineered bodies for the re-sleeve process. Many of these bodies " +
"even come with genetic and cybernetic Augmentations!<br><br>" + "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 " + "all of your currently-installed Augmentations, and replace " +
"them with the ones provided by the purchased sleeve. However, Augmentations that you have " + "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 " + "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(); 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 * Keeps track of events/notifications for this sleeve
*/ */
@ -196,7 +201,37 @@ export class Sleeve extends Person {
* Earn experience for any stats (supports multiple) * Earn experience for any stats (supports multiple)
* This function also handles experience propogating to Player and other sleeves * 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' // Experience is first multiplied by shock. Then 'synchronization'
// is accounted for // is accounted for
const multFac = (this.shock / 100) * (this.sync / 100) * numCycles; const multFac = (this.shock / 100) * (this.sync / 100) * numCycles;
@ -696,6 +731,7 @@ export class Sleeve extends Person {
return false; return false;
} }
this.gymStatType = stat;
this.currentTask = SleeveTaskType.Gym; this.currentTask = SleeveTaskType.Gym;
return true; return true;

@ -7,8 +7,10 @@ import { SleeveFaq } from "./data/SleeveFaq";
import { IPlayer } from "../IPlayer"; import { IPlayer } from "../IPlayer";
import { CONSTANTS } from "../../Constants";
import { Locations } from "../../Locations"; import { Locations } from "../../Locations";
import { FactionWorkType } from "../../Faction/FactionWorkTypeEnum";
import { Cities } from "../../Locations/Cities"; import { Cities } from "../../Locations/Cities";
import { Crimes } from "../../Crime/Crimes"; import { Crimes } from "../../Crime/Crimes";
@ -23,9 +25,12 @@ import { exceptionAlert } from "../../../utils/helpers/exceptionAlert";
import { createElement } from "../../../utils/uiHelpers/createElement"; import { createElement } from "../../../utils/uiHelpers/createElement";
import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement"; 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 { getSelectValue } from "../../../utils/uiHelpers/getSelectData";
import { removeChildrenFromElement } from "../../../utils/uiHelpers/removeChildrenFromElement"; import { removeChildrenFromElement } from "../../../utils/uiHelpers/removeChildrenFromElement";
import { removeElement } from "../../../utils/uiHelpers/removeElement"; import { removeElement } from "../../../utils/uiHelpers/removeElement";
import { removeElementById } from "../../../utils/uiHelpers/removeElementById";
// Object that keeps track of all DOM elements for the UI for a single Sleeve // Object that keeps track of all DOM elements for the UI for a single Sleeve
interface ISleeveUIElems { interface ISleeveUIElems {
@ -33,6 +38,7 @@ interface ISleeveUIElems {
statsPanel: HTMLElement | null; statsPanel: HTMLElement | null;
stats: HTMLElement | null; stats: HTMLElement | null;
moreStatsButton: HTMLElement | null; moreStatsButton: HTMLElement | null;
travelButton: HTMLElement | null;
taskPanel: HTMLElement | null; taskPanel: HTMLElement | null;
taskSelector: HTMLSelectElement | null; taskSelector: HTMLSelectElement | null;
taskDetailsSelector: HTMLSelectElement | null; taskDetailsSelector: HTMLSelectElement | null;
@ -156,6 +162,7 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
statsPanel: null, statsPanel: null,
stats: null, stats: null,
moreStatsButton: null, moreStatsButton: null,
travelButton: null,
taskPanel: null, taskPanel: null,
taskSelector: null, taskSelector: null,
taskDetailsSelector: 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.stats);
elems.statsPanel.appendChild(elems.moreStatsButton); elems.statsPanel.appendChild(elems.moreStatsButton);
elems.statsPanel.appendChild(elems.travelButton);
elems.taskPanel = createElement("div", { class: "sleeve-panel", width: "40%" }); elems.taskPanel = createElement("div", { class: "sleeve-panel", width: "40%" });
elems.taskSelector = createElement("select") as HTMLSelectElement; 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("Workout at Gym"));
elems.taskSelector.add(createOptionElement("Shock Recovery")); elems.taskSelector.add(createOptionElement("Shock Recovery"));
elems.taskSelector.add(createOptionElement("Synchronize")); elems.taskSelector.add(createOptionElement("Synchronize"));
elems.taskSelector.addEventListener("change", () => {
updateSleeveTaskSelector(sleeve, elems, allSleeves);
});
elems.taskDetailsSelector = createElement("select") as HTMLSelectElement; elems.taskDetailsSelector = createElement("select") as HTMLSelectElement;
elems.taskDetailsSelector2 = createElement("select") as HTMLSelectElement; elems.taskDetailsSelector2 = createElement("select") as HTMLSelectElement;
elems.taskDescription = createElement("p"); elems.taskDescription = createElement("p");
elems.taskProgressBar = 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.selectedIndex = sleeve.currentTask; // Set initial value for Task Selector
elems.taskSelector.dispatchEvent(new Event('change')); elems.taskSelector.dispatchEvent(new Event('change'));
updateSleeveTaskDescription(sleeve, elems); updateSleeveTaskDescription(sleeve, elems);
@ -308,7 +356,8 @@ function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) {
`Dexterity: ${numeralWrapper.format(sleeve.dexterity, "0,0")}`, `Dexterity: ${numeralWrapper.format(sleeve.dexterity, "0,0")}`,
`Agility: ${numeralWrapper.format(sleeve.agility, "0,0")}`, `Agility: ${numeralWrapper.format(sleeve.agility, "0,0")}`,
`Charisma: ${numeralWrapper.format(sleeve.charisma, "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")}`, `Shock: ${numeralWrapper.format(100 - sleeve.shock, "0,0.000")}`,
`Sync: ${numeralWrapper.format(sleeve.sync, "0,0.000")}`].join("<br>"); `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); const value: string = getSelectValue(elems.taskSelector);
switch(value) { switch(value) {
case "Work for Company": case "Work for Company":
let companyCount: number = 0;
const allJobs: string[] = Object.keys(playerRef!.jobs!); const allJobs: string[] = Object.keys(playerRef!.jobs!);
for (let i = 0; i < allJobs.length; ++i) { for (let i = 0; i < allJobs.length; ++i) {
if (!forbiddenCompanies.includes(allJobs[i])) { if (!forbiddenCompanies.includes(allJobs[i])) {
@ -412,14 +462,17 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
// Set initial value of the 'Details' selector // Set initial value of the 'Details' selector
if (sleeve.currentTaskLocation === allJobs[i]) { if (sleeve.currentTaskLocation === allJobs[i]) {
elems.taskDetailsSelector!.selectedIndex = i; elems.taskDetailsSelector!.selectedIndex = companyCount;
} }
++companyCount;
} }
elems.taskDetailsSelector2!.add(createOptionElement("------")); elems.taskDetailsSelector2!.add(createOptionElement("------"));
} }
break; break;
case "Work for Faction": case "Work for Faction":
let factionCount: number = 0;
for (let i = 0; i < playerRef!.factions!.length; ++i) { for (let i = 0; i < playerRef!.factions!.length; ++i) {
const fac: string = playerRef!.factions[i]!; const fac: string = playerRef!.factions[i]!;
if (!forbiddenFactions.includes(fac)) { if (!forbiddenFactions.includes(fac)) {
@ -427,13 +480,30 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
// Set initial value of the 'Details' Selector // Set initial value of the 'Details' Selector
if (sleeve.currentTaskLocation === fac) { if (sleeve.currentTaskLocation === fac) {
elems.taskDetailsSelector!.selectedIndex = i; elems.taskDetailsSelector!.selectedIndex = factionCount;
} }
++factionCount;
} }
} }
for (let i = 0; i < factionWorkTypeSelectorOptions.length; ++i) { for (let i = 0; i < factionWorkTypeSelectorOptions.length; ++i) {
elems.taskDetailsSelector2!.add(createOptionElement(factionWorkTypeSelectorOptions[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; break;
case "Commit Crime": case "Commit Crime":
for (const crimeLabel in Crimes) { for (const crimeLabel in Crimes) {
@ -469,17 +539,37 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
// First selector has what stat is being trained // First selector has what stat is being trained
for (let i = 0; i < gymSelectorOptions.length; ++i) { for (let i = 0; i < gymSelectorOptions.length; ++i) {
elems.taskDetailsSelector!.add(createOptionElement(gymSelectorOptions[i])); elems.taskDetailsSelector!.add(createOptionElement(gymSelectorOptions[i]));
// Set initial value
if (sleeve.gymStatType === gymSelectorOptions[i]) {
elems.taskDetailsSelector!.selectedIndex = i;
}
} }
// Second selector has gym // Second selector has gym
// In this switch statement we also set the initial value of the second selector
switch (sleeve.city) { switch (sleeve.city) {
case Cities.Aevum: case Cities.Aevum:
elems.taskDetailsSelector2!.add(createOptionElement(Locations.AevumCrushFitnessGym)); elems.taskDetailsSelector2!.add(createOptionElement(Locations.AevumCrushFitnessGym));
elems.taskDetailsSelector2!.add(createOptionElement(Locations.AevumSnapFitnessGym)); 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; break;
case Cities.Sector12: case Cities.Sector12:
elems.taskDetailsSelector2!.add(createOptionElement(Locations.Sector12IronGym)); elems.taskDetailsSelector2!.add(createOptionElement(Locations.Sector12IronGym));
elems.taskDetailsSelector2!.add(createOptionElement(Locations.Sector12PowerhouseGym)); 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; break;
case Cities.Volhaven: case Cities.Volhaven:
elems.taskDetailsSelector2!.add(createOptionElement(Locations.VolhavenMilleniumFitnessGym)); elems.taskDetailsSelector2!.add(createOptionElement(Locations.VolhavenMilleniumFitnessGym));
@ -552,6 +642,11 @@ function setSleeveTask(sleeve: Sleeve, elems: ISleeveUIElems): boolean {
if (routing.isOn(Page.Sleeves)) { if (routing.isOn(Page.Sleeves)) {
updateSleevesPage(); 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; return res;

@ -279,6 +279,12 @@ PlayerObject.prototype.prestigeAugmentation = function() {
this.resleeves = []; this.resleeves = [];
for (let i = 0; i < this.sleeves.length; ++i) {
if (this.sleeves[i] instanceof Sleeve) {
this.sleeves[i].resetTaskStatus();
}
}
this.isWorking = false; this.isWorking = false;
this.currentWorkFactionName = ""; this.currentWorkFactionName = "";
this.currentWorkFactionDescription = ""; this.currentWorkFactionDescription = "";

@ -483,8 +483,11 @@ async function parseOnlyRamCalculate(server, code, workerScript) {
return -1; return -1;
} }
} else { } else {
const script = server.getScript(nextModule); const script = server.getScript(nextModule.startsWith("./") ? nextModule.slice(2) : nextModule);
if (!script) return -1; // No such script on the server. if (!script) {
console.warn("Invalid script");
return -1; // No such script on the server.
}
code = script.code; code = script.code;
} }

@ -78,6 +78,13 @@ export class Stock {
*/ */
readonly symbol: string; readonly symbol: string;
/**
* Total number of shares of this stock
* This is different than maxShares, as this is like authorized stock while
* maxShares is outstanding stock.
*/
readonly totalShares: number;
constructor(name: string = "", constructor(name: string = "",
symbol: string = "", symbol: string = "",
mv: number = 1, mv: number = 1,
@ -97,9 +104,13 @@ export class Stock {
this.otlkMag = otlkMag; this.otlkMag = otlkMag;
this.cap = getRandomInt(initPrice * 1e3, initPrice * 25e3); this.cap = getRandomInt(initPrice * 1e3, initPrice * 25e3);
// Maximum shares is determined by market cap, and is rounded to nearest millions // Total shares is determined by market cap, and is rounded to nearest millions
let maxSharesUnrounded: number = (marketCap / initPrice); let totalSharesUnrounded: number = (marketCap / initPrice);
this.maxShares = Math.round(maxSharesUnrounded / 1e6) * 1e6; this.totalShares = Math.round(totalSharesUnrounded / 1e6) * 1e6;
// Max Shares (Outstanding shares) is a percentage of total shares
const outstandingSharePercentage: number = 0.25;
this.maxShares = Math.round((this.totalShares * outstandingSharePercentage) / 1e6) * 1e6;
this.posTxtEl = null; this.posTxtEl = null;
} }

@ -278,7 +278,7 @@ function initStockMarket() {
StockMarket[vitalife] = vitalifeStk; StockMarket[vitalife] = vitalifeStk;
var icarus = Locations.Sector12IcarusMicrosystems; var icarus = Locations.Sector12IcarusMicrosystems;
var icarusStk = new Stock(icarus, StockSymbols[icarus], randInt(60, 70)/100, true, 7.5, randInt(12e3, 24e3), 800e12); var icarusStk = new Stock(icarus, StockSymbols[icarus], randInt(60, 70)/100, true, 7.5, randInt(12e3, 24e3), 800e9);
StockMarket[icarus] = icarusStk; StockMarket[icarus] = icarusStk;
var universalenergy = Locations.Sector12UniversalEnergy; var universalenergy = Locations.Sector12UniversalEnergy;

@ -969,7 +969,7 @@ const Engine = {
if (expForOtherSleeves == null) { continue; } if (expForOtherSleeves == null) { continue; }
for (let j = 0; j < Player.sleeves.length; ++j) { for (let j = 0; j < Player.sleeves.length; ++j) {
if (j === i) { continue; } if (j === i) { continue; }
Player.sleeves[j].gainExperience(Player, expForOtherSleeves, numCycles); Player.sleeves[j].gainExperience(Player, expForOtherSleeves, numCycles, true);
} }
} }
} }
@ -1336,7 +1336,7 @@ const Engine = {
if (expForOtherSleeves == null) { continue; } if (expForOtherSleeves == null) { continue; }
for (let j = 0; j < Player.sleeves.length; ++j) { for (let j = 0; j < Player.sleeves.length; ++j) {
if (j === i) { continue; } if (j === i) { continue; }
Player.sleeves[j].gainExperience(Player, expForOtherSleeves, numCyclesOffline); Player.sleeves[j].gainExperience(Player, expForOtherSleeves, numCyclesOffline, true);
} }
} }
} }

@ -1,11 +1,13 @@
export function getSelectValue(selector: HTMLSelectElement | null): string { export function getSelectValue(selector: HTMLSelectElement | null): string {
if (selector == null) { return ""; } if (selector == null) { return ""; }
if (selector.options.length <= 0) { return ""; } if (selector.options.length <= 0) { return ""; }
if (selector.selectedIndex < 0) { return ""; }
return selector.options[selector.selectedIndex].value; return selector.options[selector.selectedIndex].value;
} }
export function getSelectText(selector: HTMLSelectElement | null): string { export function getSelectText(selector: HTMLSelectElement | null): string {
if (selector == null) { return ""; } if (selector == null) { return ""; }
if (selector.options.length <= 0) { return ""; } if (selector.options.length <= 0) { return ""; }
if (selector.selectedIndex < 0) { return ""; }
return selector.options[selector.selectedIndex].text; return selector.options[selector.selectedIndex].text;
} }