Fixed more bugs with new Corporation UI. Minor rebalancing on Corp UI. Changed the Market TA researches to allow you to automatically set price

This commit is contained in:
danielyxie 2019-03-17 17:58:06 -07:00
parent a28fe7ab9f
commit e6c5ff7ab7
14 changed files with 460 additions and 146 deletions

@ -10,7 +10,8 @@
#cmpy-mgmt-container p, #cmpy-mgmt-container p,
#cmpy-mgmt-container a, #cmpy-mgmt-container a,
#cmpy-mgmt-container div { #cmpy-mgmt-container div,
#cmpy-mgmt-container br {
font-size: $defaultFontSize * 0.8125; font-size: $defaultFontSize * 0.8125;
} }

@ -297,9 +297,11 @@ export let CONSTANTS: IMap<any> = {
** Significantly changed the effects of the different employee positions. See updated descriptions ** Significantly changed the effects of the different employee positions. See updated descriptions
** Reduced the amount of money you gain from private investors ** Reduced the amount of money you gain from private investors
** Training employees is now 3x more effective ** Training employees is now 3x more effective
** Bug Fix: An industry's products are now properly separated between different cities
* Rebalanced BitNode-3 to make it slightly harder * Rebalanced BitNode-3 to make it slightly harder
* Bug Fix: Bladeburner's Hyperbolic Regeneration Chamber should no longer instantly refill all stamina * Bug Fix: Bladeburner's Hyperbolic Regeneration Chamber should no longer instantly refill all stamina
* Bug Fix: The cost of purchasing Augmentations for Duplicate Sleeves no longer scales with how many Augs you've purchased for yourself
` `
} }

