2018-12-14 04:24:08 +01:00
|
|
|
// Defines a "Research Tree"
|
|
|
|
// Each Industry has a unique Research Tree
|
|
|
|
// Each Node in the Research Trees only holds the name(s) of Research,
|
|
|
|
// not an actual Research object. The name can be used to obtain a reference
|
|
|
|
// to the corresponding Research object using the ResearchMap
|
2018-12-16 04:47:15 +01:00
|
|
|
import { Research } from "./Research";
|
2018-12-14 04:24:08 +01:00
|
|
|
import { ResearchMap } from "./ResearchMap";
|
|
|
|
|
2018-12-22 11:27:04 +01:00
|
|
|
import { IMap } from "../types";
|
|
|
|
|
2019-03-15 10:37:06 +01:00
|
|
|
import { numeralWrapper } from "../ui/numeralFormat";
|
|
|
|
|
2018-12-14 04:24:08 +01:00
|
|
|
interface IConstructorParams {
|
2021-09-05 01:09:30 +02:00
|
|
|
children?: Node[];
|
|
|
|
cost: number;
|
|
|
|
text: string;
|
|
|
|
parent?: Node | null;
|
2018-12-14 04:24:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export class Node {
|
2021-09-05 01:09:30 +02:00
|
|
|
// All child Nodes in the tree
|
|
|
|
// The Research held in this Node is a prerequisite for all Research in
|
|
|
|
// child Nodes
|
|
|
|
children: Node[] = [];
|
|
|
|
|
|
|
|
// How much Scientific Research is needed for this
|
|
|
|
// Necessary to show it on the UI
|
|
|
|
cost = 0;
|
|
|
|
|
|
|
|
// Whether or not this Research has been unlocked
|
|
|
|
researched = false;
|
|
|
|
|
|
|
|
// Parent node in the tree
|
|
|
|
// The parent node defines the prerequisite Research (there can only be one)
|
|
|
|
// Set as null for no prerequisites
|
|
|
|
parent: Node | null = null;
|
|
|
|
|
|
|
|
// Name of the Research held in this Node
|
|
|
|
text = "";
|
|
|
|
|
|
|
|
constructor(p: IConstructorParams = { cost: 0, text: "" }) {
|
|
|
|
if (ResearchMap[p.text] == null) {
|
|
|
|
throw new Error(
|
|
|
|
`Invalid Research name used when constructing ResearchTree Node: ${p.text}`,
|
|
|
|
);
|
|
|
|
}
|
2018-12-27 03:38:07 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
this.text = p.text;
|
|
|
|
this.cost = p.cost;
|
2018-12-14 04:24:08 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (p.children && p.children.length > 0) {
|
|
|
|
this.children = p.children;
|
2018-12-14 04:24:08 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (p.parent != null) {
|
|
|
|
this.parent = p.parent;
|
2018-12-14 04:24:08 +01:00
|
|
|
}
|
2021-09-05 01:09:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
addChild(n: Node): void {
|
|
|
|
this.children.push(n);
|
|
|
|
n.parent = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an object that describes a TreantJS-compatible markup/config for this Node
|
|
|
|
// See: http://fperucic.github.io/treant-js/
|
|
|
|
createTreantMarkup(): any {
|
|
|
|
const childrenArray = [];
|
|
|
|
for (let i = 0; i < this.children.length; ++i) {
|
|
|
|
childrenArray.push(this.children[i].createTreantMarkup());
|
2018-12-16 00:31:21 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
// Determine what css class this Node should have in the diagram
|
|
|
|
let htmlClass = "";
|
|
|
|
if (this.researched) {
|
|
|
|
htmlClass = "researched";
|
|
|
|
} else if (this.parent && this.parent.researched === false) {
|
|
|
|
htmlClass = "locked";
|
|
|
|
} else {
|
|
|
|
htmlClass = "unlocked";
|
|
|
|
}
|
2018-12-16 00:31:21 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
const research: Research | null = ResearchMap[this.text];
|
|
|
|
const sanitizedName: string = this.text.replace(/\s/g, "");
|
|
|
|
return {
|
|
|
|
children: childrenArray,
|
|
|
|
HTMLclass: htmlClass,
|
|
|
|
innerHTML:
|
|
|
|
`<div id="${sanitizedName}-corp-research-click-listener" class="tooltip">` +
|
|
|
|
`${this.text}<br>${numeralWrapper.format(
|
|
|
|
this.cost,
|
|
|
|
"0,0",
|
|
|
|
)} Scientific Research` +
|
|
|
|
`<span class="tooltiptext">` +
|
|
|
|
`${research.desc}` +
|
|
|
|
`</span>` +
|
|
|
|
`</div>`,
|
|
|
|
text: { name: this.text },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Recursive function for finding a Node with the specified text
|
|
|
|
findNode(text: string): Node | null {
|
|
|
|
// Is this the Node?
|
|
|
|
if (this.text === text) {
|
|
|
|
return this;
|
2018-12-16 00:31:21 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
// Recursively search chilren
|
|
|
|
let res = null;
|
|
|
|
for (let i = 0; i < this.children.length; ++i) {
|
|
|
|
res = this.children[i].findNode(text);
|
|
|
|
if (res != null) {
|
|
|
|
return res;
|
|
|
|
}
|
2018-12-14 04:24:08 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
setParent(n: Node): void {
|
|
|
|
this.parent = n;
|
|
|
|
}
|
|
|
|
}
|
2018-12-27 03:38:07 +01:00
|
|
|
|
2018-12-14 04:24:08 +01:00
|
|
|
// A ResearchTree defines all available Research in an Industry
|
|
|
|
// The root node in a Research Tree must always be the "Hi-Tech R&D Laboratory"
|
|
|
|
export class ResearchTree {
|
2021-09-05 01:09:30 +02:00
|
|
|
// Object containing names of all acquired Research by name
|
|
|
|
researched: IMap<boolean> = {};
|
2018-12-16 00:31:21 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
// Root Node
|
|
|
|
root: Node | null = null;
|
2018-12-16 00:31:21 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
// Return an object that contains a Tree markup for TreantJS (using the JSON approach)
|
|
|
|
// See: http://fperucic.github.io/treant-js/
|
|
|
|
createTreantMarkup(): any {
|
|
|
|
if (this.root == null) {
|
|
|
|
return {};
|
2018-12-16 04:47:15 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
const treeMarkup = this.root.createTreantMarkup();
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return {
|
|
|
|
chart: {
|
|
|
|
container: "",
|
|
|
|
},
|
|
|
|
nodeStructure: treeMarkup,
|
|
|
|
};
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
// Gets an array with the 'text' values of ALL Nodes in the Research Tree
|
|
|
|
getAllNodes(): string[] {
|
|
|
|
const res: string[] = [];
|
|
|
|
const queue: Node[] = [];
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
if (this.root == null) {
|
|
|
|
return res;
|
2018-12-16 04:47:15 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
queue.push(this.root);
|
|
|
|
while (queue.length !== 0) {
|
|
|
|
const node: Node | undefined = queue.shift();
|
|
|
|
if (node == null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
res.push(node.text);
|
|
|
|
for (let i = 0; i < node.children.length; ++i) {
|
|
|
|
queue.push(node.children[i]);
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return res;
|
|
|
|
}
|
2019-03-13 23:17:30 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
// Get total multipliers from this Research Tree
|
|
|
|
getAdvertisingMultiplier(): number {
|
|
|
|
return this.getMultiplierHelper("advertisingMult");
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
getEmployeeChaMultiplier(): number {
|
|
|
|
return this.getMultiplierHelper("employeeChaMult");
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
getEmployeeCreMultiplier(): number {
|
|
|
|
return this.getMultiplierHelper("employeeCreMult");
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
getEmployeeEffMultiplier(): number {
|
|
|
|
return this.getMultiplierHelper("employeeEffMult");
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
getEmployeeIntMultiplier(): number {
|
|
|
|
return this.getMultiplierHelper("employeeIntMult");
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
getProductionMultiplier(): number {
|
|
|
|
return this.getMultiplierHelper("productionMult");
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
getProductProductionMultiplier(): number {
|
|
|
|
return this.getMultiplierHelper("productProductionMult");
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
getSalesMultiplier(): number {
|
|
|
|
return this.getMultiplierHelper("salesMult");
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
getScientificResearchMultiplier(): number {
|
|
|
|
return this.getMultiplierHelper("sciResearchMult");
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
getStorageMultiplier(): number {
|
|
|
|
return this.getMultiplierHelper("storageMult");
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
// Helper function for all the multiplier getter fns
|
|
|
|
getMultiplierHelper(propName: string): number {
|
|
|
|
let res = 1;
|
|
|
|
if (this.root == null) {
|
|
|
|
return res;
|
2018-12-16 04:47:15 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
const queue: Node[] = [];
|
|
|
|
queue.push(this.root);
|
|
|
|
while (queue.length !== 0) {
|
|
|
|
const node: Node | undefined = queue.shift();
|
|
|
|
|
|
|
|
// If the Node has not been researched, there's no need to
|
|
|
|
// process it or its children
|
|
|
|
if (node == null || !node.researched) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const research: Research | null = ResearchMap[node.text];
|
|
|
|
|
|
|
|
// Safety checks
|
|
|
|
if (research == null) {
|
|
|
|
console.warn(`Invalid Research name in node: ${node.text}`);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-09-05 01:57:49 +02:00
|
|
|
const mult: any = (research as any)[propName];
|
2021-09-05 01:09:30 +02:00
|
|
|
if (mult == null) {
|
|
|
|
console.warn(
|
|
|
|
`Invalid propName specified in ResearchTree.getMultiplierHelper: ${propName}`,
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
res *= mult;
|
|
|
|
for (let i = 0; i < node.children.length; ++i) {
|
|
|
|
queue.push(node.children[i]);
|
|
|
|
}
|
2018-12-16 00:31:21 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
return res;
|
|
|
|
}
|
2018-12-16 00:31:21 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
// Search for a Node with the given name ('text' property on the Node)
|
|
|
|
// Returns 'null' if it cannot be found
|
|
|
|
findNode(name: string): Node | null {
|
|
|
|
if (this.root == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return this.root.findNode(name);
|
|
|
|
}
|
2018-12-16 04:47:15 +01:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
// Marks a Node as researched
|
|
|
|
research(name: string): void {
|
|
|
|
if (this.root == null) {
|
|
|
|
return;
|
2018-12-16 00:31:21 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
const queue: Node[] = [];
|
|
|
|
queue.push(this.root);
|
|
|
|
while (queue.length !== 0) {
|
|
|
|
const node: Node | undefined = queue.shift();
|
|
|
|
if (node == null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node.text === name) {
|
|
|
|
node.researched = true;
|
|
|
|
this.researched[name] = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < node.children.length; ++i) {
|
|
|
|
queue.push(node.children[i]);
|
|
|
|
}
|
2018-12-14 04:24:08 +01:00
|
|
|
}
|
2021-09-05 01:09:30 +02:00
|
|
|
|
|
|
|
console.warn(
|
|
|
|
`ResearchTree.research() did not find the specified Research node for: ${name}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the tree's Root Node
|
|
|
|
setRoot(root: Node): void {
|
|
|
|
this.root = root;
|
|
|
|
}
|
2018-12-14 04:24:08 +01:00
|
|
|
}
|