Began implementing Sleeve UI page

This commit is contained in:
danielyxie 2019-01-10 00:20:04 -08:00
parent 7d51182c60
commit d9b865ef17
9 changed files with 227 additions and 24 deletions

9
css/sleeves.scss Normal file

@ -0,0 +1,9 @@
/**
* Styling for the Sleeves Management page
*/
@import "theme";
.sleeve-container {
border: 1px solid white;
margin: 4px;
}

@ -508,6 +508,10 @@ export let CONSTANTS: IMap<any> = {
LatestUpdate:
`
v0.43.0
* Stock Market Changes:
** 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
* Home Computer RAM is now capped at 2 ^ 30 GB (1073741824 GB)
`

@ -51,14 +51,6 @@ export interface ICrime {
kills: number;
}
// Interface for Faction object
// Used because at the time of implementation, the Faction object has not been
// converted to TypeScript
export interface IFaction {
name: string;
playerReputation: number;
}
// Interface that defines a generic object used to track experience/money
// earnings for tasks
export interface ITaskTracker {
@ -129,6 +121,12 @@ export abstract class Person {
work_money_mult: number;
/**
* Augmentations
*/
this.augmentations = [];
this.queuedAugmentations = [];
/**
* City that the person is in
*/

@ -1,6 +1,6 @@
/**
* Sleeves are clones of the player that can be used to perform
* different tasks synchronously.
* Sleeves are bodies that contain the player's cloned consciousness.
* The player can use these bodies to perform different tasks synchronously.
*
* Each sleeve is its own individual, meaning it has its own stats/exp
*
@ -11,7 +11,6 @@ import { SleeveTaskType } from "./SleeveTaskTypesEnum";
import { Person,
IPlayer,
ICrime,
IFaction,
ITaskTracker,
createTaskTracker } from "../Person";
@ -101,6 +100,8 @@ export class Sleeve extends Person {
* Sleeve shock. Number between 1 and 100
* Trauma/shock that comes with being in a sleeve. Experience earned
* is multipled by shock%. This gets applied before synchronization
*
* Reputation earned is also multiplied by shock%
*/
shock: number = 1;
@ -251,6 +252,7 @@ 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);
}
@ -262,17 +264,17 @@ export class Sleeve extends Person {
if (this.currentTask === SleeveTaskType.Faction) {
switch (this.factionWorkType) {
case FactionWorkType.Hacking:
return this.getFactionHackingWorkRepGain();
return this.getFactionHackingWorkRepGain() * (this.shock / 100);
case FactionWorkType.Field:
return this.getFactionFieldWorkRepGain();
return this.getFactionFieldWorkRepGain() * (this.shock / 100);
case FactionWorkType.Security:
return this.getFactionSecurityWorkRepGain();
return this.getFactionSecurityWorkRepGain() * (this.shock / 100);
default:
console.warn(`Invalid Sleeve.factionWorkType property in Sleeve.getRepGain(): ${this.factionWorkType}`);
return 0;
}
} else if (this.currentTask === SleeveTaskType.Company) {
return 0;
return 0; // TODO
} else {
console.warn(`Sleeve.getRepGain() called for invalid task type: ${this.currentTask}`);
return 0;
@ -327,16 +329,21 @@ export class Sleeve extends Person {
retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
// TODO REP for both this and company
const fac = Factions[this.currentTaskLocation];
// Gain faction reputation
const fac: Faction = Factions[this.currentTaskLocation];
if (!(fac instanceof Faction)) {
console.error(`Invalid faction for Sleeve task: ${this.currentTaskLocation}`);
break;
}
const repGainPerCycle: number = this.getRepGain();
fac.playerReputation += (repGainPerCycle * cyclesUsed);
break;
case SleeveTaskType.Company:
retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
// TODO Rep gain for this
break;
case SleeveTaskType.Recovery:
this.shock = Math.max(100, this.shock + (0.001 * this.storedCycles));

@ -0,0 +1,166 @@
/**
* Module for handling the Sleeve UI
*/
import { Sleeve } from "./Sleeve";
import { SleeveTaskType } from "./SleeveTaskTypesEnum";
import { IMap } from "../../types";
import { Page,
routing } from "../../ui/navigationTracking";
import { exceptionAlert } from "../../../utils/helpers/exceptionAlert";
import { createElement } from "../../../utils/uiHelpers/createElement";
import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement";
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: Element | null,
statsPanel: Element | null,
stats: Element | null,
statsTooltip: Element | null,
taskPanel: Element | null,
taskSelector: Element | null,
taskDetailsSelector: Element | null,
taskDescription: Element | null,
earningsPanel: Element | null,
currentEarningsInfo: Element | null,
totalEarningsInfo: Element | null,
}
// Object that keeps track of all DOM elements for the entire Sleeve UI
interface IPageUIElems {
container: Element | null;
info: Element | null,
sleeveList: Element | null,
sleeves: ISleeveUIElems[] | null,
}
const UIElems: IPageUIElems = {
container: null,
info: null,
sleeveList: null,
sleeves: null,
}
// Interface for Player object
interface IPlayer {
sleeves: Sleeve[];
}
// Creates the UI for the entire Sleeves page
export function createSleevesPage(p: IPlayer) {
if (!routing.isOn(Page.Sleeves)) { return; }
try {
UIElems.container = createElement("div", {
class: "generic-menupage-container",
id: "sleeves-container",
position: "fixed",
});
UIElems.info = createElement("p", {
display: "inline-block",
innerText: "Sleeves are MK-V Synthoids (synthetic androids) into which your " +
"consciousness has copied. In other words, these Synthoids contain " +
"a perfect duplicate of your mind.<br><br>" +
"Sleeves can be used to perform different tasks synchronously.",
});
UIElems.sleeveList = createElement("ul");
UIElems.sleeves = [];
for (const sleeve of p.sleeves) {
UIElems.sleeves.push(this.createSleeveUi(sleeve, p.sleeves));
}
UIElems.container.appendChild(UIElems.info);
UIElems.container.appendChild(UIElems.sleeveList);
document.getElementById("entire-game-container")!.appendChild(UIElems.container);
} catch(e) {
exceptionAlert(e);
}
}
// Updates the UI for the entire Sleeves page
export function updateSleevesPage() {
if (!routing.isOn(Page.Sleeves)) { return; }
}
export function clearSleevesPage() {
removeElement(UIElems.container);
for (const prop in UIElems) {
UIElems[prop] = null;
}
}
// Creates the UI for a single Sleeve
// Returns an object containing the DOM elements in the UI (ISleeveUIElems)
function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]) {
if (!routing.isOn(Page.Sleeves)) { return; }
const elems: ISleeveUIElems = {
container: null,
statsPanel: null,
stats: null,
statsTooltip: null,
taskPanel: null,
taskSelector: null,
taskDetailsSelector: null,
taskDescription: null,
earningsPanel: null,
currentEarningsInfo: null,
totalEarningsButton: null,
}
elems.container = createElement("div", {
class: "sleeve-container",
display: "block",
});
elems.statsPanel = createElement("div", { class: "sleeve-panel" });
elems.stats = createElement("p", { class: "sleeve-stats-text tooltip" });
elems.statsTooltip = createElement("span", { class: "tooltiptext" });
elems.stats.appendChild(elems.statsTooltip);
elems.statsPanel.appendChild(elems.stats);
elems.taskPanel = createElement("div", { class: "sleeve-panel" });
elems.taskSelector = createElement("select");
elems.taskSelector.add(createOptionElement("------"));
elems.taskSelector.add(createOptionElement("Work for Company"));
elems.taskSelector.add(createOptionElement("Work for Faction"));
elems.taskSelector.add(createOptionElement("Commit Crime"));
elems.taskSelector.add(createOptionElement("Take University Course"));
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);
});
// TODO Set initial value for task selector
elems.taskDetailsSelector = createElement("select");
elems.taskDescription = createElement("p");
elems.taskPanel.appendChild(elems.taskSelector);
elems.taskPanel.appendChild(elems.taskDetailsSelector);
elems.taskPanel.appendChild(elems.taskDescription);
elems.earningsPanel = createElement("div", { class: "sleeve-panel" });
elems.currentEarningsInfo = createElement("p");
elems.totalEarningsButton = createElement("button", { class: "std-button" });
return elems;
}
// Updates the UI for a single Sleeve
function updateSleeveUi() {
if (!routing.isOn(Page.Sleeves)) { return; }
}
// Whenever a new task is selected, the "details" selector must update accordingly
function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSleeves: Sleeve[]) {
const value: string =
}