@ -19,6 +19,7 @@ import { CONSTANTS } from "../Constants";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { showLiterature } from "../Literature"; import { showLiterature } from "../Literature";
import { Locations } from "../Locations"; import { Locations } from "../Locations";
import { createCityMap } from "../Locations/Cities";
import { Player } from "../Player"; import { Player } from "../Player";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
@ -590,7 +591,7 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
//At the start of the export state, set the imports of everything to 0 //At the start of the export state, set the imports of everything to 0
if (this.state === "EXPORT") { if (this.state === "EXPORT") {
for (var i = 0; i < Cities.length; ++i) { for (let i = 0; i < Cities.length; ++i) {
var city = Cities[i], office = this.offices[city]; var city = Cities[i], office = this.offices[city];
if (!(this.warehouses[city] instanceof Warehouse)) { if (!(this.warehouses[city] instanceof Warehouse)) {
continue; continue;
@ -605,7 +606,7 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
} }
} }
for (var i = 0; i < Cities.length; ++i) { for (let i = 0; i < Cities.length; ++i) {
var city = Cities[i], office = this.offices[city]; var city = Cities[i], office = this.offices[city];
if (this.warehouses[city] instanceof Warehouse) { if (this.warehouses[city] instanceof Warehouse) {
@ -665,19 +666,17 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
prod = maxProd; prod = maxProd;
} }
prod *= (SecsPerMarketCycle * marketCycles); //Convert production from per second to per market cycle prod *= (SecsPerMarketCycle * marketCycles); //Convert production from per second to per market cycle
//Calculate net change in warehouse storage making
//the produced materials will cost // Calculate net change in warehouse storage making the produced materials will cost
var totalMatSize = 0; var totalMatSize = 0;
for (var tmp = 0; tmp < this.prodMats.length; ++tmp) { for (let tmp = 0; tmp < this.prodMats.length; ++tmp) {
totalMatSize += (MaterialSizes[this.prodMats[tmp]]); totalMatSize += (MaterialSizes[this.prodMats[tmp]]);
} }
for (var reqMatName in this.reqMats) { for (const reqMatName in this.reqMats) {
if (this.reqMats.hasOwnProperty(reqMatName)) { var normQty = this.reqMats[reqMatName];
var normQty = this.reqMats[reqMatName]; totalMatSize -= (MaterialSizes[reqMatName] * normQty);
totalMatSize -= (MaterialSizes[reqMatName] * normQty);
}
} }
//If not enough space in warehouse, limit the amount of produced materials // If not enough space in warehouse, limit the amount of produced materials
if (totalMatSize > 0) { if (totalMatSize > 0) {
var maxAmt = Math.floor((warehouse.size - warehouse.sizeUsed) / totalMatSize); var maxAmt = Math.floor((warehouse.size - warehouse.sizeUsed) / totalMatSize);
prod = Math.min(maxAmt, prod); prod = Math.min(maxAmt, prod);
@ -685,10 +684,10 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
if (prod < 0) {prod = 0;} if (prod < 0) {prod = 0;}
//Keep track of production for smart supply (/s) // Keep track of production for smart supply (/s)
warehouse.smartSupplyStore += (prod / (SecsPerMarketCycle * marketCycles)); warehouse.smartSupplyStore += (prod / (SecsPerMarketCycle * marketCycles));
//Make sure we have enough resource to make our materials // Make sure we have enough resource to make our materials
var producableFrac = 1; var producableFrac = 1;
for (var reqMatName in this.reqMats) { for (var reqMatName in this.reqMats) {
if (this.reqMats.hasOwnProperty(reqMatName)) { if (this.reqMats.hasOwnProperty(reqMatName)) {
@ -700,17 +699,15 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
} }
if (producableFrac <= 0) {producableFrac = 0; prod = 0;} if (producableFrac <= 0) {producableFrac = 0; prod = 0;}
//Make our materials if they are producable // Make our materials if they are producable
if (producableFrac > 0 && prod > 0) { if (producableFrac > 0 && prod > 0) {
for (var reqMatName in this.reqMats) { for (const reqMatName in this.reqMats) {
if (this.reqMats.hasOwnProperty(reqMatName)) { var reqMatQtyNeeded = (this.reqMats[reqMatName] * prod * producableFrac);
var reqMatQtyNeeded = (this.reqMats[reqMatName] * prod * producableFrac); warehouse.materials[reqMatName].qty -= reqMatQtyNeeded;
warehouse.materials[reqMatName].qty -= reqMatQtyNeeded; warehouse.materials[reqMatName].prd = 0;
warehouse.materials[reqMatName].prd = 0; warehouse.materials[reqMatName].prd -= reqMatQtyNeeded / (SecsPerMarketCycle * marketCycles);
warehouse.materials[reqMatName].prd -= reqMatQtyNeeded / (SecsPerMarketCycle * marketCycles);
}
} }
for (var j = 0; j < this.prodMats.length; ++j) { for (let j = 0; j < this.prodMats.length; ++j) {
warehouse.materials[this.prodMats[j]].qty += (prod * producableFrac); warehouse.materials[this.prodMats[j]].qty += (prod * producableFrac);
warehouse.materials[this.prodMats[j]].qlt = warehouse.materials[this.prodMats[j]].qlt =
(office.employeeProd[EmployeePositions.Engineer] / 90 + (office.employeeProd[EmployeePositions.Engineer] / 90 +
@ -718,7 +715,7 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
Math.pow(warehouse.materials["AICores"].qty, this.aiFac) / 10e3); Math.pow(warehouse.materials["AICores"].qty, this.aiFac) / 10e3);
} }
} else { } else {
for (var reqMatName in this.reqMats) { for (const reqMatName in this.reqMats) {
if (this.reqMats.hasOwnProperty(reqMatName)) { if (this.reqMats.hasOwnProperty(reqMatName)) {
warehouse.materials[reqMatName].prd = 0; warehouse.materials[reqMatName].prd = 0;
} }
@ -726,18 +723,16 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
} }
//Per second //Per second
var fooProd = prod * producableFrac / (SecsPerMarketCycle * marketCycles); const fooProd = prod * producableFrac / (SecsPerMarketCycle * marketCycles);
for (var fooI = 0; fooI < this.prodMats.length; ++fooI) { for (let fooI = 0; fooI < this.prodMats.length; ++fooI) {
warehouse.materials[this.prodMats[fooI]].prd = fooProd; warehouse.materials[this.prodMats[fooI]].prd = fooProd;
} }
} else { } else {
//If this doesn't produce any materials, then it only creates //If this doesn't produce any materials, then it only creates
//Products. Creating products will consume materials. The //Products. Creating products will consume materials. The
//Production of all consumed materials must be set to 0 //Production of all consumed materials must be set to 0
for (var reqMatName in this.reqMats) { for (const reqMatName in this.reqMats) {
if (this.reqMats.hasOwnProperty(reqMatName)) { warehouse.materials[reqMatName].prd = 0;
warehouse.materials[reqMatName].prd = 0;
}
} }
} }
break; break;
@ -751,12 +746,39 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
mat.sll = 0; mat.sll = 0;
continue; continue;
} }
var mat = warehouse.materials[matName];
// Calculate sale cost // Sale multipliers
const businessFactor = this.getBusinessFactor(office); //Business employee productivity
const advertisingFactor = this.getAdvertisingFactors()[0]; //Awareness + popularity
const marketFactor = this.getMarketFactor(mat); //Competition + demand
// Determine the cost that the material will be sold at
const markupLimit = mat.getMarkupLimit(); const markupLimit = mat.getMarkupLimit();
var sCost; var sCost;
if (mat.marketTa1) { if (mat.marketTa2) {
const prod = mat.prd;
// Reverse engineer the 'maxSell' formula
// 1. Set 'maxSell' = prod
// 2. Substitute formula for 'markup'
// 3. Solve for 'sCost'
const numerator = markupLimit;
const sqrtNumerator = prod;
const sqrtDenominator = ((mat.qlt + .001)
* marketFactor
* businessFactor
* company.getSalesMultiplier()
* advertisingFactor
* this.getSalesMultiplier());
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
const optimalPrice = (numerator / denominator) + mat.bCost;
// We'll store this "Optimal Price" in a property so that we don't have
// to re-calculate it for the UI
mat.marketTa2Price = optimalPrice;
sCost = optimalPrice;
} else if (mat.marketTa1) {
sCost = mat.bCost + markupLimit; sCost = mat.bCost + markupLimit;
} else if (isString(mat.sCost)) { } else if (isString(mat.sCost)) {
sCost = mat.sCost.replace(/MP/g, mat.bCost); sCost = mat.sCost.replace(/MP/g, mat.bCost);
@ -780,9 +802,7 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
markup = mat.bCost / sCost; markup = mat.bCost / sCost;
} }
} }
var businessFactor = this.getBusinessFactor(office); //Business employee productivity
var advertisingFactor = this.getAdvertisingFactors()[0]; //Awareness + popularity
var marketFactor = this.getMarketFactor(mat); //Competition + demand
var maxSell = (mat.qlt + .001) var maxSell = (mat.qlt + .001)
* marketFactor * marketFactor
* markup * markup
@ -905,8 +925,8 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
//Produce Scientific Research based on R&D employees //Produce Scientific Research based on R&D employees
//Scientific Research can be produced without a warehouse //Scientific Research can be produced without a warehouse
if (office instanceof OfficeSpace) { if (office instanceof OfficeSpace) {
this.sciResearch.qty += (.005 this.sciResearch.qty += (.004
* Math.pow(office.employeeProd[EmployeePositions.RandD], 0.55) * Math.pow(office.employeeProd[EmployeePositions.RandD], 0.5)
* company.getScientificResearchMultiplier() * company.getScientificResearchMultiplier()
* this.getScientificResearchMultiplier()); * this.getScientificResearchMultiplier());
} }
@ -962,9 +982,9 @@ Industry.prototype.processProducts = function(marketCycles=1, corporation) {
//Processes FINISHED products //Processes FINISHED products
Industry.prototype.processProduct = function(marketCycles=1, product, corporation) { Industry.prototype.processProduct = function(marketCycles=1, product, corporation) {
var totalProfit = 0; let totalProfit = 0;
for (var i = 0; i < Cities.length; ++i) { for (let i = 0; i < Cities.length; ++i) {
var city = Cities[i], office = this.offices[city], warehouse = this.warehouses[city]; let city = Cities[i], office = this.offices[city], warehouse = this.warehouses[city];
if (warehouse instanceof Warehouse) { if (warehouse instanceof Warehouse) {
switch(this.state) { switch(this.state) {
@ -1040,27 +1060,60 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
} }
} }
//Since its a product, its production cost is increased for labor // Since its a product, its production cost is increased for labor
product.pCost *= ProductProductionCostRatio; product.pCost *= ProductProductionCostRatio;
//Calculate Sale Cost (sCost), which could be dynamically evaluated // Sale multipliers
const businessFactor = this.getBusinessFactor(office); //Business employee productivity
const advertisingFactor = this.getAdvertisingFactors()[0]; //Awareness + popularity
const marketFactor = this.getMarketFactor(product); //Competition + demand
// Calculate Sale Cost (sCost), which could be dynamically evaluated
const markupLimit = product.rat / product.mku;
var sCost; var sCost;
if (isString(product.sCost)) { if (product.marketTa2) {
const prod = product.data[city][1];
// Reverse engineer the 'maxSell' formula
// 1. Set 'maxSell' = prod
// 2. Substitute formula for 'markup'
// 3. Solve for 'sCost'roduct.pCost = sCost
const numerator = markupLimit;
const sqrtNumerator = prod;
const sqrtDenominator = (0.5
* Math.pow(product.rat, 0.65)
* marketFactor
* corporation.getSalesMultiplier()
* businessFactor
* advertisingFactor
* this.getSalesMultiplier());
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
let optimalPrice;
if (sqrtDenominator === 0 || denominator === 0) {
optimalPrice = 0;
} else {
optimalPrice = (numerator / denominator) + product.pCost;
}
// Store this "optimal Price" in a property so we don't have to re-calculate for UI
product.marketTa2Price[city] = optimalPrice;
sCost = optimalPrice;
} else if (product.marketTa1) {
sCost = product.pCost + markupLimit;
} else if (isString(product.sCost)) {
sCost = product.sCost.replace(/MP/g, product.pCost + product.rat / product.mku); sCost = product.sCost.replace(/MP/g, product.pCost + product.rat / product.mku);
sCost = eval(sCost); sCost = eval(sCost);
} else { } else {
sCost = product.sCost; sCost = product.sCost;
} }
var markup = 1, markupLimit = product.rat / product.mku; var markup = 1;
if (sCost > product.pCost) { if (sCost > product.pCost) {
if ((sCost - product.pCost) > markupLimit) { if ((sCost - product.pCost) > markupLimit) {
markup = markupLimit / (sCost - product.pCost); markup = markupLimit / (sCost - product.pCost);
} }
} }
var businessFactor = this.getBusinessFactor(office); //Business employee productivity
var advertisingFactor = this.getAdvertisingFactors()[0]; //Awareness + popularity
var marketFactor = this.getMarketFactor(product); //Competition + demand
var maxSell = 0.5 var maxSell = 0.5
* Math.pow(product.rat, 0.65) * Math.pow(product.rat, 0.65)
* marketFactor * marketFactor
@ -1085,8 +1138,9 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
} else if (product.sllman[city][0] && product.sllman[city][1] > 0) { } else if (product.sllman[city][0] && product.sllman[city][1] > 0) {
//Sell amount is manually limited //Sell amount is manually limited
sellAmt = Math.min(maxSell, product.sllman[city][1]); sellAmt = Math.min(maxSell, product.sllman[city][1]);
} else if (product.sllman[city][0] === false){
sellAmt = 0;
} else { } else {
//Backwards compatibility, -1 = 0
sellAmt = maxSell; sellAmt = maxSell;
} }
if (sellAmt < 0) { sellAmt = 0; } if (sellAmt < 0) { sellAmt = 0; }
@ -1114,8 +1168,7 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
return totalProfit; return totalProfit;
} }
Industry.prototype.discontinueProduct = function(product, parentRefs) { Industry.prototype.discontinueProduct = function(product) {
var company = parentRefs.company, industry = parentRefs.industry;
for (var productName in this.products) { for (var productName in this.products) {
if (this.products.hasOwnProperty(productName)) { if (this.products.hasOwnProperty(productName)) {
if (product === this.products[productName]) { if (product === this.products[productName]) {
@ -1182,7 +1235,7 @@ Industry.prototype.getOfficeProductivity = function(office, params) {
// Returns a multiplier based on the office' 'Business' employees that affects sales // Returns a multiplier based on the office' 'Business' employees that affects sales
Industry.prototype.getBusinessFactor = function(office) { Industry.prototype.getBusinessFactor = function(office) {
const businessProd = 1 + office.employeeProd[EmployeePositions.Business]; const businessProd = 1 + office.employeeProd[EmployeePositions.Business];
return calculateEffectWithFactors(businessProd, 0.26, 10e3); return calculateEffectWithFactors(businessProd, 0.26, 10e3);
} }

@ -66,6 +66,7 @@ export class Material {
// Flags that signal whether automatic sale pricing through Market TA is enabled // Flags that signal whether automatic sale pricing through Market TA is enabled
marketTa1: boolean = false; marketTa1: boolean = false;
marketTa2: boolean = false; marketTa2: boolean = false;
marketTa2Price: number = 0;
constructor(params: IConstructorParams = {}) { constructor(params: IConstructorParams = {}) {
if (params.name) { this.name = params.name; } if (params.name) { this.name = params.name; }

@ -2,15 +2,17 @@ import { IMap } from "../types";
// Map of material (by name) to their sizes (how much space it takes in warehouse) // Map of material (by name) to their sizes (how much space it takes in warehouse)
export const MaterialSizes: IMap<number> = { export const MaterialSizes: IMap<number> = {
Water: 0.05, Water: 0.05,
Energy: 0.01, Energy: 0.01,
Food: 0.03, Food: 0.03,
Plants: 0.05, Plants: 0.05,
Metal: 0.1, Metal: 0.1,
Hardware: 0.06, Hardware: 0.06,
Chemicals: 0.05, Chemicals: 0.05,
Drugs: 0.02, Drugs: 0.02,
Robots: 0.5, Robots: 0.5,
AICores: 0.1, AICores: 0.1,
RealEstate: 0, RealEstate: 0,
"Real Estate": 0,
"AI Cores": 0,
} }

@ -4,8 +4,10 @@ import { ProductRatingWeights,
IProductRatingWeight } from "./ProductRatingWeights"; IProductRatingWeight } from "./ProductRatingWeights";
import { Cities } from "../Locations/Cities"; import { Cities } from "../Locations/Cities";
import { createCityMap } from "../Locations/createCityMap";
import { IMap } from "../types"; import { IMap } from "../types";
import { Generic_fromJSON, import { Generic_fromJSON,
Generic_toJSON, Generic_toJSON,
Reviver } from "../../utils/JSONReviver"; Reviver } from "../../utils/JSONReviver";
@ -89,14 +91,7 @@ export class Product {
// Data refers to the production, sale, and quantity of the products // Data refers to the production, sale, and quantity of the products
// These values are specific to a city // These values are specific to a city
// For each city, the data is [qty, prod, sell] // For each city, the data is [qty, prod, sell]
data: IMap<number[]> = { data: IMap<number[]> = createCityMap<number[]>([0, 0, 0]);
[Cities.Aevum]: [0, 0, 0],
[Cities.Chongqing]: [0, 0, 0],
[Cities.Sector12]: [0, 0, 0],
[Cities.NewTokyo]: [0, 0, 0],
[Cities.Ishima]: [0, 0, 0],
[Cities.Volhaven]: [0, 0, 0],
}
// Location of this Product // Location of this Product
// Only applies for location-based products like restaurants/hospitals // Only applies for location-based products like restaurants/hospitals
@ -113,23 +108,13 @@ export class Product {
// Data to keep track of whether production/sale of this Product is // Data to keep track of whether production/sale of this Product is
// manually limited. These values are specific to a city // manually limited. These values are specific to a city
// [Whether production/sale is limited, limit amount] // [Whether production/sale is limited, limit amount]
prdman: IMap<any[]> = { prdman: IMap<any[]> = createCityMap<any[]>([false, 0]);
[Cities.Aevum]: [false, 0], sllman: IMap<any[]> = createCityMap<any[]>([false, 0]);
[Cities.Chongqing]: [false, 0],
[Cities.Sector12]: [false, 0],
[Cities.NewTokyo]: [false, 0],
[Cities.Ishima]: [false, 0],
[Cities.Volhaven]: [false, 0],
}
sllman: IMap<any[]> = { // Flags that signal whether automatic sale pricing through Market TA is enabled
[Cities.Aevum]: [false, 0], marketTa1: boolean = false;
[Cities.Chongqing]: [false, 0], marketTa2: boolean = false;
[Cities.Sector12]: [false, 0], marketTa2Price: IMap<number> = createCityMap<number>(0);
[Cities.NewTokyo]: [false, 0],
[Cities.Ishima]: [false, 0],
[Cities.Volhaven]: [false, 0],
}
constructor(params: IConstructorParams={}) { constructor(params: IConstructorParams={}) {
this.name = params.name ? params.name : ""; this.name = params.name ? params.name : "";

@ -46,6 +46,10 @@ export class Warehouse {
// Whether Smart Supply is enabled for this Industry (the Industry that this Warehouse is for) // Whether Smart Supply is enabled for this Industry (the Industry that this Warehouse is for)
smartSupplyEnabled: boolean = false; smartSupplyEnabled: boolean = false;
// Flag that indicates whether Smart Supply accounts for imports when calculating
// the amount fo purchase
smartSupplyConsiderExports: boolean = false;
// Stores the amount of product to be produced. Used for Smart Supply unlock. // Stores the amount of product to be produced. Used for Smart Supply unlock.
// The production tracked by smart supply is always based on the previous cycle, // The production tracked by smart supply is always based on the previous cycle,
// so it will always trail the "true" production by 1 cycle // so it will always trail the "true" production by 1 cycle

@ -94,11 +94,13 @@ export const researchMetadata: IConstructorParams[] = [
}, },
{ {
name: "Market-TA.II", name: "Market-TA.II",
cost: 40e3, cost: 50e3,
desc: "Develop double-advanced AI software that uses technical analysis to " + desc: "Develop double-advanced AI software that uses technical analysis to " +
"help you understand and exploit the market. This research " + "help you understand and exploit the market. This research " +
"allows you to know how many sales of a Material/Product you lose or gain " + "allows you to know how many sales of a Material/Product you lose or gain " +
"from having too high or too low or a sale price.", "from having too high or too low or a sale price. It also lets you automatically " +
"set the sale price of your Materials/Products at the optimal price such that " +
"the amount sold matches the amount produced.",
}, },
{ {
name: "Overclock", name: "Overclock",

@ -17,10 +17,14 @@ import { Industries,
IndustryDescriptions, IndustryDescriptions,
IndustryResearchTrees } from "../IndustryData"; IndustryResearchTrees } from "../IndustryData";
import { MaterialSizes } from "../MaterialSizes";
import { Product } from "../Product"; import { Product } from "../Product";
import { Player } from "../../Player"; import { Player } from "../../Player";
import { Cities } from "../../Locations/Cities";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
@ -81,7 +85,7 @@ export class CorporationEventHandler {
var totalAmount = Number(money) + (stockShares * stockPrice); var totalAmount = Number(money) + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio; var repGain = totalAmount / BribeToRepRatio;
repGainText.innerText = "You will gain " + numeralWrapper.formatNumber(repGain, "0,0") + repGainText.innerText = "You will gain " + numeralWrapper.format(repGain, "0,0") +
" reputation with " + " reputation with " +
factionSelector.options[factionSelector.selectedIndex].value + factionSelector.options[factionSelector.selectedIndex].value +
" with this bribe"; " with this bribe";
@ -104,7 +108,7 @@ export class CorporationEventHandler {
var totalAmount = money + (stockShares * stockPrice); var totalAmount = money + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio; var repGain = totalAmount / BribeToRepRatio;
console.log("repGain: " + repGain); console.log("repGain: " + repGain);
repGainText.innerText = "You will gain " + numeralWrapper.formatNumber(repGain, "0,0") + repGainText.innerText = "You will gain " + numeralWrapper.format(repGain, "0,0") +
" reputation with " + " reputation with " +
factionSelector.options[factionSelector.selectedIndex].value + factionSelector.options[factionSelector.selectedIndex].value +
" with this bribe"; " with this bribe";
@ -131,7 +135,7 @@ export class CorporationEventHandler {
} else { } else {
var totalAmount = money + (stockShares * stockPrice); var totalAmount = money + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio; var repGain = totalAmount / BribeToRepRatio;
dialogBoxCreate("You gained " + formatNumber(repGain, 0) + dialogBoxCreate("You gained " + numeralWrapper.format(repGain, "0,0") +
" reputation with " + fac.name + " by bribing them."); " reputation with " + fac.name + " by bribing them.");
fac.playerReputation += repGain; fac.playerReputation += repGain;
this.corp.funds = this.corp.funds.minus(money); this.corp.funds = this.corp.funds.minus(money);
@ -170,7 +174,6 @@ export class CorporationEventHandler {
type:"number", placeholder:"Shares to buyback", margin:"5px", type:"number", placeholder:"Shares to buyback", margin:"5px",
inputListener: ()=> { inputListener: ()=> {
var numShares = Math.round(input.value); var numShares = Math.round(input.value);
//TODO add conditional for if player doesn't have enough money
if (isNaN(numShares) || numShares <= 0) { if (isNaN(numShares) || numShares <= 0) {
costIndicator.innerText = "ERROR: Invalid value entered for number of shares to buyback" costIndicator.innerText = "ERROR: Invalid value entered for number of shares to buyback"
} else if (numShares > this.corp.issuedShares) { } else if (numShares > this.corp.issuedShares) {
@ -228,7 +231,7 @@ export class CorporationEventHandler {
} }
// Create a popup that lets the player discontinue a product // Create a popup that lets the player discontinue a product
createDiscontinueProductPopup(product) { createDiscontinueProductPopup(product, industry) {
const popupId = "cmpy-mgmt-discontinue-product-popup"; const popupId = "cmpy-mgmt-discontinue-product-popup";
const txt = createElement("p", { const txt = createElement("p", {
innerText:"Are you sure you want to do this? Discontinuing a product " + innerText:"Are you sure you want to do this? Discontinuing a product " +
@ -237,9 +240,9 @@ export class CorporationEventHandler {
"removed and left unsold", "removed and left unsold",
}); });
const confirmBtn = createElement("button", { const confirmBtn = createElement("button", {
class:"a-link-button",innerText:"Discontinue", class:"popup-box-button",innerText:"Discontinue",
clickListener: () => { clickListener: () => {
industry.discontinueProduct(product, parentRefs); industry.discontinueProduct(product);
removeElementById(popupId); removeElementById(popupId);
this.rerender(); this.rerender();
return false; return false;
@ -247,7 +250,7 @@ export class CorporationEventHandler {
}); });
const cancelBtn = createPopupCloseButton(popupId, { innerText: "Cancel" }); const cancelBtn = createPopupCloseButton(popupId, { innerText: "Cancel" });
createPopup(popupId, [txt, confirmBtn, cancelBtn]); createPopup(popupId, [txt, cancelBtn, confirmBtn]);
} }
// Create a popup that lets the player manage exports // Create a popup that lets the player manage exports
@ -669,8 +672,8 @@ export class CorporationEventHandler {
productNameInput.focus(); productNameInput.focus();
} }
// Create a popup that lets the player use the Market TA research // Create a popup that lets the player use the Market TA research for Materials
createMarketTaPopup(mat, industry) { createMaterialMarketTaPopup(mat, industry) {
const corp = this.corp; const corp = this.corp;
const popupId = "cmpy-mgmt-marketta-popup"; const popupId = "cmpy-mgmt-marketta-popup";
@ -694,19 +697,21 @@ export class CorporationEventHandler {
"be sold at the price identified by Market-TA.I (i.e. the price shown above)" "be sold at the price identified by Market-TA.I (i.e. the price shown above)"
}) })
const useTa1AutoSaleCheckbox = createElement("input", { const useTa1AutoSaleCheckbox = createElement("input", {
checked: mat.marketTa1,
id: useTa1AutoSaleId, id: useTa1AutoSaleId,
margin: "3px",
type: "checkbox", type: "checkbox",
value: mat.marketTa1,
changeListener: (e) => { changeListener: (e) => {
mat.marketTa1 = e.target.value; mat.marketTa1 = e.target.checked;
} }
}); });
useTa1AutoSaleDiv.appendChild(useTa1AutoSaleCheckbox); useTa1AutoSaleDiv.appendChild(useTa1AutoSaleLabel);
useTa1AutoSaleDiv.appendChild(useTa1AutoSaleCheckbox); useTa1AutoSaleDiv.appendChild(useTa1AutoSaleCheckbox);
const closeBtn = createPopupCloseButton(popupId, { const closeBtn = createPopupCloseButton(popupId, {
class: "std-button", class: "std-button",
display: "block", display: "block",
innerText: "Close",
}); });
if (industry.hasResearch("Market-TA.II")) { if (industry.hasResearch("Market-TA.II")) {
@ -741,11 +746,36 @@ export class CorporationEventHandler {
} }
ta2Text.innerHTML = `<br><u><strong>Market-TA.II</strong></u><br>` + ta2Text.innerHTML = `<br><u><strong>Market-TA.II</strong></u><br>` +
`If you sell at ${numeralWrapper.formatMoney(sCost)}, ` + `If you sell at ${numeralWrapper.formatMoney(sCost)}, ` +
`then you will sell ${formatNumber(markup, 5)}x as much compared ` + `then you will sell ${numeralWrapper.format(markup, "0.00000")}x as much compared ` +
`to if you sold at market price.`; `to if you sold at market price.`;
} }
updateTa2Text(); updateTa2Text();
createPopup(popupId, [ta1, ta2Text, ta2Input, closeBtn]);
// 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);
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
} else { } else {
// Market-TA.I only // Market-TA.I only
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]); createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
@ -775,6 +805,7 @@ export class CorporationEventHandler {
display:"inline-block", display:"inline-block",
innerText: "Confirm", innerText: "Confirm",
clickListener: () => { clickListener: () => {
if (citySelector.length <= 0) { return false; }
let city = citySelector.options[citySelector.selectedIndex].value; let city = citySelector.options[citySelector.selectedIndex].value;
if (this.corp.funds.lt(OfficeInitialCost)) { if (this.corp.funds.lt(OfficeInitialCost)) {
dialogBoxCreate("You don't have enough company funds to open a new office!"); dialogBoxCreate("You don't have enough company funds to open a new office!");
@ -921,8 +952,110 @@ export class CorporationEventHandler {
return false; return false;
} }
// Create a popup that lets the player use the Market TA research for Products
createProductMarketTaPopup(product, industry) {
const corp = this.corp;
const popupId = "cmpy-mgmt-marketta-popup";
const markupLimit = product.rat / product.mku;
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(product.pCost + 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 Product will automatically " +
"be sold at the price identified by Market-TA.I (i.e. the price shown above)"
})
const useTa1AutoSaleCheckbox = createElement("input", {
checked: product.marketTa1,
id: useTa1AutoSaleId,
margin: "3px",
type: "checkbox",
changeListener: (e) => {
product.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: product.pCost,
});
// Function that updates the text in ta2Text element
updateTa2Text = function() {
const sCost = parseFloat(ta2Input.value);
let markup = 1;
if (sCost > product.pCost) {
if ((sCost - product.pCost) > markupLimit) {
markup = markupLimit / (sCost - product.pCost);
}
}
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 Product 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: product.marketTa2,
id: useTa2AutoSaleId,
margin: "3px",
type: "checkbox",
changeListener: (e) => {
product.marketTa2 = e.target.checked;
}
});
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
} else {
// Market-TA.I only
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
}
}
// Create a popup that lets the player purchase a Material // Create a popup that lets the player purchase a Material
createPurchaseMaterialPopup(mat, industry) { createPurchaseMaterialPopup(mat, industry, warehouse) {
const corp = this.corp; const corp = this.corp;
const purchasePopupId = "cmpy-mgmt-material-purchase-popup"; const purchasePopupId = "cmpy-mgmt-material-purchase-popup";
@ -980,15 +1113,20 @@ export class CorporationEventHandler {
let bulkPurchaseCostTxt = createElement("p"); let bulkPurchaseCostTxt = createElement("p");
function updateBulkPurchaseText(amount) { function updateBulkPurchaseText(amount) {
const cost = parseFloat(amount) * mat.bCost; const parsedAmt = parseFloat(amount);
if (isNaN(cost)) { const cost = parsedAmt * mat.bCost;
dialogBoxCreate(`Bulk Purchase Cost calculated to be NaN. This is either due to ` +
`invalid input, or it is a bug (in which case you should report to dev)`);
return;
}
bulkPurchaseCostTxt.innerText = `Purchasing ${numeralWrapper.format(amt, "0,0.00")} of ` + const matSize = MaterialSizes[mat.name];
`${mat.name} will cost ${numeralWrapper.formatMoney(cost)}`; const maxAmount = ((warehouse.size - warehouse.sizeUsed) / matSize);
if (parsedAmt * matSize > maxAmount) {
bulkPurchaseCostTxt.innerText = "Not enough warehouse space to purchase this amount";
} else if (isNaN(cost)) {
bulkPurchaseCostTxt.innerText = "Invalid put for Bulk Purchase amount";
} else {
bulkPurchaseCostTxt.innerText = `Purchasing ${numeralWrapper.format(parsedAmt, "0,0.00")} of ` +
`${mat.name} will cost ${numeralWrapper.formatMoney(cost)}`;
}
} }
let bulkPurchaseConfirmBtn; let bulkPurchaseConfirmBtn;
@ -998,7 +1136,7 @@ export class CorporationEventHandler {
type: "number", type: "number",
onkeyup: (e) => { onkeyup: (e) => {
e.preventDefault(); e.preventDefault();
updateBulkPurchaseText(); updateBulkPurchaseText(e.target.value);
if (e.keyCode === KEY.ENTER) {bulkPurchaseConfirmBtn.click();} if (e.keyCode === KEY.ENTER) {bulkPurchaseConfirmBtn.click();}
} }
}); });
@ -1007,7 +1145,15 @@ export class CorporationEventHandler {
class: "std-button", class: "std-button",
innerText: "Confirm Bulk Purchase", innerText: "Confirm Bulk Purchase",
clickListener: () => { clickListener: () => {
const amount = parseFloat(input.value); const amount = parseFloat(bulkPurchaseInput.value);
const matSize = MaterialSizes[mat.name];
const maxAmount = ((warehouse.size - warehouse.sizeUsed) / matSize);
if (amount * matSize > maxAmount) {
dialogBoxCreate(`You do not have enough warehouse size to fit this purchase`);
return false;
}
if (isNaN(amount)) { if (isNaN(amount)) {
dialogBoxCreate("Invalid input amount"); dialogBoxCreate("Invalid input amount");
} else { } else {
@ -1065,9 +1211,18 @@ export class CorporationEventHandler {
if (e.keyCode === KEY.ENTER) {confirmBtn.click();} if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
} }
}); });
let inputButtonInitValue = mat.sCost ? mat.sCost : null;
if (mat.marketTa2) {
inputButtonInitValue += " (Market-TA.II)";
} else if (mat.marketTa1) {
inputButtonInitValue += " (Market-TA.I)";
}
const inputPx = createElement("input", { const inputPx = createElement("input", {
type: "text", marginTop: "4px", type: "text", marginTop: "4px",
value: mat.sCost ? mat.sCost : null, placeholder: "Sell price", value: inputButtonInitValue,
placeholder: "Sell price",
onkeyup: (e) => { onkeyup: (e) => {
e.preventDefault(); e.preventDefault();
if (e.keyCode === KEY.ENTER) {confirmBtn.click();} if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
@ -1179,16 +1334,41 @@ export class CorporationEventHandler {
if (e.keyCode === KEY.ENTER) {confirmBtn.click();} if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
} }
}); });
let inputButtonInitValue = product.sCost ? product.sCost : null;
if (product.marketTa2) {
inputButtonInitValue += " (Market-TA.II)";
} else if (product.marketTa1) {
inputButtonInitValue += " (Market-TA.I)";
}
const inputPx = createElement("input", { const inputPx = createElement("input", {
margin: "5px 0px 5px 0px", margin: "5px 0px 5px 0px",
placeholder: "Sell price", placeholder: "Sell price",
type: "text", type: "text",
value: product.sCost ? product.sCost : null, value: inputButtonInitValue,
onkeyup: (e) => { onkeyup: (e) => {
e.preventDefault(); e.preventDefault();
if (e.keyCode === KEY.ENTER) {confirmBtn.click();} if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
} }
}); });
const checkboxDiv = createElement("div", {
border: "1px solid white",
display: "inline-block",
})
const checkboxLabel = createElement("label", {
for: popupId + "-checkbox",
innerText: "Use same 'Sell Amount' for all cities",
});
const checkbox = createElement("input", {
checked: true,
id: popupId + "-checkbox",
margin: "2px",
type: "checkbox",
});
checkboxDiv.appendChild(checkboxLabel);
checkboxDiv.appendChild(checkbox);
confirmBtn = createElement("button", { confirmBtn = createElement("button", {
class: "std-button", class: "std-button",
innerText: "Confirm", innerText: "Confirm",
@ -1220,7 +1400,10 @@ export class CorporationEventHandler {
product.sCost = cost; product.sCost = cost;
} }
//Parse quantity // Array of all cities. Used later
const cities = Object.values(Cities);
// Parse quantity
if (inputQty.value.includes("MAX") || inputQty.value.includes("PROD")) { if (inputQty.value.includes("MAX") || inputQty.value.includes("PROD")) {
//Dynamically evaluated quantity. First test to make sure its valid //Dynamically evaluated quantity. First test to make sure its valid
var qty = inputQty.value.replace(/\s+/g, ''); var qty = inputQty.value.replace(/\s+/g, '');
@ -1238,8 +1421,16 @@ export class CorporationEventHandler {
dialogBoxCreate("Invalid value or expression for sell price field"); dialogBoxCreate("Invalid value or expression for sell price field");
return false; return false;
} }
product.sllman[city][0] = true; if (checkbox.checked) {
product.sllman[city][1] = qty; //Use sanitized input for (let i = 0; i < cities.length; ++i) {
const tempCity = cities[i];
product.sllman[tempCity][0] = true;
product.sllman[tempCity][1] = qty; //Use sanitized input
}
} else {
product.sllman[city][0] = true;
product.sllman[city][1] = qty; //Use sanitized input
}
} else if (isNaN(inputQty.value)) { } else if (isNaN(inputQty.value)) {
dialogBoxCreate("Invalid value for sell quantity field! Must be numeric"); dialogBoxCreate("Invalid value for sell quantity field! Must be numeric");
return false; return false;
@ -1247,10 +1438,25 @@ export class CorporationEventHandler {
var qty = parseFloat(inputQty.value); var qty = parseFloat(inputQty.value);
if (isNaN(qty)) {qty = 0;} if (isNaN(qty)) {qty = 0;}
if (qty === 0) { if (qty === 0) {
product.sllman[city][0] = false; if (checkbox.checked) {
for (let i = 0; i < cities.length; ++i) {
const tempCity = cities[i];
product.sllman[tempCity][0] = false;
}
} else {
product.sllman[city][0] = false;
}
} else { } else {
product.sllman[city][0] = true; if (checkbox.checked) {
product.sllman[city][1] = qty; for (let i = 0; i < cities.length; ++i) {
const tempCity = cities[i];
product.sllman[tempCity][0] = true;
product.sllman[tempCity][1] = qty;
}
} else {
product.sllman[city][0] = true;
product.sllman[city][1] = qty;
}
} }
} }
@ -1259,9 +1465,12 @@ export class CorporationEventHandler {
return false; return false;
} }
}); });
const cancelBtn = createPopupCloseButton(popupId, { innerText: "Cancel" }); const cancelBtn = createPopupCloseButton(popupId, { class: "std-button" });
createPopup(popupId, [txt, inputQty, inputPx, confirmBtn, cancelBtn]); const linebreak1 = createElement("br");
createPopup(popupId, [txt, inputQty, inputPx, confirmBtn, cancelBtn, linebreak1,
checkboxDiv]);
inputQty.focus(); inputQty.focus();
} }

@ -305,7 +305,7 @@ export class IndustryOffice extends BaseReactComponent {
<p>Total Employee Salary: {numeralWrapper.formatMoney(totalSalary)}</p> <p>Total Employee Salary: {numeralWrapper.formatMoney(totalSalary)}</p>
{ {
vechain && vechain &&
<p className={"tooltip"} style={{display: "block"}}> <p className={"tooltip"} style={{display: "inline-block"}}>
Material Production: {numeralWrapper.format(division.getOfficeProductivity(office), "0.000")} Material Production: {numeralWrapper.format(division.getOfficeProductivity(office), "0.000")}
<span className={"tooltiptext"}> <span className={"tooltiptext"}>
The base amount of material this office can produce. Does not include The base amount of material this office can produce. Does not include
@ -314,9 +314,12 @@ export class IndustryOffice extends BaseReactComponent {
</span> </span>
</p> </p>
} }
{
vechain && <br />
}
{ {
vechain && vechain &&
<p className={"tooltip"} style={{display: "block"}}> <p className={"tooltip"} style={{display: "inline-block"}}>
Product Production: {numeralWrapper.format(division.getOfficeProductivity(office, {forProduct:true}), "0.000")} Product Production: {numeralWrapper.format(division.getOfficeProductivity(office, {forProduct:true}), "0.000")}
<span className={"tooltiptext"}> <span className={"tooltiptext"}>
The base amount of any given Product this office can produce. Does not include The base amount of any given Product this office can produce. Does not include
@ -325,15 +328,21 @@ export class IndustryOffice extends BaseReactComponent {
</span> </span>
</p> </p>
} }
{
vechain && <br />
}
{ {
vechain && vechain &&
<p className={"tooltip"} style={{display: "block"}}> <p className={"tooltip"} style={{display: "inline-block"}}>
Business Multiplier: x{numeralWrapper.format(division.getBusinessFactor(office), "0.000")} Business Multiplier: x{numeralWrapper.format(division.getBusinessFactor(office), "0.000")}
<span className={"tooltiptext"}> <span className={"tooltiptext"}>
The effect this office's 'Business' employees has on boosting sales The effect this office's 'Business' employees has on boosting sales
</span> </span>
</p> </p>
} }
{
vechain && <br />
}
<h2 className={"tooltip"} style={positionHeaderStyle}> <h2 className={"tooltip"} style={positionHeaderStyle}>
{EmployeePositions.Operations} ({this.state.numOperations}) {EmployeePositions.Operations} ({this.state.numOperations})

@ -3,13 +3,13 @@
import React from "react"; import React from "react";
import { BaseReactComponent } from "./BaseReactComponent"; import { BaseReactComponent } from "./BaseReactComponent";
import { Material } from "../Material"; import { OfficeSpace,
import { Product } from "../Product";
import { Warehouse,
WarehouseInitialCost, WarehouseInitialCost,
WarehouseUpgradeBaseCost, WarehouseUpgradeBaseCost,
ProductProductionCostRatio } from "../Corporation"; ProductProductionCostRatio } from "../Corporation";
import { Material } from "../Material";
import { Product } from "../Product";
import { Warehouse } from "../Warehouse";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
@ -45,7 +45,12 @@ function ProductComponent(props) {
sellButtonText = "Sell (0.000/0.000)"; sellButtonText = "Sell (0.000/0.000)";
} }
if (product.sCost) { if (product.marketTa2) {
sellButtonText += (" @ " + numeralWrapper.formatMoney(product.marketTa2Price[city]));
} else if (product.marketTa1) {
const markupLimit = product.rat / product.mku;
sellButtonText += (" @ " + numeralWrapper.formatMoney(product.pCost + markupLimit));
} else if (product.sCost) {
if (isString(product.sCost)) { if (isString(product.sCost)) {
sellButtonText += (" @ " + product.sCost); sellButtonText += (" @ " + product.sCost);
} else { } else {
@ -55,14 +60,17 @@ function ProductComponent(props) {
const sellButtonOnClick = eventHandler.createSellProductPopup.bind(eventHandler, product, city); const sellButtonOnClick = eventHandler.createSellProductPopup.bind(eventHandler, product, city);
// Limit Production button // Limit Production button
const limitProductionButtonText = "Limit Production"; let limitProductionButtonText = "Limit Production";
if (product.prdman[city][0]) { if (product.prdman[city][0]) {
limitProductionButtonText += " (" + numeralWrapper.format(product.prdman[city][1], nf) + ")"; limitProductionButtonText += " (" + numeralWrapper.format(product.prdman[city][1], nf) + ")";
} }
const limitProductionButtonOnClick = eventHandler.createLimitProductProdutionPopup.bind(eventHandler, product, city); const limitProductionButtonOnClick = eventHandler.createLimitProductProdutionPopup.bind(eventHandler, product, city);
// Discontinue Button // Discontinue Button
const discontinueButtonOnClick = eventHandler.createDiscontinueProductPopup.bind(eventHandler, product); const discontinueButtonOnClick = eventHandler.createDiscontinueProductPopup.bind(eventHandler, product, division);
// Market TA button
const marketTaButtonOnClick = eventHandler.createProductMarketTaPopup.bind(eventHandler, product, division);
// Unfinished Product // Unfinished Product
if (!product.fin) { if (!product.fin) {
@ -83,6 +91,12 @@ function ProductComponent(props) {
<button className={"std-button"} onClick={discontinueButtonOnClick}> <button className={"std-button"} onClick={discontinueButtonOnClick}>
Discontinue Discontinue
</button> </button>
{
division.hasResearch("Market-TA.I") &&
<button className={"std-button"} onClick={marketTaButtonOnClick}>
Market-TA
</button>
}
</div> </div>
</div> </div>
) )
@ -139,7 +153,7 @@ function ProductComponent(props) {
</span> </span>
</p><br /> </p><br />
<p className={"tooltip"}> <p className={"tooltip"}>
Est. Market Price: {numeralWrapper.formatMoney(product.pCost + product.rat / product.mku)} Est. Market Price: {numeralWrapper.formatMoney(product.pCost)}
<span className={"tooltiptext"}> <span className={"tooltiptext"}>
An estimate of how much consumers are willing to pay for this product. An estimate of how much consumers are willing to pay for this product.
Setting the sale price above this may result in less sales. Setting the sale price below this may result Setting the sale price above this may result in less sales. Setting the sale price below this may result
@ -157,6 +171,12 @@ function ProductComponent(props) {
<button className={"std-button"} onClick={discontinueButtonOnClick}> <button className={"std-button"} onClick={discontinueButtonOnClick}>
Discontinue Discontinue
</button> </button>
{
division.hasResearch("Market-TA.I") &&
<button className={"std-button"} onClick={marketTaButtonOnClick}>
Market-TA
</button>
}
</div> </div>
</div> </div>
) )
@ -167,9 +187,14 @@ function MaterialComponent(props) {
const corp = props.corp; const corp = props.corp;
const division = props.division; const division = props.division;
const warehouse = props.warehouse; const warehouse = props.warehouse;
const city = props.city;
const mat = props.mat; const mat = props.mat;
const eventHandler = props.eventHandler; const eventHandler = props.eventHandler;
const markupLimit = mat.getMarkupLimit(); const markupLimit = mat.getMarkupLimit();
const office = division.offices[city];
if (!(office instanceof OfficeSpace)) {
throw new Error(`Could not get OfficeSpace object for this city (${city})`);
}
// Numeraljs formatter // Numeraljs formatter
const nf = "0.000"; const nf = "0.000";
@ -195,7 +220,7 @@ function MaterialComponent(props) {
// Purchase material button // Purchase material button
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nf)})`; const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nf)})`;
const purchaseButtonClass = tutorial ? "std-button flashing-button tooltip" : "std-button"; const purchaseButtonClass = tutorial ? "std-button flashing-button tooltip" : "std-button";
const purchaseButtonOnClick = eventHandler.createPurchaseMaterialPopup.bind(eventHandler, mat, division); const purchaseButtonOnClick = eventHandler.createPurchaseMaterialPopup.bind(eventHandler, mat, division, warehouse);
// Export material button // Export material button
const exportButtonOnClick = eventHandler.createExportMaterialPopup.bind(eventHandler, mat); const exportButtonOnClick = eventHandler.createExportMaterialPopup.bind(eventHandler, mat);
@ -209,10 +234,12 @@ function MaterialComponent(props) {
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${numeralWrapper.format(mat.sllman[1], nf)})`; sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${numeralWrapper.format(mat.sllman[1], nf)})`;
} }
if (mat.sCost) { if (mat.marketTa2) {
if (mat.marketTa1) { sellButtonText += " @ " + numeralWrapper.formatMoney(mat.marketTa2Price);
sellButtonText += " @ " + numeralWrapper.formatMoney(mat.bCost + markupLimit); } else if (mat.marketTa1) {
} else if (isString(mat.sCost)) { sellButtonText += " @ " + numeralWrapper.formatMoney(mat.bCost + markupLimit);
} else if (mat.sCost) {
if (isString(mat.sCost)) {
var sCost = mat.sCost.replace(/MP/g, mat.bCost); var sCost = mat.sCost.replace(/MP/g, mat.bCost);
sellButtonText += " @ " + numeralWrapper.formatMoney(eval(sCost)); sellButtonText += " @ " + numeralWrapper.formatMoney(eval(sCost));
} else { } else {
@ -225,7 +252,7 @@ function MaterialComponent(props) {
const sellButtonOnClick = eventHandler.createSellMaterialPopup.bind(eventHandler, mat); const sellButtonOnClick = eventHandler.createSellMaterialPopup.bind(eventHandler, mat);
// Market TA button // Market TA button
const marketTaButtonOnClick = eventHandler.createMarketTaPopup.bind(eventHandler, mat, division); const marketTaButtonOnClick = eventHandler.createMaterialMarketTaPopup.bind(eventHandler, mat, division);
return ( return (
<div className={"cmpy-mgmt-warehouse-material-div"} key={props.key}> <div className={"cmpy-mgmt-warehouse-material-div"} key={props.key}>
@ -411,6 +438,7 @@ export class IndustryWarehouse extends BaseReactComponent {
// Only create UI for materials that are relevant for the industry // Only create UI for materials that are relevant for the industry
if (isRelevantMaterial(matName)) { if (isRelevantMaterial(matName)) {
mats.push(MaterialComponent({ mats.push(MaterialComponent({
city: this.props.currentCity,
corp: corp, corp: corp,
division: division, division: division,
eventHandler: this.eventHandler(), eventHandler: this.eventHandler(),

@ -0,0 +1,18 @@
/**
* Utility function that creates a "city map", which is an object where
* each city is a key (property).
*
* This map uses the official name of the city, NOT its key in the 'Cities' object
*/
import { Cities } from "./Cities";
import { IMap } from "../types";
export function createCityMap<T>(initValue: T): IMap<T> {
const map: IMap<any> = {};
const cities = Object.values(Cities);
for (let i = 0; i < cities.length; ++i) {
map[cities[i]] = initValue;
}
return map;
}

@ -105,13 +105,13 @@ export function createSleevePurchaseAugsPopup(sleeve: Sleeve, p: IPlayer) {
innerHTML: innerHTML:
[ [
`<h2>${aug.name}</h2><br>`, `<h2>${aug.name}</h2><br>`,
`Cost: ${numeralWrapper.formatMoney(aug.baseCost)}<br><br>`, `Cost: ${numeralWrapper.formatMoney(aug.startingCost)}<br><br>`,
`${aug.info}` `${aug.info}`
].join(" "), ].join(" "),
padding: "2px", padding: "2px",
clickListener: () => { clickListener: () => {
if (p.canAfford(aug.baseCost)) { if (p.canAfford(aug.startingCost)) {
p.loseMoney(aug.baseCost); p.loseMoney(aug.startingCost);
sleeve.installAugmentation(aug); sleeve.installAugmentation(aug);
dialogBoxCreate(`Installed ${aug.name} on Duplicate Sleeve!`, false) dialogBoxCreate(`Installed ${aug.name} on Duplicate Sleeve!`, false)
removeElementById(popupId); removeElementById(popupId);

@ -2,7 +2,7 @@
"compilerOptions": { "compilerOptions": {
"baseUrl" : ".", "baseUrl" : ".",
"jsx": "react", "jsx": "react",
"lib" : ["es2016", "dom"], "lib" : ["es2016", "dom", "es2017.object"],
"module": "commonjs", "module": "commonjs",
"target": "es6", "target": "es6",
"sourceMap": true, "sourceMap": true,