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
+4 -5
View File
@@ -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));
}
/**
+1 -1
View File
@@ -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);
}
}
+1 -1
View File
@@ -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 " +
+37 -1
View File
@@ -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;
+101 -6
View File
@@ -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;