@ -190,6 +190,9 @@ function PlayerObject() {
this.bladeburner_analysis_mult = 1; //Field Analysis Only
this.bladeburner_success_chance_mult = 1;
// Sleeves
this.sleeves = [];
//bitnode
this.bitNodeN = 1;

@ -112,6 +112,11 @@ export enum Page {
* Manage special Bladeburner activities.
*/
Bladeburner = "Bladeburner",
/**
* Manage your Sleeves
*/
Sleeves = "Sleeves",
}
/**

@ -1,16 +1,16 @@
import { dialogBoxCreate } from "../DialogBox";
function exceptionAlert(e) {
interface IError {
fileName?: string,
lineNumber?: number,
}
export function exceptionAlert(e: IError): void {
dialogBoxCreate("Caught an exception: " + e + "<br><br>" +
"Filename: " + (e.fileName || "UNKNOWN FILE NAME") + "<br><br>" +
"Line Number: " + (e.lineNumber || "UNKNOWN LINE NUMBER") + "<br><br>" +
"This is a bug, please report to game developer with this " +
"message as well as details about how to reproduce the bug.<br><br>" +
"If you want to be safe, I suggest refreshing the game WITHOUT saving so that your " +
"safe doesn't get corrupted");
"safe doesn't get corrupted", false);
}
export {
exceptionAlert
}

@ -0,0 +1,11 @@
import { createElement } from "./createElement";
export function createOptionElement(text: string, value: string="") {
const sanitizedValue: string = value;
if (sanitizedValue === "") { sanitizedValue = text; }
return createElement("option", {
text: text,
value: sanitizedValue,
});
}