mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-22 15:43:49 +01:00
more conversion
This commit is contained in:
parent
21008ba65a
commit
a72d1aa99f
3
src/Corporation/Corporation.d.ts
vendored
Normal file
3
src/Corporation/Corporation.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
export class Industry {
|
||||
constructor(props: any)
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
import { ResearchTree } from "./ResearchTree";
|
||||
import { getBaseResearchTreeCopy,
|
||||
getProductIndustryResearchTreeCopy } from "./data/BaseResearchTree";
|
||||
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
|
||||
interface IIndustryMap<T> {
|
||||
Energy: T;
|
||||
Utilities: T;
|
||||
Agriculture: T;
|
||||
Fishing: T;
|
||||
Mining: T;
|
||||
Food: T;
|
||||
Tobacco: T;
|
||||
Chemical: T;
|
||||
Pharmaceutical: T;
|
||||
Computer: T;
|
||||
Robotics: T;
|
||||
Software: T;
|
||||
Healthcare: T;
|
||||
RealEstate: T;
|
||||
}
|
||||
|
||||
// Map of official names for each Industry
|
||||
export const Industries: IIndustryMap<string> = {
|
||||
Energy: "Energy",
|
||||
Utilities: "Water Utilities",
|
||||
Agriculture: "Agriculture",
|
||||
Fishing: "Fishing",
|
||||
Mining: "Mining",
|
||||
Food: "Food",
|
||||
Tobacco: "Tobacco",
|
||||
Chemical: "Chemical",
|
||||
Pharmaceutical: "Pharmaceutical",
|
||||
Computer: "Computer Hardware",
|
||||
Robotics: "Robotics",
|
||||
Software: "Software",
|
||||
Healthcare: "Healthcare",
|
||||
RealEstate: "RealEstate",
|
||||
}
|
||||
|
||||
// Map of how much money it takes to start each industry
|
||||
export const IndustryStartingCosts: IIndustryMap<number> = {
|
||||
Energy: 225e9,
|
||||
Utilities: 150e9,
|
||||
Agriculture: 40e9,
|
||||
Fishing: 80e9,
|
||||
Mining: 300e9,
|
||||
Food: 10e9,
|
||||
Tobacco: 20e9,
|
||||
Chemical: 70e9,
|
||||
Pharmaceutical: 200e9,
|
||||
Computer: 500e9,
|
||||
Robotics: 1e12,
|
||||
Software: 25e9,
|
||||
Healthcare: 750e9,
|
||||
RealEstate: 600e9,
|
||||
}
|
||||
|
||||
// Map of description for each industry
|
||||
export const IndustryDescriptions: IIndustryMap<string> = {
|
||||
Energy: "Engage in the production and distribution of energy.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Energy, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: NO",
|
||||
Utilities: "Distribute water and provide wastewater services.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Utilities, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: NO",
|
||||
Agriculture: "Cultivate crops and breed livestock to produce food.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Agriculture, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: YES",
|
||||
Fishing: "Produce food through the breeding and processing of fish and fish products.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Fishing, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: NO",
|
||||
Mining: "Extract and process metals from the earth.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Mining, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: NO",
|
||||
Food: "Create your own restaurants all around the world.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Food, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: YES",
|
||||
Tobacco: "Create and distribute tobacco and tobacco-related products.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Tobacco, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: YES",
|
||||
Chemical: "Produce industrial chemicals.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Chemical, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: NO",
|
||||
Pharmaceutical: "Discover, develop, and create new pharmaceutical drugs.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Pharmaceutical, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: NO",
|
||||
Computer: "Develop and manufacture new computer hardware and networking infrastructures.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Computer, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: NO",
|
||||
Robotics: "Develop and create robots.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Robotics, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: NO",
|
||||
Software: "Develop computer software and create AI Cores.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Software, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: YES",
|
||||
Healthcare: "Create and manage hospitals.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.Healthcare, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: NO",
|
||||
RealEstate: "Develop and manage real estate properties.<br><br>" +
|
||||
"Starting cost: " + numeralWrapper.format(IndustryStartingCosts.RealEstate, "$0.000a") + "<br>" +
|
||||
"Recommended starting Industry: NO",
|
||||
}
|
||||
|
||||
// Map of available Research for each Industry. This data is held in a
|
||||
// ResearchTree object
|
||||
export const IndustryResearchTrees: IIndustryMap<ResearchTree> = {
|
||||
Energy: getBaseResearchTreeCopy(),
|
||||
Utilities: getBaseResearchTreeCopy(),
|
||||
Agriculture: getBaseResearchTreeCopy(),
|
||||
Fishing: getBaseResearchTreeCopy(),
|
||||
Mining: getBaseResearchTreeCopy(),
|
||||
Food: getProductIndustryResearchTreeCopy(),
|
||||
Tobacco: getProductIndustryResearchTreeCopy(),
|
||||
Chemical: getBaseResearchTreeCopy(),
|
||||
Pharmaceutical: getProductIndustryResearchTreeCopy(),
|
||||
Computer: getProductIndustryResearchTreeCopy(),
|
||||
Robotics: getProductIndustryResearchTreeCopy(),
|
||||
Software: getProductIndustryResearchTreeCopy(),
|
||||
Healthcare: getProductIndustryResearchTreeCopy(),
|
||||
RealEstate: getProductIndustryResearchTreeCopy(),
|
||||
}
|
||||
|
||||
export function resetIndustryResearchTrees(): void {
|
||||
IndustryResearchTrees.Energy = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Utilities = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Agriculture = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Fishing = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Mining = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Food = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Tobacco = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Chemical = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Pharmaceutical = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Computer = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Robotics = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Software = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Healthcare = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.RealEstate = getBaseResearchTreeCopy();
|
||||
}
|
143
src/Corporation/IndustryData.tsx
Normal file
143
src/Corporation/IndustryData.tsx
Normal file
@ -0,0 +1,143 @@
|
||||
import React from 'react';
|
||||
import { ResearchTree } from "./ResearchTree";
|
||||
import { getBaseResearchTreeCopy,
|
||||
getProductIndustryResearchTreeCopy } from "./data/BaseResearchTree";
|
||||
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
import { Money } from "../ui/React/Money";
|
||||
|
||||
interface IIndustryMap<T> {
|
||||
[key: string]: T | undefined;
|
||||
Energy: T;
|
||||
Utilities: T;
|
||||
Agriculture: T;
|
||||
Fishing: T;
|
||||
Mining: T;
|
||||
Food: T;
|
||||
Tobacco: T;
|
||||
Chemical: T;
|
||||
Pharmaceutical: T;
|
||||
Computer: T;
|
||||
Robotics: T;
|
||||
Software: T;
|
||||
Healthcare: T;
|
||||
RealEstate: T;
|
||||
}
|
||||
|
||||
// Map of official names for each Industry
|
||||
export const Industries: IIndustryMap<string> = {
|
||||
Energy: "Energy",
|
||||
Utilities: "Water Utilities",
|
||||
Agriculture: "Agriculture",
|
||||
Fishing: "Fishing",
|
||||
Mining: "Mining",
|
||||
Food: "Food",
|
||||
Tobacco: "Tobacco",
|
||||
Chemical: "Chemical",
|
||||
Pharmaceutical: "Pharmaceutical",
|
||||
Computer: "Computer Hardware",
|
||||
Robotics: "Robotics",
|
||||
Software: "Software",
|
||||
Healthcare: "Healthcare",
|
||||
RealEstate: "RealEstate",
|
||||
}
|
||||
|
||||
// Map of how much money it takes to start each industry
|
||||
export const IndustryStartingCosts: IIndustryMap<number> = {
|
||||
Energy: 225e9,
|
||||
Utilities: 150e9,
|
||||
Agriculture: 40e9,
|
||||
Fishing: 80e9,
|
||||
Mining: 300e9,
|
||||
Food: 10e9,
|
||||
Tobacco: 20e9,
|
||||
Chemical: 70e9,
|
||||
Pharmaceutical: 200e9,
|
||||
Computer: 500e9,
|
||||
Robotics: 1e12,
|
||||
Software: 25e9,
|
||||
Healthcare: 750e9,
|
||||
RealEstate: 600e9,
|
||||
}
|
||||
|
||||
// Map of description for each industry
|
||||
export const IndustryDescriptions: IIndustryMap<JSX.Element> = {
|
||||
Energy: (<>Engage in the production and distribution of energy.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Energy)}<br />
|
||||
Recommended starting Industry: NO</>),
|
||||
Utilities: (<>Distribute water and provide wastewater services.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Utilities)}<br />
|
||||
Recommended starting Industry: NO</>),
|
||||
Agriculture: (<>Cultivate crops and breed livestock to produce food.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Agriculture)}<br />
|
||||
Recommended starting Industry: YES</>),
|
||||
Fishing: (<>Produce food through the breeding and processing of fish and fish products.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Fishing)}<br />
|
||||
Recommended starting Industry: NO</>),
|
||||
Mining: (<>Extract and process metals from the earth.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Mining)}<br />
|
||||
Recommended starting Industry: NO</>),
|
||||
Food: (<>Create your own restaurants all around the world.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Food)}<br />
|
||||
Recommended starting Industry: YES</>),
|
||||
Tobacco: (<>Create and distribute tobacco and tobacco-related products.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Tobacco)}<br />
|
||||
Recommended starting Industry: YES</>),
|
||||
Chemical: (<>Produce industrial chemicals.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Chemical)}<br />
|
||||
Recommended starting Industry: NO</>),
|
||||
Pharmaceutical: (<>Discover, develop, and create new pharmaceutical drugs.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Pharmaceutical)}<br />
|
||||
Recommended starting Industry: NO</>),
|
||||
Computer: (<>Develop and manufacture new computer hardware and networking infrastructures.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Computer)}<br />
|
||||
Recommended starting Industry: NO</>),
|
||||
Robotics: (<>Develop and create robots.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Robotics)}<br />
|
||||
Recommended starting Industry: NO</>),
|
||||
Software: (<>Develop computer software and create AI Cores.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Software)}<br />
|
||||
Recommended starting Industry: YES</>),
|
||||
Healthcare: (<>Create and manage hospitals.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.Healthcare)}<br />
|
||||
Recommended starting Industry: NO</>),
|
||||
RealEstate: (<>Develop and manage real estate properties.<br /><br />
|
||||
Starting cost: {Money(IndustryStartingCosts.RealEstate)}<br />
|
||||
Recommended starting Industry: NO</>),
|
||||
}
|
||||
|
||||
// Map of available Research for each Industry. This data is held in a
|
||||
// ResearchTree object
|
||||
export const IndustryResearchTrees: IIndustryMap<ResearchTree> = {
|
||||
Energy: getBaseResearchTreeCopy(),
|
||||
Utilities: getBaseResearchTreeCopy(),
|
||||
Agriculture: getBaseResearchTreeCopy(),
|
||||
Fishing: getBaseResearchTreeCopy(),
|
||||
Mining: getBaseResearchTreeCopy(),
|
||||
Food: getProductIndustryResearchTreeCopy(),
|
||||
Tobacco: getProductIndustryResearchTreeCopy(),
|
||||
Chemical: getBaseResearchTreeCopy(),
|
||||
Pharmaceutical: getProductIndustryResearchTreeCopy(),
|
||||
Computer: getProductIndustryResearchTreeCopy(),
|
||||
Robotics: getProductIndustryResearchTreeCopy(),
|
||||
Software: getProductIndustryResearchTreeCopy(),
|
||||
Healthcare: getProductIndustryResearchTreeCopy(),
|
||||
RealEstate: getProductIndustryResearchTreeCopy(),
|
||||
}
|
||||
|
||||
export function resetIndustryResearchTrees(): void {
|
||||
IndustryResearchTrees.Energy = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Utilities = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Agriculture = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Fishing = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Mining = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Food = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Tobacco = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Chemical = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Pharmaceutical = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Computer = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Robotics = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Software = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.Healthcare = getBaseResearchTreeCopy();
|
||||
IndustryResearchTrees.RealEstate = getBaseResearchTreeCopy();
|
||||
}
|
@ -47,666 +47,6 @@ export class CorporationEventHandler {
|
||||
this.routing = routing;
|
||||
}
|
||||
|
||||
// Create a popup that lets the player manage exports
|
||||
createExportMaterialPopup(mat) {
|
||||
const corp = this.corp;
|
||||
|
||||
const popupId = "cmpy-mgmt-export-popup";
|
||||
const exportTxt = createElement("p", {
|
||||
innerText:"Select the industry and city to export this material to, as well as " +
|
||||
"how much of this material to export per second. You can set the export " +
|
||||
"amount to 'MAX' to export all of the materials in this warehouse.",
|
||||
});
|
||||
|
||||
//Select industry and city to export to
|
||||
const citySelector = createElement("select", {class: "dropdown"});
|
||||
const industrySelector = createElement("select", {
|
||||
class: "dropdown",
|
||||
changeListener: () => {
|
||||
const industryName = getSelectValue(industrySelector);
|
||||
for (let i = 0; i < corp.divisions.length; ++i) {
|
||||
if (corp.divisions[i].name == industryName) {
|
||||
clearSelector(citySelector);
|
||||
for (const cityName in corp.divisions[i].warehouses) {
|
||||
if (corp.divisions[i].warehouses[cityName] instanceof Warehouse) {
|
||||
citySelector.add(createElement("option", {
|
||||
value:cityName, text:cityName,
|
||||
}));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
for (let i = 0; i < corp.divisions.length; ++i) {
|
||||
industrySelector.add(createOptionElement(corp.divisions[i].name));
|
||||
}
|
||||
|
||||
// Force change listener to initialize citySelector
|
||||
industrySelector.dispatchEvent(new Event("change"));
|
||||
|
||||
//Select amount to export
|
||||
const exportAmount = createElement("input", {
|
||||
class: "text-input",
|
||||
placeholder:"Export amount / s",
|
||||
});
|
||||
|
||||
const exportBtn = createElement("button", {
|
||||
class: "std-button", display:"inline-block", innerText:"Export",
|
||||
clickListener: () => {
|
||||
const industryName = getSelectText(industrySelector);
|
||||
const cityName = citySelector.options[citySelector.selectedIndex].text;
|
||||
const amt = exportAmount.value;
|
||||
|
||||
// Sanitize amt
|
||||
let sanitizedAmt = amt.replace(/\s+/g, '');
|
||||
sanitizedAmt = sanitizedAmt.replace(/[^-()\d/*+.MAX]/g, '');
|
||||
let temp = sanitizedAmt.replace(/MAX/g, 1);
|
||||
try {
|
||||
temp = eval(temp);
|
||||
} catch(e) {
|
||||
dialogBoxCreate("Invalid expression entered for export amount: " + e);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (temp == null || isNaN(temp) || temp < 0) {
|
||||
dialogBoxCreate("Invalid amount entered for export");
|
||||
return;
|
||||
}
|
||||
var exportObj = {ind:industryName, city:cityName, amt:sanitizedAmt};
|
||||
mat.exp.push(exportObj);
|
||||
removeElementById(popupId);
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
const cancelBtn = createPopupCloseButton(popupId, { innerText: "Cancel" });
|
||||
|
||||
const currExportsText = createElement("p", {
|
||||
innerText:"Below is a list of all current exports of this material from this warehouse. " +
|
||||
"Clicking on one of the exports below will REMOVE that export.",
|
||||
});
|
||||
const currExports = [];
|
||||
for (var i = 0; i < mat.exp.length; ++i) {
|
||||
(function(i, mat, currExports){
|
||||
currExports.push(createElement("div", {
|
||||
class:"cmpy-mgmt-existing-export",
|
||||
innerHTML: "Industry: " + mat.exp[i].ind + "<br>" +
|
||||
"City: " + mat.exp[i].city + "<br>" +
|
||||
"Amount/s: " + mat.exp[i].amt,
|
||||
clickListener:()=>{
|
||||
mat.exp.splice(i, 1); //Remove export object
|
||||
removeElementById(popupId);
|
||||
createExportMaterialPopup(mat);
|
||||
},
|
||||
}));
|
||||
})(i, mat, currExports);
|
||||
}
|
||||
createPopup(popupId, [exportTxt, industrySelector, citySelector, exportAmount,
|
||||
exportBtn, cancelBtn, currExportsText].concat(currExports));
|
||||
}
|
||||
|
||||
// Create a popup that lets the player issue & manage dividends
|
||||
// This is created when the player clicks the "Issue Dividends" button in the overview panel
|
||||
createIssueDividendsPopup() {
|
||||
const popupId = "cmpy-mgmt-issue-dividends-popup";
|
||||
const descText = "Dividends are a distribution of a portion of the corporation's " +
|
||||
"profits to the shareholders. This includes yourself, as well.<br><br>" +
|
||||
"In order to issue dividends, simply allocate some percentage " +
|
||||
"of your corporation's profits to dividends. This percentage must be an " +
|
||||
`integer between 0 and ${DividendMaxPercentage}. (A percentage of 0 means no dividends will be ` +
|
||||
"issued<br><br>" +
|
||||
"Two important things to note:<br>" +
|
||||
" * Issuing dividends will negatively affect your corporation's stock price<br>" +
|
||||
" * Dividends are taxed. Taxes start at 50%, but can be decreased<br><br>" +
|
||||
"Example: Assume your corporation makes $100m / sec in profit and you allocate " +
|
||||
"40% of that towards dividends. That means your corporation will gain $60m / sec " +
|
||||
"in funds and the remaining $40m / sec will be paid as dividends. Since your " +
|
||||
"corporation starts with 1 billion shares, every shareholder will be paid $0.04 per share " +
|
||||
"per second before taxes.";
|
||||
const txt = createElement("p", { innerHTML: descText });
|
||||
|
||||
let allocateBtn;
|
||||
const dividendPercentInput = createElement("input", {
|
||||
margin: "5px",
|
||||
placeholder: "Dividend %",
|
||||
type: "number",
|
||||
onkeyup: (e) => {
|
||||
e.preventDefault();
|
||||
if (e.keyCode === KEY.ENTER) {allocateBtn.click();}
|
||||
},
|
||||
});
|
||||
|
||||
allocateBtn = createElement("button", {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
innerText: "Allocate Dividend Percentage",
|
||||
clickListener: () => {
|
||||
const percentage = Math.round(parseInt(dividendPercentInput.value));
|
||||
if (isNaN(percentage) || percentage < 0 || percentage > DividendMaxPercentage) {
|
||||
return dialogBoxCreate(`Invalid value. Must be an integer between 0 and ${DividendMaxPercentage}`);
|
||||
}
|
||||
|
||||
this.corp.dividendPercentage = percentage;
|
||||
|
||||
removeElementById(popupId);
|
||||
|
||||
this.rerender();
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
const cancelBtn = createPopupCloseButton(popupId, {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
innerText: "Cancel",
|
||||
});
|
||||
|
||||
createPopup(popupId, [txt, dividendPercentInput, allocateBtn, cancelBtn]);
|
||||
dividendPercentInput.focus();
|
||||
}
|
||||
|
||||
// Create a popup that lets the player issue new shares
|
||||
// This is created when the player clicks the "Issue New Shares" buttons in the overview panel
|
||||
createIssueNewSharesPopup() {
|
||||
const popupId = "cmpy-mgmt-issue-new-shares-popup";
|
||||
const maxNewSharesUnrounded = Math.round(this.corp.totalShares * 0.2);
|
||||
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
|
||||
|
||||
const descText = createElement("p", {
|
||||
innerHTML: "You can issue new equity shares (i.e. stocks) in order to raise " +
|
||||
"capital for your corporation.<br><br>" +
|
||||
` * You can issue at most ${numeralWrapper.format(maxNewShares, "0.000a")} new shares<br>` +
|
||||
` * New shares are sold at a 10% discount<br>` +
|
||||
` * You can only issue new shares once every 12 hours<br>` +
|
||||
` * Issuing new shares causes dilution, resulting in a decrease in stock price and lower dividends per share<br>` +
|
||||
` * Number of new shares issued must be a multiple of 10 million<br><br>` +
|
||||
`When you choose to issue new equity, private shareholders have first priority for up to 50% of the new shares. ` +
|
||||
`If they choose to exercise this option, these newly issued shares become private, restricted shares, which means ` +
|
||||
`you cannot buy them back.`,
|
||||
});
|
||||
|
||||
let issueBtn, newSharesInput;
|
||||
const dynamicText = createElement("p", {
|
||||
display: "block",
|
||||
});
|
||||
|
||||
function updateDynamicText(corp) {
|
||||
const newSharePrice = Math.round(corp.sharePrice * 0.9);
|
||||
let newShares = parseInt(newSharesInput.value);
|
||||
if (isNaN(newShares)) {
|
||||
dynamicText.innerText = "Invalid input";
|
||||
return;
|
||||
}
|
||||
|
||||
// Round to nearest ten-millionth
|
||||
newShares /= 10e6;
|
||||
newShares = Math.round(newShares) * 10e6;
|
||||
|
||||
if (newShares < 10e6) {
|
||||
dynamicText.innerText = "Must issue at least 10 million new shares";
|
||||
return;
|
||||
}
|
||||
|
||||
if (newShares > maxNewShares) {
|
||||
dynamicText.innerText = "You cannot issue that many shares";
|
||||
return;
|
||||
}
|
||||
|
||||
dynamicText.innerText = `Issue ${numeralWrapper.format(newShares, "0.000a")} new shares ` +
|
||||
`for ${numeralWrapper.formatMoney(newShares * newSharePrice)}?`
|
||||
}
|
||||
newSharesInput = createElement("input", {
|
||||
margin: "5px",
|
||||
placeholder: "# New Shares",
|
||||
type: "number",
|
||||
onkeyup: (e) => {
|
||||
e.preventDefault();
|
||||
if (e.keyCode === KEY.ENTER) {
|
||||
issueBtn.click();
|
||||
} else {
|
||||
updateDynamicText(this.corp);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
issueBtn = createElement("button", {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
innerText: "Issue New Shares",
|
||||
clickListener: () => {
|
||||
const newSharePrice = Math.round(this.corp.sharePrice * 0.9);
|
||||
let newShares = parseInt(newSharesInput.value);
|
||||
if (isNaN(newShares)) {
|
||||
dialogBoxCreate("Invalid input for number of new shares");
|
||||
return;
|
||||
}
|
||||
|
||||
// Round to nearest ten-millionth
|
||||
newShares = Math.round(newShares / 10e6) * 10e6;
|
||||
|
||||
if (newShares < 10e6 || newShares > maxNewShares) {
|
||||
dialogBoxCreate("Invalid input for number of new shares");
|
||||
return;
|
||||
}
|
||||
|
||||
const profit = newShares * newSharePrice;
|
||||
this.corp.issueNewSharesCooldown = IssueNewSharesCooldown;
|
||||
this.corp.totalShares += newShares;
|
||||
|
||||
// Determine how many are bought by private investors
|
||||
// Private investors get up to 50% at most
|
||||
// Round # of private shares to the nearest millionth
|
||||
let privateShares = getRandomInt(0, Math.round(newShares / 2));
|
||||
privateShares = Math.round(privateShares / 1e6) * 1e6;
|
||||
|
||||
this.corp.issuedShares += (newShares - privateShares);
|
||||
this.corp.funds = this.corp.funds.plus(profit);
|
||||
this.corp.immediatelyUpdateSharePrice();
|
||||
|
||||
removeElementById(popupId);
|
||||
dialogBoxCreate(`Issued ${numeralWrapper.format(newShares, "0.000a")} and raised ` +
|
||||
`${numeralWrapper.formatMoney(profit)}. ${numeralWrapper.format(privateShares, "0.000a")} ` +
|
||||
`of these shares were bought by private investors.<br><br>` +
|
||||
`Stock price decreased to ${numeralWrapper.formatMoney(this.corp.sharePrice)}`);
|
||||
|
||||
this.rerender();
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
const cancelBtn = createPopupCloseButton(popupId, {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
innerText: "Cancel",
|
||||
});
|
||||
|
||||
createPopup(popupId, [descText, dynamicText, newSharesInput, issueBtn, cancelBtn]);
|
||||
newSharesInput.focus();
|
||||
}
|
||||
|
||||
// Create a popup that lets the player limit the production of a product
|
||||
createLimitProductProdutionPopup(product, city) {
|
||||
const popupId = "cmpy-mgmt-limit-product-production-popup";
|
||||
const txt = createElement("p", {
|
||||
innerText:"Enter a limit to the amount of this product you would " +
|
||||
"like to product per second. Leave the box empty to set no limit.",
|
||||
});
|
||||
let confirmBtn;
|
||||
const input = createElement("input", {
|
||||
margin: "5px",
|
||||
placeholder:"Limit",
|
||||
type:"number",
|
||||
onkeyup: (e) => {
|
||||
e.preventDefault();
|
||||
if (e.keyCode === KEY.ENTER) { confirmBtn.click(); }
|
||||
},
|
||||
});
|
||||
confirmBtn = createElement("button", {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
innerText: "Limit production",
|
||||
margin: "5px",
|
||||
clickListener: () => {
|
||||
if (input.value === "") {
|
||||
product.prdman[city][0] = false;
|
||||
removeElementById(popupId);
|
||||
return false;
|
||||
}
|
||||
var qty = parseFloat(input.value);
|
||||
if (isNaN(qty)) {
|
||||
dialogBoxCreate("Invalid value entered");
|
||||
return false;
|
||||
}
|
||||
if (qty < 0) {
|
||||
product.prdman[city][0] = false;
|
||||
} else {
|
||||
product.prdman[city][0] = true;
|
||||
product.prdman[city][1] = qty;
|
||||
}
|
||||
removeElementById(popupId);
|
||||
this.rerender();
|
||||
return false;
|
||||
},
|
||||
});
|
||||
const cancelBtn = createPopupCloseButton(popupId, { innerText: "Cancel" });
|
||||
cancelBtn.style.margin = "6px";
|
||||
|
||||
createPopup(popupId, [txt, input, confirmBtn, cancelBtn]);
|
||||
input.focus();
|
||||
}
|
||||
|
||||
// Create a popup that lets the player create a product for their current industry
|
||||
createMakeProductPopup(popupText, division) {
|
||||
if (division.hasMaximumNumberProducts()) { return; }
|
||||
|
||||
const popupId = "cmpy-mgmt-create-product-popup";
|
||||
const txt = createElement("p", {
|
||||
innerHTML: popupText,
|
||||
});
|
||||
const designCity = createElement("select", {
|
||||
class: "dropdown",
|
||||
margin: "5px",
|
||||
});
|
||||
for (const cityName in division.offices) {
|
||||
if (division.offices[cityName] instanceof OfficeSpace) {
|
||||
designCity.add(createElement("option", {
|
||||
value: cityName,
|
||||
text: cityName,
|
||||
}));
|
||||
}
|
||||
}
|
||||
let productNamePlaceholder = "Product Name";
|
||||
if (division.type === Industries.Food) {
|
||||
productNamePlaceholder = "Restaurant Name";
|
||||
} else if (division.type === Industries.Healthcare) {
|
||||
productNamePlaceholder = "Hospital Name";
|
||||
} else if (division.type === Industries.RealEstate) {
|
||||
productNamePlaceholder = "Property Name";
|
||||
}
|
||||
var productNameInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: productNamePlaceholder,
|
||||
});
|
||||
var lineBreak1 = createElement("br");
|
||||
var designInvestInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "Design investment",
|
||||
type: "number",
|
||||
});
|
||||
let confirmBtn;
|
||||
var marketingInvestInput = createElement("input", {
|
||||
class: "text-input",
|
||||
margin: "5px",
|
||||
placeholder: "Marketing investment",
|
||||
type: "number",
|
||||
onkeyup: (e) => {
|
||||
e.preventDefault();
|
||||
if (e.keyCode === KEY.ENTER) { confirmBtn.click(); }
|
||||
},
|
||||
});
|
||||
confirmBtn = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Develop Product",
|
||||
clickListener: () => {
|
||||
if (designInvestInput.value == null || designInvestInput.value < 0) { designInvestInput.value = 0; }
|
||||
if (marketingInvestInput.value == null || marketingInvestInput.value < 0) { marketingInvestInput.value = 0; }
|
||||
var designInvest = parseFloat(designInvestInput.value),
|
||||
marketingInvest = parseFloat(marketingInvestInput.value);
|
||||
if (productNameInput.value == null || productNameInput.value === "") {
|
||||
dialogBoxCreate("You must specify a name for your product!");
|
||||
} else if (isNaN(designInvest)) {
|
||||
dialogBoxCreate("Invalid value for design investment");
|
||||
} else if (isNaN(marketingInvest)) {
|
||||
dialogBoxCreate("Invalid value for marketing investment");
|
||||
} else if (this.corp.funds.lt(designInvest + marketingInvest)) {
|
||||
dialogBoxCreate("You don't have enough company funds to make this large of an investment");
|
||||
} else {
|
||||
const product = new Product({
|
||||
name:productNameInput.value.replace(/[<>]/g, ''), //Sanitize for HTMl elements
|
||||
createCity:designCity.options[designCity.selectedIndex].value,
|
||||
designCost: designInvest,
|
||||
advCost: marketingInvest,
|
||||
});
|
||||
if (division.products[product.name] instanceof Product) {
|
||||
dialogBoxCreate(`You already have a product with this name!`);
|
||||
return;
|
||||
}
|
||||
this.corp.funds = this.corp.funds.minus(designInvest + marketingInvest);
|
||||
division.products[product.name] = product;
|
||||
removeElementById(popupId);
|
||||
}
|
||||
this.rerender();
|
||||
return false;
|
||||
},
|
||||
})
|
||||
const cancelBtn = createPopupCloseButton(popupId, {
|
||||
class: "std-button",
|
||||
innerText: "Cancel",
|
||||
});
|
||||
|
||||
createPopup(popupId, [txt, designCity, productNameInput, lineBreak1,
|
||||
designInvestInput, marketingInvestInput, confirmBtn, cancelBtn]);
|
||||
productNameInput.focus();
|
||||
}
|
||||
|
||||
// Create a popup that lets the player use the Market TA research for Materials
|
||||
createMaterialMarketTaPopup(mat, industry) {
|
||||
const popupId = "cmpy-mgmt-marketta-popup";
|
||||
const markupLimit = mat.getMarkupLimit();
|
||||
const ta1 = createElement("p", {
|
||||
innerHTML: "<u><strong>Market-TA.I</strong></u><br>" +
|
||||
"The maximum sale price you can mark this up to is " +
|
||||
numeralWrapper.formatMoney(mat.bCost + markupLimit) +
|
||||
". This means that if you set the sale price higher than this, " +
|
||||
"you will begin to experience a loss in number of sales",
|
||||
});
|
||||
|
||||
// Enable using Market-TA1 for automatically setting sale price
|
||||
const useTa1AutoSaleId = "cmpy-mgmt-marketa1-checkbox";
|
||||
const useTa1AutoSaleDiv = createElement("div", { display: "block" });
|
||||
const useTa1AutoSaleLabel = createElement("label", {
|
||||
color: "white",
|
||||
for: useTa1AutoSaleId,
|
||||
innerText: "Use Market-TA.I for Auto-Sale Price",
|
||||
tooltip: "If this is enabled, then this Material will automatically " +
|
||||
"be sold at the price identified by Market-TA.I (i.e. the price shown above)",
|
||||
})
|
||||
const useTa1AutoSaleCheckbox = createElement("input", {
|
||||
checked: mat.marketTa1,
|
||||
id: useTa1AutoSaleId,
|
||||
margin: "3px",
|
||||
type: "checkbox",
|
||||
changeListener: (e) => {
|
||||
mat.marketTa1 = e.target.checked;
|
||||
},
|
||||
});
|
||||
useTa1AutoSaleDiv.appendChild(useTa1AutoSaleLabel);
|
||||
useTa1AutoSaleDiv.appendChild(useTa1AutoSaleCheckbox);
|
||||
|
||||
const closeBtn = createPopupCloseButton(popupId, {
|
||||
class: "std-button",
|
||||
display: "block",
|
||||
innerText: "Close",
|
||||
});
|
||||
|
||||
if (industry.hasResearch("Market-TA.II")) {
|
||||
let updateTa2Text;
|
||||
const ta2Text = createElement("p");
|
||||
const ta2Input = createElement("input", {
|
||||
marginTop: "4px",
|
||||
onkeyup: (e) => {
|
||||
e.preventDefault();
|
||||
updateTa2Text();
|
||||
},
|
||||
type: "number",
|
||||
value: mat.bCost,
|
||||
});
|
||||
|
||||
// Function that updates the text in ta2Text element
|
||||
updateTa2Text = function() {
|
||||
const sCost = parseFloat(ta2Input.value);
|
||||
let markup = 1;
|
||||
if (sCost > mat.bCost) {
|
||||
//Penalty if difference between sCost and bCost is greater than markup limit
|
||||
if ((sCost - mat.bCost) > markupLimit) {
|
||||
markup = Math.pow(markupLimit / (sCost - mat.bCost), 2);
|
||||
}
|
||||
} else if (sCost < mat.bCost) {
|
||||
if (sCost <= 0) {
|
||||
markup = 1e12; //Sell everything, essentially discard
|
||||
} else {
|
||||
//Lower prices than market increases sales
|
||||
markup = mat.bCost / sCost;
|
||||
}
|
||||
}
|
||||
ta2Text.innerHTML = `<br><u><strong>Market-TA.II</strong></u><br>` +
|
||||
`If you sell at ${numeralWrapper.formatMoney(sCost)}, ` +
|
||||
`then you will sell ${numeralWrapper.format(markup, "0.00000")}x as much compared ` +
|
||||
`to if you sold at market price.`;
|
||||
}
|
||||
updateTa2Text();
|
||||
|
||||
// Enable using Market-TA2 for automatically setting sale price
|
||||
const useTa2AutoSaleId = "cmpy-mgmt-marketa2-checkbox";
|
||||
const useTa2AutoSaleDiv = createElement("div", { display: "block" });
|
||||
const useTa2AutoSaleLabel = createElement("label", {
|
||||
color: "white",
|
||||
for: useTa2AutoSaleId,
|
||||
innerText: "Use Market-TA.II for Auto-Sale Price",
|
||||
tooltip: "If this is enabled, then this Material will automatically " +
|
||||
"be sold at the optimal price such that the amount sold matches the " +
|
||||
"amount produced. (i.e. the highest possible price, while still ensuring " +
|
||||
" that all produced materials will be sold)",
|
||||
})
|
||||
const useTa2AutoSaleCheckbox = createElement("input", {
|
||||
checked: mat.marketTa2,
|
||||
id: useTa2AutoSaleId,
|
||||
margin: "3px",
|
||||
type: "checkbox",
|
||||
changeListener: (e) => {
|
||||
mat.marketTa2 = e.target.checked;
|
||||
},
|
||||
});
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
|
||||
|
||||
const ta2OverridesTa1 = createElement("p", {
|
||||
innerText: "Note that Market-TA.II overrides Market-TA.I. This means that if " +
|
||||
"both are enabled, then Market-TA.II will take effect, not Market-TA.I",
|
||||
});
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, ta2OverridesTa1, closeBtn]);
|
||||
} else {
|
||||
// Market-TA.I only
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a popup that lets the player create a new industry.
|
||||
// This is created when the player clicks the "Expand into new Industry" header tab
|
||||
createNewIndustryPopup() {
|
||||
const popupId = "cmpy-mgmt-expand-industry-popup";
|
||||
if (document.getElementById(popupId) != null) { return; }
|
||||
|
||||
var txt = createElement("p", {
|
||||
innerHTML: "Create a new division to expand into a new industry:",
|
||||
});
|
||||
var selector = createElement("select", {
|
||||
class:"dropdown",
|
||||
});
|
||||
var industryDescription = createElement("p", {});
|
||||
var yesBtn;
|
||||
var nameInput = createElement("input", {
|
||||
type:"text",
|
||||
id:"cmpy-mgmt-expand-industry-name-input",
|
||||
class: "text-input",
|
||||
display:"block",
|
||||
maxLength: 30,
|
||||
pattern:"[a-zA-Z0-9-_]",
|
||||
onkeyup:(e)=>{
|
||||
e.preventDefault();
|
||||
if (e.keyCode === KEY.ENTER) {yesBtn.click();}
|
||||
},
|
||||
});
|
||||
var nameLabel = createElement("label", {
|
||||
for:"cmpy-mgmt-expand-industry-name-input",
|
||||
innerText:"Division name: ",
|
||||
});
|
||||
yesBtn = createElement("span", {
|
||||
class:"popup-box-button",
|
||||
innerText:"Create Division",
|
||||
clickListener: ()=>{
|
||||
const ind = selector.options[selector.selectedIndex].value;
|
||||
const newDivisionName = nameInput.value;
|
||||
|
||||
for (let i = 0; i < this.corp.divisions.length; ++i) {
|
||||
if (this.corp.divisions[i].name === newDivisionName) {
|
||||
dialogBoxCreate("This name is already in use!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this.corp.funds.lt(IndustryStartingCosts[ind])) {
|
||||
dialogBoxCreate("Not enough money to create a new division in this industry");
|
||||
} else if (newDivisionName === "") {
|
||||
dialogBoxCreate("New division must have a name!");
|
||||
} else {
|
||||
this.corp.funds = this.corp.funds.minus(IndustryStartingCosts[ind]);
|
||||
var newInd = new Industry({
|
||||
corp: this.corp,
|
||||
name: newDivisionName,
|
||||
type: ind,
|
||||
});
|
||||
this.corp.divisions.push(newInd);
|
||||
|
||||
// Set routing to the new division so that the UI automatically switches to it
|
||||
this.routing.routeTo(newDivisionName);
|
||||
|
||||
removeElementById("cmpy-mgmt-expand-industry-popup");
|
||||
this.rerender();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
const noBtn = createPopupCloseButton(popupId, {
|
||||
display: "inline-block",
|
||||
innerText: "Cancel",
|
||||
});
|
||||
|
||||
// Make an object to keep track of what industries you're already in
|
||||
const ownedIndustries = {};
|
||||
for (let i = 0; i < this.corp.divisions.length; ++i) {
|
||||
ownedIndustries[this.corp.divisions[i].type] = true;
|
||||
}
|
||||
|
||||
// Add industry types to selector
|
||||
// Have Agriculture be first as recommended option
|
||||
if (!ownedIndustries["Agriculture"]) {
|
||||
selector.add(createElement("option", {
|
||||
text:Industries["Agriculture"], value:"Agriculture",
|
||||
}));
|
||||
}
|
||||
|
||||
for (var key in Industries) {
|
||||
if (key !== "Agriculture" && Industries.hasOwnProperty(key) && !ownedIndustries[key]) {
|
||||
var ind = Industries[key];
|
||||
selector.add(createElement("option", {
|
||||
text: ind,value:key,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
//Initial Industry Description
|
||||
var ind = selector.options[selector.selectedIndex].value;
|
||||
industryDescription.innerHTML = (IndustryDescriptions[ind] + "<br><br>");
|
||||
|
||||
//Change the industry description text based on selected option
|
||||
selector.addEventListener("change", function() {
|
||||
var ind = selector.options[selector.selectedIndex].value;
|
||||
industryDescription.innerHTML = IndustryDescriptions[ind] + "<br><br>";
|
||||
});
|
||||
|
||||
//Add to DOM
|
||||
const elems = [];
|
||||
elems.push(txt);
|
||||
elems.push(selector);
|
||||
elems.push(industryDescription);
|
||||
elems.push(nameLabel);
|
||||
elems.push(nameInput);
|
||||
elems.push(noBtn);
|
||||
elems.push(yesBtn);
|
||||
|
||||
createPopup(popupId, elems);
|
||||
nameInput.focus();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a popup that lets the player use the Market TA research for Products
|
||||
createProductMarketTaPopup(product, industry) {
|
||||
const popupId = "cmpy-mgmt-marketta-popup";
|
||||
|
120
src/Corporation/ui/ExportPopup.tsx
Normal file
120
src/Corporation/ui/ExportPopup.tsx
Normal file
@ -0,0 +1,120 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Warehouse } from "../Warehouse";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement";
|
||||
import { clearSelector } from "../../../utils/uiHelpers/clearSelector";
|
||||
import { getSelectText,
|
||||
getSelectValue } from "../../../utils/uiHelpers/getSelectData";
|
||||
|
||||
interface IProps {
|
||||
mat: any;
|
||||
corp: any;
|
||||
popupId: string;
|
||||
}
|
||||
|
||||
// Create a popup that lets the player manage exports
|
||||
export function ExportPopup(props: IProps): React.ReactElement {
|
||||
if(props.corp.divisions.length === 0)
|
||||
throw new Error('Export popup created with no divisions.');
|
||||
if(Object.keys(props.corp.divisions[0].warehouses).length === 0)
|
||||
throw new Error('Export popup created in a division with no warehouses.');
|
||||
const [industry, setIndustry] = useState<string>(props.corp.divisions[0].name);
|
||||
const [city, setCity] = useState<string>(Object.keys(props.corp.divisions[0].warehouses)[0]);
|
||||
const [amt, setAmt] = useState('');
|
||||
const setRerender = useState(false)[1];
|
||||
|
||||
function rerender(): void {
|
||||
setRerender(old => !old);
|
||||
}
|
||||
|
||||
function onCityChange(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
setCity(event.target.value);
|
||||
}
|
||||
|
||||
function onIndustryChange(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
setIndustry(event.target.value);
|
||||
}
|
||||
|
||||
function onAmtChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setAmt(event.target.value);
|
||||
}
|
||||
|
||||
function exportMaterial(): void {
|
||||
const industryName = industry;
|
||||
const cityName = city;
|
||||
console.log(`${industryName}, ${cityName}, ${amt}`)
|
||||
|
||||
// Sanitize amt
|
||||
let sanitizedAmt = amt.replace(/\s+/g, '');
|
||||
sanitizedAmt = sanitizedAmt.replace(/[^-()\d/*+.MAX]/g, '');
|
||||
let temp = sanitizedAmt.replace(/MAX/g, '1');
|
||||
try {
|
||||
temp = eval(temp);
|
||||
} catch(e) {
|
||||
dialogBoxCreate("Invalid expression entered for export amount: " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
const n = parseFloat(temp);
|
||||
|
||||
if (n == null || isNaN(n) || n < 0) {
|
||||
dialogBoxCreate("Invalid amount entered for export");
|
||||
return;
|
||||
}
|
||||
const exportObj = {ind:industryName, city:cityName, amt:sanitizedAmt};
|
||||
console.log(exportObj);
|
||||
props.mat.exp.push(exportObj);
|
||||
removePopup(props.popupId);
|
||||
}
|
||||
|
||||
function removeExport(exp: any): void {
|
||||
for (let i = 0; i < props.mat.exp.length; ++i) {
|
||||
if(props.mat.exp[i].ind !== exp.ind ||
|
||||
props.mat.exp[i].city !== exp.city ||
|
||||
props.mat.exp[i].amt !== exp.amt) continue;
|
||||
props.mat.exp.splice(i, 1);
|
||||
break
|
||||
}
|
||||
rerender();
|
||||
}
|
||||
|
||||
const currentDivision = props.corp.divisions.find((division: any) => division.name === industry);
|
||||
|
||||
return (<>
|
||||
<p>
|
||||
Select the industry and city to export this material to, as well as
|
||||
how much of this material to export per second. You can set the export
|
||||
amount to 'MAX' to export all of the materials in this warehouse.
|
||||
</p>
|
||||
<select className="dropdown" onChange={onIndustryChange} defaultValue={industry}>
|
||||
{
|
||||
props.corp.divisions.map((division: any) =>
|
||||
<option key={division.name} value={division.name}>{division.name}</option>)
|
||||
}
|
||||
</select>
|
||||
<select className="dropdown" onChange={onCityChange} defaultValue={city}>
|
||||
{
|
||||
currentDivision && Object.keys(currentDivision.warehouses).map((cityName: any) => {
|
||||
if(currentDivision.warehouses[cityName] === 0) return;
|
||||
return (<option key={cityName} value={cityName}>{cityName}</option>);
|
||||
})
|
||||
}
|
||||
</select>
|
||||
<input className="text-input" placeholder="Export amount / s" onChange={onAmtChange} />
|
||||
<button className="std-button" style={{display:"inline-block"}} onClick={exportMaterial}>Export</button>
|
||||
<p>
|
||||
Below is a list of all current exports of this material from this warehouse.
|
||||
Clicking on one of the exports below will REMOVE that export.
|
||||
</p>
|
||||
{
|
||||
props.mat.exp.map((exp: any, index: number) =>
|
||||
<div key={index} className="cmpy-mgmt-existing-export" onClick={() => removeExport(exp)}>
|
||||
Industry: {exp.ind}<br />
|
||||
City: {exp.city}<br />
|
||||
Amount/s: {exp.amt}
|
||||
</div>)
|
||||
}
|
||||
</>);
|
||||
}
|
@ -4,6 +4,8 @@
|
||||
import React from "react";
|
||||
import { HeaderTab } from "./HeaderTab";
|
||||
import { IDivision } from "../IDivision";
|
||||
import { NewIndustryPopup } from "./NewIndustryPopup";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
|
||||
interface IProps {
|
||||
corp: any;
|
||||
@ -17,6 +19,15 @@ export function HeaderTabs(props: IProps): React.ReactElement {
|
||||
props.corp.rerender();
|
||||
}
|
||||
|
||||
function openNewIndustryPopup(): void {
|
||||
const popupId = "cmpy-mgmt-expand-industry-popup";
|
||||
createPopup(popupId, NewIndustryPopup, {
|
||||
corp: props.corp,
|
||||
routing: props.routing,
|
||||
popupId: popupId,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<HeaderTab
|
||||
@ -38,7 +49,7 @@ export function HeaderTabs(props: IProps): React.ReactElement {
|
||||
}
|
||||
<HeaderTab
|
||||
current={false}
|
||||
onClick={() => props.eventHandler.createNewIndustryPopup()}
|
||||
onClick={openNewIndustryPopup}
|
||||
text={"Expand into new Industry"}
|
||||
/>
|
||||
</div>
|
||||
|
@ -8,6 +8,8 @@ import { IndustryUpgrades } from "../IndustryUpgrades";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||
import { MakeProductPopup } from "./MakeProductPopup";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
|
||||
interface IProps {
|
||||
routing: any;
|
||||
@ -77,8 +79,18 @@ export function IndustryOverview(props: IProps): React.ReactElement {
|
||||
display: "inline-block",
|
||||
}
|
||||
|
||||
function openMakeProductPopup() {
|
||||
const popupId = "cmpy-mgmt-create-product-popup";
|
||||
createPopup(popupId, MakeProductPopup, {
|
||||
popupText: createProductPopupText,
|
||||
division: division,
|
||||
corp: props.corp,
|
||||
popupId: popupId,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={className} onClick={() => props.eventHandler.createMakeProductPopup(createProductPopupText, division)} style={buttonStyle}>
|
||||
<button className={className} onClick={openMakeProductPopup} style={buttonStyle}>
|
||||
{createProductButtonText}
|
||||
{
|
||||
hasMaxProducts &&
|
||||
|
@ -8,6 +8,9 @@ import { Material } from "../Material";
|
||||
import { Product } from "../Product";
|
||||
import { Warehouse } from "../Warehouse";
|
||||
import { DiscontinueProductPopup } from "./DiscontinueProductPopup";
|
||||
import { ExportPopup } from "./ExportPopup";
|
||||
import { LimitProductProductionPopup } from "./LimitProductProductionPopup";
|
||||
import { MaterialMarketTaPopup } from "./MaterialMarketTaPopup";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
@ -71,7 +74,15 @@ function ProductComponent(props: IProductProps): React.ReactElement {
|
||||
if (product.prdman[city][0]) {
|
||||
limitProductionButtonText += " (" + numeralWrapper.format(product.prdman[city][1], nf) + ")";
|
||||
}
|
||||
const limitProductionButtonOnClick = eventHandler.createLimitProductProdutionPopup.bind(eventHandler, product, city);
|
||||
|
||||
function openLimitProductProdutionPopup(): void {
|
||||
const popupId = "cmpy-mgmt-limit-product-production-popup";
|
||||
createPopup(popupId, LimitProductProductionPopup, {
|
||||
product: product,
|
||||
city: city,
|
||||
popupId: popupId,
|
||||
});
|
||||
}
|
||||
|
||||
function openDiscontinueProductPopup(): void {
|
||||
const popupId = "cmpy-mgmt-discontinue-product-popup";
|
||||
@ -83,8 +94,15 @@ function ProductComponent(props: IProductProps): React.ReactElement {
|
||||
});
|
||||
}
|
||||
|
||||
// Market TA button
|
||||
const marketTaButtonOnClick = eventHandler.createProductMarketTaPopup.bind(eventHandler, product, division);
|
||||
function openMaterialMarketTaPopup(): void {
|
||||
const popupId = "cmpy-mgmt-export-popup";
|
||||
createPopup(popupId, MaterialMarketTaPopup, {
|
||||
mat: product,
|
||||
industry: division,
|
||||
corp: props.corp,
|
||||
popupId: popupId,
|
||||
});
|
||||
}
|
||||
|
||||
// Unfinished Product
|
||||
if (!product.fin) {
|
||||
@ -99,7 +117,7 @@ function ProductComponent(props: IProductProps): React.ReactElement {
|
||||
<button className={"std-button"} onClick={sellButtonOnClick}>
|
||||
{sellButtonText}
|
||||
</button><br />
|
||||
<button className={"std-button"} onClick={limitProductionButtonOnClick}>
|
||||
<button className={"std-button"} onClick={openLimitProductProdutionPopup}>
|
||||
{limitProductionButtonText}
|
||||
</button>
|
||||
<button className={"std-button"} onClick={openDiscontinueProductPopup}>
|
||||
@ -107,7 +125,7 @@ function ProductComponent(props: IProductProps): React.ReactElement {
|
||||
</button>
|
||||
{
|
||||
division.hasResearch("Market-TA.I") &&
|
||||
<button className={"std-button"} onClick={marketTaButtonOnClick}>
|
||||
<button className={"std-button"} onClick={openMaterialMarketTaPopup}>
|
||||
Market-TA
|
||||
</button>
|
||||
}
|
||||
@ -179,7 +197,7 @@ function ProductComponent(props: IProductProps): React.ReactElement {
|
||||
<button className={"std-button"} onClick={sellButtonOnClick}>
|
||||
{sellButtonText}
|
||||
</button><br />
|
||||
<button className={"std-button"} onClick={limitProductionButtonOnClick}>
|
||||
<button className={"std-button"} onClick={openLimitProductProdutionPopup}>
|
||||
{limitProductionButtonText}
|
||||
</button>
|
||||
<button className={"std-button"} onClick={openDiscontinueProductPopup}>
|
||||
@ -187,7 +205,7 @@ function ProductComponent(props: IProductProps): React.ReactElement {
|
||||
</button>
|
||||
{
|
||||
division.hasResearch("Market-TA.I") &&
|
||||
<button className={"std-button"} onClick={marketTaButtonOnClick}>
|
||||
<button className={"std-button"} onClick={openMaterialMarketTaPopup}>
|
||||
Market-TA
|
||||
</button>
|
||||
}
|
||||
@ -236,8 +254,14 @@ function MaterialComponent(props: IMaterialProps): React.ReactElement {
|
||||
const purchaseButtonClass = tutorial ? "std-button flashing-button tooltip" : "std-button";
|
||||
const purchaseButtonOnClick = eventHandler.createPurchaseMaterialPopup.bind(eventHandler, mat, division, warehouse);
|
||||
|
||||
// Export material button
|
||||
const exportButtonOnClick = eventHandler.createExportMaterialPopup.bind(eventHandler, mat);
|
||||
function openExportPopup() {
|
||||
const popupId = "cmpy-mgmt-export-popup";
|
||||
createPopup(popupId, ExportPopup, {
|
||||
mat: mat,
|
||||
corp: props.corp,
|
||||
popupId: popupId,
|
||||
});
|
||||
}
|
||||
|
||||
// Sell material button
|
||||
let sellButtonText;
|
||||
@ -265,8 +289,15 @@ function MaterialComponent(props: IMaterialProps): React.ReactElement {
|
||||
}
|
||||
const sellButtonOnClick = eventHandler.createSellMaterialPopup.bind(eventHandler, mat);
|
||||
|
||||
// Market TA button
|
||||
const marketTaButtonOnClick = eventHandler.createMaterialMarketTaPopup.bind(eventHandler, mat, division);
|
||||
function openMaterialMarketTaPopup(): void {
|
||||
const popupId = "cmpy-mgmt-export-popup";
|
||||
createPopup(popupId, MaterialMarketTaPopup, {
|
||||
mat: mat,
|
||||
industry: division,
|
||||
corp: props.corp,
|
||||
popupId: popupId,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={"cmpy-mgmt-warehouse-material-div"}>
|
||||
@ -322,7 +353,7 @@ function MaterialComponent(props: IMaterialProps): React.ReactElement {
|
||||
|
||||
{
|
||||
corp.unlockUpgrades[0] === 1 &&
|
||||
<button className={"std-button"} onClick={exportButtonOnClick}>
|
||||
<button className={"std-button"} onClick={openExportPopup}>
|
||||
Export
|
||||
</button>
|
||||
}
|
||||
@ -334,7 +365,7 @@ function MaterialComponent(props: IMaterialProps): React.ReactElement {
|
||||
|
||||
{
|
||||
division.hasResearch("Market-TA.I") &&
|
||||
<button className={"std-button"} onClick={marketTaButtonOnClick}>
|
||||
<button className={"std-button"} onClick={openMaterialMarketTaPopup}>
|
||||
Market-TA
|
||||
</button>
|
||||
}
|
||||
|
57
src/Corporation/ui/IssueDividendsPopup.tsx
Normal file
57
src/Corporation/ui/IssueDividendsPopup.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import React, { useState } from 'react';
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { CorporationConstants } from "../data/Constants";
|
||||
|
||||
interface IProps {
|
||||
popupId: string;
|
||||
corp: any;
|
||||
}
|
||||
|
||||
// Create a popup that lets the player issue & manage dividends
|
||||
// This is created when the player clicks the "Issue Dividends" button in the overview panel
|
||||
export function IssueDividendsPopup(props: IProps): React.ReactElement {
|
||||
const [percent, setPercent] = useState<number | null>(null);
|
||||
|
||||
function issueDividends(): void {
|
||||
if(percent === null) return;
|
||||
if (isNaN(percent) || percent < 0 || percent > CorporationConstants.DividendMaxPercentage) {
|
||||
dialogBoxCreate(`Invalid value. Must be an integer between 0 and ${CorporationConstants.DividendMaxPercentage}`);
|
||||
return;
|
||||
}
|
||||
|
||||
props.corp.dividendPercentage = percent;
|
||||
|
||||
removePopup(props.popupId);
|
||||
}
|
||||
|
||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
if (event.keyCode === 13) issueDividends();
|
||||
}
|
||||
|
||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
if(event.target.value === "") setPercent(null);
|
||||
else setPercent(parseFloat(event.target.value));
|
||||
}
|
||||
|
||||
return (<>
|
||||
<p>
|
||||
"Dividends are a distribution of a portion of the corporation's
|
||||
profits to the shareholders. This includes yourself, as well.<br /><br />
|
||||
In order to issue dividends, simply allocate some percentage
|
||||
of your corporation's profits to dividends. This percentage must be an
|
||||
integer between 0 and {CorporationConstants.DividendMaxPercentage}. (A percentage of 0 means no dividends will be
|
||||
issued<br /><br />
|
||||
Two important things to note:<br />
|
||||
* Issuing dividends will negatively affect your corporation's stock price<br />
|
||||
* Dividends are taxed. Taxes start at 50%, but can be decreased<br /><br />
|
||||
Example: Assume your corporation makes $100m / sec in profit and you allocate
|
||||
40% of that towards dividends. That means your corporation will gain $60m / sec
|
||||
in funds and the remaining $40m / sec will be paid as dividends. Since your
|
||||
corporation starts with 1 billion shares, every shareholder will be paid $0.04 per share
|
||||
per second before taxes.
|
||||
</p>
|
||||
<input autoFocus={true} onChange={onChange} onKeyDown={onKeyDown} className="text-input" placeholder="Dividend %" type="number" style={{margin: "5px"}} />
|
||||
<button onClick={issueDividends} className="std-button" style={{display: "inline-block"}}>Allocate Dividend Percentage</button>
|
||||
</>);
|
||||
}
|
134
src/Corporation/ui/IssueNewSharesPopup.tsx
Normal file
134
src/Corporation/ui/IssueNewSharesPopup.tsx
Normal file
@ -0,0 +1,134 @@
|
||||
import React, { useState } from 'react';
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { getRandomInt } from "../../../utils/helpers/getRandomInt";
|
||||
import { CorporationConstants } from "../data/Constants";
|
||||
|
||||
interface IEffectTextProps {
|
||||
corp: any;
|
||||
shares: number | null;
|
||||
}
|
||||
|
||||
function EffectText(props: IEffectTextProps): React.ReactElement {
|
||||
if(props.shares === null) return (<></>);
|
||||
const newSharePrice = Math.round(props.corp.sharePrice * 0.9);
|
||||
const maxNewSharesUnrounded = Math.round(props.corp.totalShares * 0.2);
|
||||
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
|
||||
let newShares = props.shares;
|
||||
if (isNaN(newShares)) {
|
||||
return (<p>Invalid input</p>);
|
||||
}
|
||||
|
||||
// Round to nearest ten-millionth
|
||||
newShares /= 10e6;
|
||||
newShares = Math.round(newShares) * 10e6;
|
||||
|
||||
if (newShares < 10e6) {
|
||||
return (<p>Must issue at least 10 million new shares</p>);
|
||||
}
|
||||
|
||||
if (newShares > maxNewShares) {
|
||||
return (<p>You cannot issue that many shares</p>);
|
||||
}
|
||||
|
||||
return (<p>
|
||||
Issue ${numeralWrapper.format(newShares, "0.000a")} new
|
||||
shares for {numeralWrapper.formatMoney(newShares * newSharePrice)}?
|
||||
</p>);
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
corp: any;
|
||||
popupId: string;
|
||||
}
|
||||
|
||||
// Create a popup that lets the player issue new shares
|
||||
// This is created when the player clicks the "Issue New Shares" buttons in the overview panel
|
||||
export function IssueNewSharesPopup(props: IProps): React.ReactElement {
|
||||
const [shares, setShares] = useState<number | null>(null);
|
||||
const maxNewSharesUnrounded = Math.round(props.corp.totalShares * 0.2);
|
||||
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
|
||||
|
||||
function issueNewShares(): void {
|
||||
if(shares === null) return;
|
||||
const newSharePrice = Math.round(props.corp.sharePrice * 0.9);
|
||||
let newShares = shares;
|
||||
if (isNaN(newShares)) {
|
||||
dialogBoxCreate("Invalid input for number of new shares");
|
||||
return;
|
||||
}
|
||||
|
||||
// Round to nearest ten-millionth
|
||||
newShares = Math.round(newShares / 10e6) * 10e6;
|
||||
|
||||
if (newShares < 10e6 || newShares > maxNewShares) {
|
||||
dialogBoxCreate("Invalid input for number of new shares");
|
||||
return;
|
||||
}
|
||||
|
||||
const profit = newShares * newSharePrice;
|
||||
props.corp.issueNewSharesCooldown = CorporationConstants.IssueNewSharesCooldown;
|
||||
props.corp.totalShares += newShares;
|
||||
|
||||
// Determine how many are bought by private investors
|
||||
// Private investors get up to 50% at most
|
||||
// Round # of private shares to the nearest millionth
|
||||
let privateShares = getRandomInt(0, Math.round(newShares / 2));
|
||||
privateShares = Math.round(privateShares / 1e6) * 1e6;
|
||||
|
||||
props.corp.issuedShares += (newShares - privateShares);
|
||||
props.corp.funds = props.corp.funds.plus(profit);
|
||||
props.corp.immediatelyUpdateSharePrice();
|
||||
|
||||
removePopup(props.popupId);
|
||||
dialogBoxCreate(`Issued ${numeralWrapper.format(newShares, "0.000a")} and raised ` +
|
||||
`${numeralWrapper.formatMoney(profit)}. ${numeralWrapper.format(privateShares, "0.000a")} ` +
|
||||
`of these shares were bought by private investors.<br><br>` +
|
||||
`Stock price decreased to ${numeralWrapper.formatMoney(props.corp.sharePrice)}`);
|
||||
}
|
||||
|
||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
if (event.keyCode === 13) issueNewShares();
|
||||
}
|
||||
|
||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
if(event.target.value === "") setShares(null);
|
||||
else setShares(parseFloat(event.target.value));
|
||||
}
|
||||
|
||||
return (<>
|
||||
<p>
|
||||
You can issue new equity shares (i.e. stocks) in order to raise
|
||||
capital for your corporation.<br /><br />
|
||||
* You can issue at most {numeralWrapper.formatMoney(maxNewShares)} new shares<br />
|
||||
* New shares are sold at a 10% discount<br />
|
||||
* You can only issue new shares once every 12 hours<br />
|
||||
* Issuing new shares causes dilution, resulting in a decrease in stock price and lower dividends per share<br />
|
||||
* Number of new shares issued must be a multiple of 10 million<br /><br />
|
||||
When you choose to issue new equity, private shareholders have first priority for up to 50% of the new shares.
|
||||
If they choose to exercise this option, these newly issued shares become private, restricted shares, which means
|
||||
you cannot buy them back.
|
||||
</p>
|
||||
<EffectText corp={props.corp} shares={shares} />
|
||||
<input className="text-input" autoFocus={true} placeholder="# New Shares" style={{margin: "5px"}} onChange={onChange} onKeyDown={onKeyDown} />
|
||||
<button onClick={issueNewShares} className="std-button" style={{display: "inline-block"}}>Issue New Shares</button>
|
||||
</>);
|
||||
|
||||
|
||||
// let issueBtn, newSharesInput;
|
||||
// const dynamicText = createElement("p", {
|
||||
// display: "block",
|
||||
// });
|
||||
|
||||
// function updateDynamicText(corp) {
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
// createPopup(popupId, [descText, dynamicText, newSharesInput, issueBtn, cancelBtn]);
|
||||
// newSharesInput.focus();
|
||||
}
|
59
src/Corporation/ui/LimitProductProductionPopup.tsx
Normal file
59
src/Corporation/ui/LimitProductProductionPopup.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Warehouse } from "../Warehouse";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement";
|
||||
import { clearSelector } from "../../../utils/uiHelpers/clearSelector";
|
||||
import { getSelectText,
|
||||
getSelectValue } from "../../../utils/uiHelpers/getSelectData";
|
||||
|
||||
|
||||
interface IProps {
|
||||
product: any;
|
||||
city: any;
|
||||
popupId: string;
|
||||
}
|
||||
|
||||
// Create a popup that lets the player limit the production of a product
|
||||
export function LimitProductProductionPopup(props: IProps): React.ReactElement {
|
||||
const [limit, setLimit] = useState<number | null>(null);
|
||||
|
||||
function limitProductProduction(): void {
|
||||
if (limit === null) {
|
||||
props.product.prdman[props.city][0] = false;
|
||||
removePopup(props.popupId);
|
||||
return;
|
||||
}
|
||||
var qty = limit;
|
||||
if (isNaN(qty)) {
|
||||
dialogBoxCreate("Invalid value entered");
|
||||
return;
|
||||
}
|
||||
if (qty < 0) {
|
||||
props.product.prdman[props.city][0] = false;
|
||||
} else {
|
||||
props.product.prdman[props.city][0] = true;
|
||||
props.product.prdman[props.city][1] = qty;
|
||||
}
|
||||
removePopup(props.popupId);
|
||||
}
|
||||
|
||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
if (event.keyCode === 13) limitProductProduction();
|
||||
}
|
||||
|
||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
if(event.target.value === "") setLimit(null);
|
||||
else setLimit(parseFloat(event.target.value));
|
||||
}
|
||||
|
||||
return (<>
|
||||
<p>
|
||||
Enter a limit to the amount of this product you would
|
||||
like to product per second. Leave the box empty to set no limit.
|
||||
</p>
|
||||
<input autoFocus={true} className="text-input" style={{margin: "5px"}} placeholder="Limit" type="number" onChange={onChange} onKeyDown={onKeyDown} />
|
||||
<button className="std-button" style={{margin:"5px", display:"inline-block"}} onClick={limitProductProduction}>Limit production</button>
|
||||
</>);
|
||||
}
|
107
src/Corporation/ui/MakeProductPopup.tsx
Normal file
107
src/Corporation/ui/MakeProductPopup.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Warehouse } from "../Warehouse";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement";
|
||||
import { clearSelector } from "../../../utils/uiHelpers/clearSelector";
|
||||
import { getSelectText,
|
||||
getSelectValue } from "../../../utils/uiHelpers/getSelectData";
|
||||
import { Industries } from "../IndustryData";
|
||||
import { Product } from "../Product";
|
||||
|
||||
interface IProps {
|
||||
popupText: string;
|
||||
division: any;
|
||||
corp: any;
|
||||
popupId: string;
|
||||
}
|
||||
|
||||
function productPlaceholder(tpe: string): string {
|
||||
if (tpe === Industries.Food) {
|
||||
return "Restaurant Name";
|
||||
} else if (tpe === Industries.Healthcare) {
|
||||
return "Hospital Name";
|
||||
} else if (tpe === Industries.RealEstate) {
|
||||
return "Property Name";
|
||||
}
|
||||
return "Product Name";
|
||||
}
|
||||
|
||||
// Create a popup that lets the player create a product for their current industry
|
||||
export function MakeProductPopup(props: IProps) {
|
||||
const allCities = Object.keys(props.division.offices).
|
||||
filter((cityName: string) => props.division.offices[cityName] !== 0);
|
||||
const [city, setCity] = useState(allCities.length > 0 ? allCities[0] : '');
|
||||
const [name, setName] = useState('');
|
||||
const [design, setDesign] = useState<number | null>(null);
|
||||
const [marketing, setMarketing] = useState<number | null>(null);
|
||||
if (props.division.hasMaximumNumberProducts()) return (<></>);
|
||||
|
||||
function makeProduct(): void {
|
||||
let designInvest = design;
|
||||
let marketingInvest = marketing;
|
||||
if (designInvest == null || designInvest < 0) { designInvest = 0; }
|
||||
if (marketingInvest == null || marketingInvest < 0) { marketingInvest = 0; }
|
||||
if (name == null || name === "") {
|
||||
dialogBoxCreate("You must specify a name for your product!");
|
||||
} else if (isNaN(designInvest)) {
|
||||
dialogBoxCreate("Invalid value for design investment");
|
||||
} else if (isNaN(marketingInvest)) {
|
||||
dialogBoxCreate("Invalid value for marketing investment");
|
||||
} else if (props.corp.funds.lt(designInvest + marketingInvest)) {
|
||||
dialogBoxCreate("You don't have enough company funds to make this large of an investment");
|
||||
} else {
|
||||
const product = new Product({
|
||||
name: name.replace(/[<>]/g, ''), //Sanitize for HTMl elements
|
||||
createCity: city,
|
||||
designCost: designInvest,
|
||||
advCost: marketingInvest,
|
||||
});
|
||||
if (props.division.products[product.name] instanceof Product) {
|
||||
dialogBoxCreate(`You already have a product with this name!`);
|
||||
return;
|
||||
}
|
||||
props.corp.funds = props.corp.funds.minus(designInvest + marketingInvest);
|
||||
props.division.products[product.name] = product;
|
||||
removePopup(props.popupId);
|
||||
}
|
||||
}
|
||||
|
||||
function onCityChange(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
setCity(event.target.value);
|
||||
}
|
||||
|
||||
function onProductNameChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setName(event.target.value);
|
||||
}
|
||||
|
||||
function onDesignChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
if(event.target.value === "") setDesign(null);
|
||||
else setDesign(parseFloat(event.target.value));
|
||||
}
|
||||
|
||||
function onMarketingChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
if(event.target.value === "") setMarketing(null);
|
||||
else setMarketing(parseFloat(event.target.value));
|
||||
}
|
||||
|
||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
if (event.keyCode === 13) makeProduct();
|
||||
}
|
||||
|
||||
return (<>
|
||||
<p dangerouslySetInnerHTML={{__html: props.popupText}} />
|
||||
<select className="dropdown" style={{margin: "5px"}} onChange={onCityChange} defaultValue={city}>
|
||||
{
|
||||
allCities.map((cityName: string) =>
|
||||
<option key={cityName} value={cityName}>{cityName}</option>)
|
||||
}
|
||||
</select>
|
||||
<input onChange={onProductNameChange} className="text-input" style={{margin:"5px"}} placeholder={productPlaceholder(props.division.type)} />
|
||||
<br />
|
||||
<input onChange={onDesignChange} autoFocus={true} type="number" className="text-input" style={{margin:"5px"}} placeholder={"Design investment"}/>
|
||||
<input onChange={onMarketingChange} onKeyDown={onKeyDown} type="number" className="text-input" style={{margin:"5px"}} placeholder={"Marketing investment"}/>
|
||||
<button className="std-button" onClick={makeProduct}>Develop Product</button>
|
||||
</>);
|
||||
}
|
121
src/Corporation/ui/MaterialMarketTaPopup.tsx
Normal file
121
src/Corporation/ui/MaterialMarketTaPopup.tsx
Normal file
@ -0,0 +1,121 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Warehouse } from "../Warehouse";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement";
|
||||
import { clearSelector } from "../../../utils/uiHelpers/clearSelector";
|
||||
import { getSelectText,
|
||||
getSelectValue } from "../../../utils/uiHelpers/getSelectData";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
interface IMarketTA2Props {
|
||||
industry: any;
|
||||
mat: any;
|
||||
}
|
||||
|
||||
function MarketTA2(props: IMarketTA2Props): React.ReactElement {
|
||||
if(!props.industry.hasResearch("Market-TA.II")) return (<></>);
|
||||
|
||||
const [newCost, setNewCost] = useState<number>(props.mat.bCost);
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender(old => !old);
|
||||
}
|
||||
const markupLimit = props.mat.getMarkupLimit();
|
||||
|
||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
if(event.target.value === "") setNewCost(0);
|
||||
else setNewCost(parseFloat(event.target.value));
|
||||
}
|
||||
|
||||
const sCost = newCost;
|
||||
let markup = 1;
|
||||
if (sCost > props.mat.bCost) {
|
||||
//Penalty if difference between sCost and bCost is greater than markup limit
|
||||
if ((sCost - props.mat.bCost) > markupLimit) {
|
||||
markup = Math.pow(markupLimit / (sCost - props.mat.bCost), 2);
|
||||
}
|
||||
} else if (sCost < props.mat.bCost) {
|
||||
if (sCost <= 0) {
|
||||
markup = 1e12; //Sell everything, essentially discard
|
||||
} else {
|
||||
//Lower prices than market increases sales
|
||||
markup = props.mat.bCost / sCost;
|
||||
}
|
||||
}
|
||||
|
||||
function onMarketTA2(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
props.mat.marketTa2 = event.target.checked;
|
||||
rerender();
|
||||
}
|
||||
|
||||
return (<>
|
||||
<p>
|
||||
<br /><u><strong>Market-TA.II</strong></u><br />
|
||||
If you sell at {numeralWrapper.formatMoney(sCost)},
|
||||
then you will sell {numeralWrapper.format(markup, "0.00000")}x as much compared
|
||||
to if you sold at market price.
|
||||
</p>
|
||||
<input className="text-input" type="number" style={{marginTop: "4px"}} onChange={onChange} value={newCost} />
|
||||
<div style={{display: "block"}}>
|
||||
<label className="tooltip" htmlFor="cmpy-mgmt-marketa2-checkbox" style={{color: "white"}}>
|
||||
Use Market-TA.II for Auto-Sale Price
|
||||
<span className="tooltiptext">
|
||||
If this is enabled, then this Material will automatically
|
||||
be sold at the optimal price such that the amount sold matches the
|
||||
amount produced. (i.e. the highest possible price, while still ensuring
|
||||
that all produced materials will be sold)
|
||||
</span>
|
||||
</label>
|
||||
<input id="cmpy-mgmt-marketa2-checkbox" type="checkbox" onChange={onMarketTA2} checked={props.mat.marketTa2} style={{margin: "3px"}} />
|
||||
</div>
|
||||
<p>
|
||||
Note that Market-TA.II overrides Market-TA.I. This means that if
|
||||
both are enabled, then Market-TA.II will take effect, not Market-TA.I
|
||||
</p>
|
||||
</>);
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
mat: any;
|
||||
industry: any;
|
||||
corp: any;
|
||||
popupId: string;
|
||||
}
|
||||
|
||||
// Create a popup that lets the player use the Market TA research for Materials
|
||||
export function MaterialMarketTaPopup(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender(old => !old);
|
||||
}
|
||||
const markupLimit = props.mat.getMarkupLimit();
|
||||
|
||||
function onMarketTA1(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
props.mat.marketTa1 = event.target.checked;
|
||||
rerender();
|
||||
}
|
||||
|
||||
return (<>
|
||||
<p>
|
||||
<u><strong>Market-TA.I</strong></u><br />
|
||||
The maximum sale price you can mark this up to
|
||||
is {numeralWrapper.formatMoney(props.mat.bCost + markupLimit)}.
|
||||
This means that if you set the sale price higher than this,
|
||||
you will begin to experience a loss in number of sales
|
||||
</p>
|
||||
<div style={{display: 'block'}}>
|
||||
<label className="tooltip" htmlFor="cmpy-mgmt-marketa1-checkbox" style={{color: "white"}}>
|
||||
Use Market-TA.I for Auto-Sale Price
|
||||
<span className="tooltiptext">
|
||||
If this is enabled, then this Material will automatically
|
||||
be sold at the price identified by Market-TA.I (i.e. the price shown above)
|
||||
</span>
|
||||
</label>
|
||||
<input id="cmpy-mgmt-marketa1-checkbox" type="checkbox" onChange={onMarketTA1} checked={props.mat.marketTa1} style={{margin: "3px"}} />
|
||||
</div>
|
||||
<MarketTA2 mat={props.mat} industry={props.industry} />
|
||||
</>);
|
||||
|
||||
}
|
89
src/Corporation/ui/NewIndustryPopup.tsx
Normal file
89
src/Corporation/ui/NewIndustryPopup.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Warehouse } from "../Warehouse";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement";
|
||||
import { clearSelector } from "../../../utils/uiHelpers/clearSelector";
|
||||
import { getSelectText,
|
||||
getSelectValue } from "../../../utils/uiHelpers/getSelectData";
|
||||
import {
|
||||
Industries,
|
||||
IndustryStartingCosts,
|
||||
IndustryDescriptions } from "../IndustryData";
|
||||
import { Industry } from "../Corporation";
|
||||
|
||||
interface IProps {
|
||||
corp: any;
|
||||
popupId: string;
|
||||
routing: any;
|
||||
}
|
||||
// Create a popup that lets the player create a new industry.
|
||||
// This is created when the player clicks the "Expand into new Industry" header tab
|
||||
export function NewIndustryPopup(props: IProps): React.ReactElement {
|
||||
const allIndustries = Object.keys(Industries).sort();
|
||||
const possibleIndustries = allIndustries.filter((industryType: string) =>
|
||||
props.corp.divisions.find((division: any) =>
|
||||
division.type === industryType) === undefined).sort();
|
||||
const [industry, setIndustry] = useState(possibleIndustries.length > 0 ? possibleIndustries[0] : '');
|
||||
const [name, setName] = useState('');
|
||||
|
||||
function newIndustry(): void {
|
||||
const ind = industry;
|
||||
const newDivisionName = name;
|
||||
|
||||
for (let i = 0; i < props.corp.divisions.length; ++i) {
|
||||
if (props.corp.divisions[i].name === newDivisionName) {
|
||||
dialogBoxCreate("This name is already in use!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (props.corp.funds.lt(IndustryStartingCosts[ind])) {
|
||||
dialogBoxCreate("Not enough money to create a new division in this industry");
|
||||
} else if (newDivisionName === "") {
|
||||
dialogBoxCreate("New division must have a name!");
|
||||
} else {
|
||||
props.corp.funds = props.corp.funds.minus(IndustryStartingCosts[ind]);
|
||||
const newInd = new Industry({
|
||||
corp: props.corp,
|
||||
name: newDivisionName,
|
||||
type: ind,
|
||||
});
|
||||
props.corp.divisions.push(newInd);
|
||||
|
||||
// Set routing to the new division so that the UI automatically switches to it
|
||||
props.routing.routeTo(newDivisionName);
|
||||
|
||||
removePopup(props.popupId);
|
||||
}
|
||||
}
|
||||
|
||||
function onNameChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setName(event.target.value);
|
||||
}
|
||||
|
||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
if (event.keyCode === 13) newIndustry();
|
||||
}
|
||||
|
||||
function onIndustryChange(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
setIndustry(event.target.value);
|
||||
}
|
||||
|
||||
return (<>
|
||||
<p>Create a new division to expand into a new industry:</p>
|
||||
<select className="dropdown" defaultValue={industry} onChange={onIndustryChange}>
|
||||
{
|
||||
possibleIndustries.map((industry: string) =>
|
||||
<option key={industry} value={industry}>{industry}</option>)
|
||||
}
|
||||
</select>
|
||||
<p>{IndustryDescriptions[industry]}</p>
|
||||
<br /><br />
|
||||
|
||||
<p>Division name:</p>
|
||||
<input autoFocus={true} value={name} onChange={onNameChange} onKeyDown={onKeyDown} type="text" className="text-input" style={{display:"block"}} maxLength={30} pattern="[a-zA-Z0-9-_]" />
|
||||
<span onClick={newIndustry} className="popup-box-button">Create Division</span>
|
||||
</>);
|
||||
|
||||
}
|
@ -5,6 +5,8 @@ import { UnlockUpgrade } from "./UnlockUpgrade";
|
||||
import { BribeFactionPopup } from "./BribeFactionPopup";
|
||||
import { SellSharesPopup } from "./SellSharesPopup";
|
||||
import { BuybackSharesPopup } from "./BuybackSharesPopup";
|
||||
import { IssueDividendsPopup } from "./IssueDividendsPopup";
|
||||
import { IssueNewSharesPopup } from "./IssueNewSharesPopup";
|
||||
|
||||
import { CorporationConstants } from "../data/Constants";
|
||||
import { CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades";
|
||||
@ -245,6 +247,14 @@ export function Overview(props: IProps): React.ReactElement {
|
||||
tooltip: "Buy back shares you that previously issued or sold at market price.",
|
||||
});
|
||||
|
||||
function openIssueNewSharesPopup(): void {
|
||||
const popupId = "cmpy-mgmt-issue-new-shares-popup";
|
||||
createPopup(popupId, IssueNewSharesPopup, {
|
||||
popupId: popupId,
|
||||
corp: props.corp,
|
||||
});
|
||||
}
|
||||
|
||||
const issueNewSharesOnCd = (corp.issueNewSharesCooldown > 0);
|
||||
const issueNewSharesClass = issueNewSharesOnCd ? "a-link-button-inactive" : "std-button";
|
||||
const issueNewSharesTooltip = issueNewSharesOnCd
|
||||
@ -253,15 +263,23 @@ export function Overview(props: IProps): React.ReactElement {
|
||||
const issueNewSharesBtn = createButton({
|
||||
class: issueNewSharesClass,
|
||||
display: "inline-block",
|
||||
onClick: props.eventHandler.createIssueNewSharesPopup,
|
||||
onClick: openIssueNewSharesPopup,
|
||||
text: "Issue New Shares",
|
||||
tooltip: issueNewSharesTooltip,
|
||||
});
|
||||
|
||||
function openIssueDividendsPopup(): void {
|
||||
const popupId = "cmpy-mgmt-issue-dividends-popup";
|
||||
createPopup(popupId, IssueDividendsPopup, {
|
||||
popupId: popupId,
|
||||
corp: props.corp,
|
||||
});
|
||||
}
|
||||
|
||||
const issueDividendsBtn = createButton({
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
onClick: props.eventHandler.createIssueDividendsPopup,
|
||||
onClick: openIssueDividendsPopup,
|
||||
text: "Issue Dividends",
|
||||
tooltip: "Manage the dividends that are paid out to shareholders (including yourself)",
|
||||
});
|
||||
|
@ -591,6 +591,13 @@ class DevMenuComponent extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
addCorporationResearch() {
|
||||
if(!Player.corporation) return;
|
||||
Player.corporation.divisions.forEach(div => {
|
||||
div.sciResearch.qty += 1e10;
|
||||
});
|
||||
}
|
||||
|
||||
specificContract() {
|
||||
generateContract({
|
||||
problemType: this.state.codingcontract,
|
||||
@ -1181,6 +1188,11 @@ class DevMenuComponent extends Component {
|
||||
<button className="std-button" onClick={this.finishCorporationProducts}>Finish products</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button className="std-button" onClick={this.addCorporationResearch}>Tons of research</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user