Added 'Issue New Shares' feature for Corporations. Added cooldowns for issuing new shares and selling shares. Selling shares now dynamically updated stock price

This commit is contained in:
danielyxie 2019-01-08 16:41:42 -08:00
parent ca002e6e53
commit 78f72b8542
10 changed files with 172581 additions and 362 deletions

61079
dist/engine.bundle.js vendored

File diff suppressed because one or more lines are too long

111102
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>Bitburner</title>
<title>Bitburner - development</title>
<link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png">
@ -112,7 +112,7 @@
<div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1"/>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1" />
</div>
<div id="javascript-editor"></div>
@ -162,7 +162,7 @@
<fieldset>
<label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label>
<input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr"/>
<input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr" />
<em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em>
</fieldset>
</div> <!-- End script editor options panel -->
@ -173,7 +173,7 @@
<table id="terminal">
<tr id="terminal-input">
<td id="terminal-input-td" tabindex="2">$
<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" onfocus="this.value = this.value;"/>
<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" onfocus="this.value = this.value;" />
</td>
</tr>
</table>
@ -190,7 +190,7 @@
provides information about each script's production. The scripts are categorized by the hostname of the servers on which
they are running. </p>
<p id="active-scripts-total-prod">Total online production of
Active scripts: <span class="money-gold"><span id="active-scripts-total-production-active">$0.000</span> / sec</span><br/>
Active scripts: <span class="money-gold"><span id="active-scripts-total-production-active">$0.000</span> / sec</span><br />
Total online production since last Aug installation: <span id="active-scripts-total-prod-aug-total" class="money-gold">$0.000</span>
(<span class="money-gold"><span id="active-scripts-total-prod-aug-avg" class="money-gold">$0.000</span> / sec</span>)</p>
<ul class="active-scripts-list" id="active-scripts-list" style="list-style: none;">
@ -204,19 +204,19 @@
The Hacknet is a global, decentralized network of machines. It is used by hackers all around
the world to anonymously share computing power and perform distributed cyberattacks without the
fear of being traced.
<br/><br/>
<br /><br />
Here, you can purchase a Hacknet Node, a specialized machine that can connect and contribute its
resources to the Hacknet network. This allows you to take a small percentage of profits
from hacks performed on the network. Essentially, you are renting out your Node's computing power.
<br/><br/>
<br /><br />
Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node can be upgraded
in order to increase its computing power and thereby increase the profit you earn from it.
</p>
<a id="hacknet-nodes-purchase-button" class="a-link-button"> Purchase Hacknet Node </a>
<br/>
<br />
<div id="hacknet-nodes-money-multipliers-div">
<p id="hacknet-nodes-money">
<span>Money:</span><span id="hacknet-nodes-player-money" class="money-gold"></span><br/>
<span>Money:</span><span id="hacknet-nodes-player-money" class="money-gold"></span><br />
<span>Total Hacknet Node Production:</span><span id="hacknet-nodes-total-production" class="money-gold"></span>
</p>
<span id="hacknet-nodes-multipliers">
@ -474,7 +474,8 @@
<!-- Tutorial content -->
<div id="tutorial-container" class="generic-menupage-container">
<a id="tutorial-getting-started-link" class="a-link-button" href="http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner" target="_blank"> Getting Started </a>
<a id="tutorial-getting-started-link" class="a-link-button"
href="http://bitburner.wikia.com/wiki/Chapt3rs_Guide_to_Getting_Started_with_Bitburner" target="_blank"> Getting Started </a>
<a id="tutorial-networking-link" class="a-link-button"> Servers & Networking </a>
<a id="tutorial-hacking-link" class="a-link-button"> Hacking </a>
<a id="tutorial-scripts-link" class="a-link-button"> Scripts </a>
@ -483,7 +484,8 @@
<a id="tutorial-jobs-link" class="a-link-button"> Companies and Infiltration </a>
<a id="tutorial-factions-link" class="a-link-button"> Factions </a>
<a id="tutorial-augmentations-link" class="a-link-button"> Augmentations </a>
<a id="tutorial-shortcuts-link" class="a-link-button" href="https://bitburner.wikia.com/wiki/Shortcuts" target="_blank"> Keyboard Shortcuts </a>
<a id="tutorial-shortcuts-link" class="a-link-button"
href="https://bitburner.wikia.com/wiki/Shortcuts" target="_blank"> Keyboard Shortcuts </a>
<a id="tutorial-back-button" class="a-link-button"> Back </a>
<p id="tutorial-text"> </p>
@ -574,7 +576,7 @@
<p id="location-slums-description">
You have entered the Slums, a poverty-ridden district filled with gangs, criminals, and
other shadowy entities. The city's government and police have neglected this area for years...
<br/><br/><br/>
<br /><br /><br />
In the Slums, you can commit crimes to earn money and experience. Crime attempts are not always
successful. Your chance at successfully committing a crime is determined by your stats.
</p>
@ -621,7 +623,7 @@
<div id="stock-market-container" class="generic-menupage-container">
<p>
Welcome to the World Stock Exchange (WSE)! <br/><br/>
Welcome to the World Stock Exchange (WSE)! <br /><br />
To begin trading, you must first purchase an account. WSE accounts will persist
after you 'reset' by installing Augmentations.
@ -634,7 +636,7 @@
TIX, short for Trade Information eXchange, is the communications protocol supported by the WSE.
Purchasing access to the TIX API lets you write code to create your own algorithmic/automated
trading strategies.
<br/><br/>
<br /><br />
If you purchase access to the TIX API, you will retain that access even after
you 'reset' by installing Augmentations.
</p>
@ -644,7 +646,7 @@
<p>
Four Sigma's (4S) Market Data Feed provides information about stocks
that will help your trading strategies.
<br/><br/>
<br /><br />
If you purchase access to 4S Market Data and/or the 4S TIX API, you will
retain that access even after you 'reset' by installing Augmentations.
</p>
@ -662,7 +664,7 @@
<a id="stock-market-mode" class="a-link-button tooltip"></a>
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
<a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a>
<br/><br/>
<br /><br />
<input id="stock-market-watchlist-filter" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"/>
<a id="stock-market-watchlist-filter-update" class="a-link-button"> Update Watchlist </a>
<ul id="stock-market-list" style="list-style:none;">
@ -692,7 +694,7 @@
<div id="yes-no-text-input-box-container" class="popup-box-container">
<div id="yes-no-text-input-box-content" class="popup-box-content">
<p id="yes-no-text-input-box-text"> </p>
<input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30"/>
<input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30" />
<button id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </button>
<button id="yes-no-text-input-box-no" class="popup-box-button"> No </button>
</div>
@ -704,7 +706,7 @@
<p id="faction-invitation-box-text"> </p>
<p id="faction-invitation-box-message"> </p>
<p id="faction-invitation-box-warning">
Would you like to join? <br/> <br/>
Would you like to join? <br /> <br />
Warning: Joining this faction may prevent you from joining other factions during this run!
</p>
<button id="faction-invitation-box-yes" class="popup-box-button"> Yes </button>
@ -717,8 +719,8 @@
<div id="infiltration-box-content" class="popup-box-content">
<p id="infiltration-box-text"> </p>
<button id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </button> <br/><br/>
<select id="infiltration-faction-select"> </select> <br/>
<button id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </button> <br /><br />
<select id="infiltration-faction-select"> </select> <br />
<button id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </button>
</div>
@ -804,7 +806,7 @@
<div id="game-options-content" class="game-options-box">
<button id="game-options-close-button">&times;</button>
<h1> Game Options </h1>
<br/>
<br />
<div id="game-options-left-panel">
<!-- Netscript execution time -->
<fieldset>
@ -816,7 +818,7 @@
</span>
</label>
<input type="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25"/>
<input type ="range" max="100" min="10" step="1" name="settingsNSExecTimeRangeVal" id="settingsNSExecTimeRangeVal" value="25" />
<em id="settingsNSExecTimeRangeValLabel" style="font-style: normal;"></em>
</fieldset>
@ -830,7 +832,7 @@
</span>
</label>
<input type="range" max="100" min="20" step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50"/>
<input type="range" max="100" min="20" step="1" name="settingsNSLogRangeVal" id="settingsNSLogRangeVal" value="50" />
<em id="settingsNSLogRangeValLabel" style="font-style: normal;"></em>
</fieldset>
@ -844,7 +846,7 @@
</span>
</label>
<input type="range" max="100" min="20" step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50"/>
<input type="range" max="100" min="20" step="1" name="settingsNSPortRangeVal" id="settingsNSPortRangeVal" value="50" />
<em id="settingsNSPortRangeValLabel" style="font-style: normal;"></em>
</fieldset>
@ -856,7 +858,7 @@
</span>
</label>
<input type="range" max="600" min="0" step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60"/>
<input type="range" max="600" min="0" step="1" name="settingsAutosaveIntervalVal" id="settingsAutosaveIntervalVal" value="60" />
<em id="settingsAutosaveIntervalValLabel" style="font-style: normal;"></em>
</fieldset>
@ -968,7 +970,7 @@
<button id="save-game-link" class="a-link-button"> Save Game </button>
<button id="delete-game-link" class="a-link-button"> Delete Game </button>
<button id="export-game-link" class="a-link-button"> Export Game </button>
<input type="file" id="import-game-file-selector" name="file"/>
<input type="file" id="import-game-file-selector" name="file" />
<button id="import-game-link" class="a-link-button"> Import Game </button>
<button id="copy-save-to-clipboard-link" class="std-button">
Copy Save data to Clipboard

@ -508,6 +508,8 @@ export let CONSTANTS: IMap<any> = {
`
v0.42.0
* Corporation Changes:
** Corporation can now be self-funded with $150b or using seed money in exchange for 500m newly-issued shares
** In BitNode-3, you no longer start with $150b
** Changed initial market prices for many materials
** Changed the way a material's demand, competition, and market price change over time
** The sale price of materials can no longer be marked-up as high
@ -519,6 +521,13 @@ export let CONSTANTS: IMap<any> = {
** Employee salaries now slowly increase over time
** Slightly reduced the effect "Real Estate" has on the Production Multiplier for the Agriculture industry
** Changed the way your Corporation's value is calculated (this is what determines stock price)
** After taking your corporation public, it is now possible to issue new shares to raise capital
** Issuing new shares can only be done once every 12 hours
** Buying back shares must now be done at a premium
** Selling shares can now only be done once per hour
** Selling large amounts of shares now immediately impacts stock price (during the transaction)
** Reduced the initial cost of the DreamSense upgrade from $8b to $4b, but increased its price multiplier
** Reduced the price multiplier for ABC SalesBots upgrade
* Added getOrders() Netscript function to the TIX API
* Added getAugmentationPrereq() Singularity function (by havocmayhem)

@ -35,6 +35,7 @@ import { createPopupCloseButton } from "../../utils/uiHelp
import { formatNumber, generateRandomString } from "../../utils/StringHelperFunctions";
import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { isString } from "../../utils/helpers/isString";
import { KEY } from "../../utils/helpers/keyCodes";
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
import { removeElement } from "../../utils/uiHelpers/removeElement";
import { removeElementById } from "../../utils/uiHelpers/removeElementById";
@ -52,11 +53,17 @@ import { yesNoBoxCreate,
import Decimal from "decimal.js";
/* Constants */
export const TOTALSHARES = 1e9; //Total number of shares you have at your company
export const INITIALSHARES = 1e9; //Total number of shares you have at your company
export const SHARESPERPRICEUPDATE = 1e6; //When selling large number of shares, price is dynamically updated for every batch of this amount
export const IssueNewSharesCooldown = 216e3; // 12 Hour in terms of game cycles
export const SellSharesCooldown = 18e3; // 1 Hour in terms of game cycles
export const CyclesPerMarketCycle = 75;
export const CyclesPerIndustryStateCycle = CyclesPerMarketCycle / AllCorporationStates.length;
export const SecsPerMarketCycle = CyclesPerMarketCycle / 5;
export const Cities = ["Aevum", "Chongqing", "Sector-12", "New Tokyo", "Ishima", "Volhaven"];
export const WarehouseInitialCost = 5e9; //Initial purchase cost of warehouse
export const WarehouseInitialSize = 100;
export const WarehouseUpgradeBaseCost = 1e9;
@ -320,7 +327,7 @@ Industry.prototype.init = function() {
this.advFac = 0.16;
this.hwFac = 0.25;
this.reFac = 0.1;
this.aiFac = 0.1;
this.aiFac = 0.15;
this.robFac = 0.05;
this.reqMats = {
"Hardware": 0.5,
@ -1131,7 +1138,7 @@ Industry.prototype.getOfficeProductivity = function(office, params) {
ratio = Math.max(0.01, ratio); //Minimum ratio value if you have employees
}
if (params && params.forProduct) {
return ratio * Math.pow(total, 0.2);
return ratio * Math.pow(total, 0.22);
} else {
return 2 * ratio * Math.pow(total, 0.3);
}
@ -2129,7 +2136,7 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
value: mat.buy ? mat.buy : null,
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
}
});
confirmBtn = createElement("button", {
@ -2338,7 +2345,7 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
value: mat.sllman[1] ? mat.sllman[1] : null, placeholder: "Sell amount",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
}
});
var inputPx = createElement("input", {
@ -2346,7 +2353,7 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
value: mat.sCost ? mat.sCost : null, placeholder: "Sell price",
onkeyup: (e) => {
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
}
});
confirmBtn = createElement("button", {
@ -2584,14 +2591,14 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
type:"text", value:product.sllman[city][1] ? product.sllman[city][1] : null, placeholder: "Sell amount",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
}
});
var inputPx = createElement("input", {
type:"text", value: product.sCost ? product.sCost : null, placeholder: "Sell price",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
}
});
confirmBtn = createElement("a", {
@ -2693,7 +2700,7 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
type:"number", placeholder:"Limit",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
}
});
confirmBtn = createElement("a", {
@ -2784,7 +2791,11 @@ function Corporation(params={}) {
this.expenses = new Decimal(0);
this.fundingRound = 0;
this.public = false; //Publicly traded
this.numShares = TOTALSHARES;
this.totalShares = INITIALSHARES; // Total existing shares
this.numShares = INITIALSHARES; // Total shares owned by player
this.shareSalesUntilPriceUpdate = SHARESPERPRICEUPDATE;
this.shareSaleCooldown = 0; // Game cycles until player can sell shares again
this.issueNewSharesCooldown = 0; // Game cycles until player can issue shares again
this.dividendPercentage = 0;
this.dividendTaxPercentage = 50;
this.issuedShares = 0;
@ -2814,12 +2825,21 @@ Corporation.prototype.process = function() {
if (this.storedCycles >= CyclesPerIndustryStateCycle) {
const state = this.getState();
const marketCycles = 1;
this.storedCycles -= (marketCycles * CyclesPerIndustryStateCycle);
const gameCycles = (marketCycles * CyclesPerIndustryStateCycle);
this.storedCycles -= gameCycles;
this.divisions.forEach(function(ind) {
ind.process(marketCycles, state, corp);
});
// Process cooldowns
if (this.shareSaleCooldown > 0) {
this.shareSaleCooldown -= gameCycles;
}
if (this.issueNewSharesCooldown > 0) {
this.issueNewSharesCooldown -= gameCycles;
}
//At the start of a new cycle, calculate profits from previous cycle
if (state === "START") {
this.revenue = new Decimal(0);
@ -2847,7 +2867,7 @@ Corporation.prototype.process = function() {
} else {
const totalDividends = (this.dividendPercentage / 100) * cycleProfit;
const retainedEarnings = cycleProfit - totalDividends;
const dividendsPerShare = totalDividends / TOTALSHARES;
const dividendsPerShare = totalDividends / this.totalShares;
Player.gainMoney(this.numShares * dividendsPerShare * (this.dividendTaxPercentage / 100));
this.funds = this.funds.plus(retainedEarnings);
}
@ -2878,7 +2898,7 @@ Corporation.prototype.determineValuation = function() {
} else {
val = 10e9 + Math.max(this.funds.toNumber(), 0) / 3; //Base valuation
if (profit > 0) {
val += (profit * 300e3);
val += (profit * 315e3);
val *= (Math.pow(1.1, this.divisions.length));
} else {
val = 10e9 * Math.pow(1.1, this.divisions.length);
@ -2890,24 +2910,29 @@ Corporation.prototype.determineValuation = function() {
Corporation.prototype.getInvestment = function() {
var val = this.determineValuation(), percShares;
let roundMultiplier = 4;
switch (this.fundingRound) {
case 0: //Seed
percShares = 0.10;
roundMultiplier = 5;
break;
case 1: //Series A
percShares = 0.35;
roundMultiplier = 4;
break;
case 2: //Series B
percShares = 0.25;
roundMultiplier = 4;
break;
case 3: //Series C
percShares = 0.20;
roundMultiplier = 3.5;
break;
case 4:
return;
}
var funding = val * percShares * 4,
investShares = Math.floor(TOTALSHARES * percShares),
var funding = val * percShares * roundMultiplier,
investShares = Math.floor(INITIALSHARES * percShares),
yesBtn = yesNoBoxGetYesButton(),
noBtn = yesNoBoxGetNoButton();
yesBtn.innerHTML = "Accept";
@ -2931,7 +2956,7 @@ Corporation.prototype.getInvestment = function() {
Corporation.prototype.goPublic = function() {
var goPublicPopupId = "cmpy-mgmt-go-public-popup";
var initialSharePrice = this.determineValuation() / (TOTALSHARES);
var initialSharePrice = this.determineValuation() / (this.totalShares);
var txt = createElement("p", {
innerHTML: "Enter the number of shares you would like to issue " +
"for your IPO. These shares will be publicly sold " +
@ -2946,7 +2971,7 @@ Corporation.prototype.goPublic = function() {
placeholder: "Shares to issue",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {yesBtn.click();}
if (e.keyCode === KEY.ENTER) {yesBtn.click();}
}
});
var br = createElement("br", {});
@ -2955,7 +2980,7 @@ Corporation.prototype.goPublic = function() {
innerText:"Go Public",
clickListener:()=>{
var numShares = Math.round(input.value);
var initialSharePrice = this.determineValuation() / (TOTALSHARES);
var initialSharePrice = this.determineValuation() / (this.totalShares);
if (isNaN(numShares)) {
dialogBoxCreate("Invalid value for number of issued shares");
return false;
@ -2971,6 +2996,8 @@ Corporation.prototype.goPublic = function() {
this.funds = this.funds.plus(numShares * initialSharePrice);
this.displayCorporationOverviewContent();
removeElementById(goPublicPopupId);
dialogBoxCreate(`You took your ${this.name} public and earned ` +
`${numeralWrapper.formatMoney(numShares * initialSharePrice)} in your IPO`);
return false;
}
});
@ -2985,8 +3012,14 @@ Corporation.prototype.goPublic = function() {
createPopup(goPublicPopupId, [txt, br, input, yesBtn, noBtn]);
}
Corporation.prototype.getTargetSharePrice = function() {
// Note: totalShares - numShares is not the same as issuedShares because
// issuedShares does not account for private investors
return this.determineValuation() / (2 * (this.totalShares - this.numShares) + 1);
}
Corporation.prototype.updateSharePrice = function() {
var targetPrice = this.determineValuation() / (1.5 * TOTALSHARES - this.numShares);
const targetPrice = this.getTargetSharePrice();
if (this.sharePrice <= targetPrice) {
this.sharePrice *= (1 + (Math.random() * 0.01));
} else {
@ -2995,6 +3028,62 @@ Corporation.prototype.updateSharePrice = function() {
if (this.sharePrice <= 0.01) {this.sharePrice = 0.01;}
}
Corporation.prototype.immediatelyUpdateSharePrice = function() {
this.sharePrice = this.getTargetSharePrice();
}
// Calculates how much money will be made and what the resulting stock price
// will be when the player sells his/her shares
// @return - [Player profit, final stock price, end shareSalesUntilPriceUpdate property]
Corporation.prototype.calculateShareSale = function(numShares) {
let sharesTracker = numShares;
let sharesUntilUpdate = this.shareSalesUntilPriceUpdate;
let sharePrice = this.sharePrice;
let sharesSold = 0;
let profit = 0;
const maxIterations = Math.ceil(numShares / SHARESPERPRICEUPDATE);
if (isNaN(maxIterations) || maxIterations > 10e6) {
console.error(`Something went wrong or unexpected when calculating share sale. Maxiterations calculated to be ${maxIterations}`);
return;
}
for (let i = 0; i < maxIterations; ++i) {
if (sharesTracker < sharesUntilUpdate) {
profit += (sharePrice * sharesTracker);
sharesUntilUpdate -= sharesTracker;
break;
} else {
profit += (sharePrice * sharesUntilUpdate);
sharesUntilUpdate = SHARESPERPRICEUPDATE;
sharesTracker -= sharesUntilUpdate;
sharesSold += sharesUntilUpdate;
// Calculate what new share price would be
sharePrice = this.determineValuation() / (2 * (this.totalShares + sharesSold - this.numShares));
}
}
return [profit, sharePrice, sharesUntilUpdate];
}
Corporation.prototype.convertCooldownToString = function(cd) {
// The cooldown value is based on game cycles. Convert to a simple string
const CyclesPerSecond = 1000 / CONSTANTS.MilliPerCycle;
const seconds = cd / 5;
const SecondsPerMinute = 60;
const SecondsPerHour = 3600;
if (seconds > SecondsPerHour) {
return `${Math.floor(seconds / SecondsPerHour)} hour(s)`;
} else if (seconds > SecondsPerMinute) {
return `${Math.floor(seconds / SecondsPerMinute)} minute(s)`;
} else {
return `${Math.floor(seconds)} second(s)`;
}
}
//One time upgrades that unlock new features
Corporation.prototype.unlock = function(upgrade) {
const upgN = upgrade[0], price = upgrade[1];
@ -3104,6 +3193,9 @@ var companyManagementDiv, companyManagementHeaderTabs, companyManagementPanel,
currentCityUi,
corporationUnlockUpgrades, corporationUpgrades,
sellSharesButton, sellSharesButtonTooltip,
issueNewSharesButton, issueNewSharesButtonTooltip,
//Industry Overview Panel
industryOverviewPanel, industryOverviewText,
@ -3200,7 +3292,7 @@ Corporation.prototype.updateUIHeaderTabs = function() {
pattern:"[a-zA-Z0-9-_]",
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {yesBtn.click();}
if (e.keyCode === KEY.ENTER) {yesBtn.click();}
}
});
var nameLabel = createElement("label", {
@ -3379,16 +3471,16 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
if (this.public) {
//Sell share buttons
var sellShares = createElement("a", {
class:"a-link-button", innerText:"Sell Shares", display:"inline-block",
tooltip: "Sell your shares in the company. The money earned from selling your " +
"shares goes into your personal account, not the Corporation's. " +
"This is one of the only ways to profit from your business venture.",
clickListener:()=>{
class:"a-link-button tooltip", innerText:"Sell Shares", display:"inline-block",
clickListener: () => {
var popupId = "cmpy-mgmt-sell-shares-popup";
var currentStockPrice = this.sharePrice;
var txt = createElement("p", {
innerHTML: "Enter the number of shares you would like to sell. The money from " +
"selling your shares will go directly to you (NOT your Corporation). " +
"selling your shares will go directly to you (NOT your Corporation).<br><br>" +
"Selling your shares will cause your corporation's stock price to fall due to " +
"dilution. Furthermore, selling a large number of shares all at once will have an immediate effect " +
"in reducing your stock price.<br><br>" +
"The current price of your " +
"company's stock is " + numeralWrapper.format(currentStockPrice, "$0.000a"),
});
@ -3402,8 +3494,12 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
} else if (numShares > this.numShares) {
profitIndicator.innerText = "You don't have this many shares to sell!";
} else {
const stockSaleResults = this.calculateShareSale(numShares);
const profit = stockSaleResults[0];
const newSharePrice = stockSaleResults[1];
const newSharesUntilUpdate = stockSaleResults[2];
profitIndicator.innerText = "Sell " + numShares + " shares for a total of " +
numeralWrapper.format(numShares * currentStockPrice, '$0.000a');
numeralWrapper.format(profit, '$0.000a');
}
}
});
@ -3416,6 +3512,11 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
} else if (shares > this.numShares) {
dialogBoxCreate("ERROR: You don't have this many shares to sell");
} else {
const stockSaleResults = this.calculateShareSale(shares);
const profit = stockSaleResults[0];
const newSharePrice = stockSaleResults[1];
const newSharesUntilUpdate = stockSaleResults[2];
this.numShares -= shares;
if (isNaN(this.issuedShares)) {
console.log("ERROR: Corporation issuedShares is NaN: " + this.issuedShares);
@ -3428,8 +3529,15 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
}
}
this.issuedShares += shares;
Player.gainMoney(shares * this.sharePrice);
this.sharePrice = newSharePrice;
this.shareSalesUntilPriceUpdate = newSharesUntilUpdate;
this.shareSaleCooldown = SellSharesCooldown;
Player.gainMoney(profit);
removeElementById(popupId);
dialogBoxCreate(`Sold ${numeralWrapper.formatMoney(shares, "0.000a")} shares for ` +
`${numeralWrapper.formatMoney(profit, "$0.000a")}. ` +
`The corporation's stock price fell to ${numeralWrapper.formatMoney(this.sharePrice)} ` +
`as a result of dilution.`);
return false;
}
@ -3446,18 +3554,29 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
}
});
sellSharesButtonTooltip = createElement("span", {
class: "tooltiptext",
innerText: "Sell your shares in the company. The money earned from selling your " +
"shares goes into your personal account, not the Corporation's. " +
"This is one of the only ways to profit from your business venture.",
});
sellShares.appendChild(sellSharesButtonTooltip);
//Buyback shares button
var buybackShares = createElement("a", {
class:"a-link-button", innerText:"Buyback shares", display:"inline-block",
tooltip:"Buy back shares you that previously issued or sold at market price.",
clickListener:()=>{
var popupId = "cmpy-mgmt-buyback-shares-popup";
var currentStockPrice = this.sharePrice;
const currentStockPrice = this.sharePrice;
const buybackPrice = currentStockPrice * 1.1;
var txt = createElement("p", {
innerHTML: "Enter the number of shares you would like to buy back at market price. To purchase " +
"these shares, you must use your own money (NOT your Corporation's funds). " +
"The current price of your " +
"company's stock is " + numeralWrapper.format(currentStockPrice, "$0.000a") +
innerHTML: "Enter the number of outstanding shares you would like to buy back. " +
"These shares must be bought at a 10% premium. However, " +
"repurchasing shares from the market tends to lead to an increase in stock price.<br><bR>" +
"To purchase these shares, you must use your own money (NOT your Corporation's funds).<br><br>" +
"The current buyback price of your company's stock is " +
numeralWrapper.format(buybackPrice, "$0.000a") +
". Your company currently has " + formatNumber(this.issuedShares, 3) + " outstanding stock shares",
});
var costIndicator = createElement("p", {});
@ -3472,9 +3591,8 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
costIndicator.innerText = "There are not this many shares available to buy back. " +
"There are only " + this.issuedShares + " outstanding shares.";
} else {
console.log("here");
costIndicator.innerText = "Purchase " + numShares + " shares for a total of " +
numeralWrapper.format(numShares * currentStockPrice, '$0.000a');
numeralWrapper.format(numShares * buybackPrice, '$0.000a');
}
}
});
@ -3482,14 +3600,15 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
class:"a-link-button", innerText:"Buy shares", display:"inline-block",
clickListener:()=>{
var shares = Math.round(input.value);
var tempStockPrice = this.sharePrice;
const tempStockPrice = this.sharePrice;
const buybackPrice = tempStockPrice * 1.1;
if (isNaN(shares) || shares <= 0) {
dialogBoxCreate("ERROR: Invalid value for number of shares");
} else if (shares > this.issuedShares) {
dialogBoxCreate("ERROR: There are not this many oustanding shares to buy back");
} else if (shares * tempStockPrice > Player.money) {
} else if (shares * buybackPrice > Player.money) {
dialogBoxCreate("ERROR: You do not have enough money to purchase this many shares (you need " +
numeralWrapper.format(shares * tempStockPrice, "$0.000a") + ")");
numeralWrapper.format(shares * buybackPrice, "$0.000a") + ")");
} else {
this.numShares += shares;
if (isNaN(this.issuedShares)) {
@ -3503,8 +3622,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
}
}
this.issuedShares -= shares;
Player.loseMoney(shares * tempStockPrice);
//TODO REMOVE from Player money
Player.loseMoney(shares * buybackPrice);
removeElementById(popupId);
}
return false;
@ -3527,121 +3645,142 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
companyManagementPanel.appendChild(sellShares);
companyManagementPanel.appendChild(buybackShares);
//If your Corporation is big enough, buy faction influence through bribes
var canBribe = this.determineValuation() >= BribeThreshold;
var bribeFactions = createElement("a", {
class: canBribe ? "a-link-button" : "a-link-button-inactive",
innerText:"Bribe Factions", display:"inline-block",
tooltip:canBribe
? "Use your Corporations power and influence to bribe Faction leaders in exchange for reputation"
: "Your Corporation is not powerful enough to bribe Faction leaders",
clickListener:()=>{
var popupId = "cmpy-mgmt-bribe-factions-popup";
var txt = createElement("p", {
innerText:"You can use Corporation funds or stock shares to bribe Faction Leaders in exchange for faction reputation"
});
var factionSelector = createElement("select", {margin:"3px"});
for (var i = 0; i < Player.factions.length; ++i) {
var facName = Player.factions[i];
factionSelector.add(createElement("option", {
text:facName, value:facName
}));
}
var repGainText = createElement("p");
var stockSharesInput;
var moneyInput = createElement("input", {
type:"number", placeholder:"Corporation funds", margin:"5px",
inputListener:()=>{
var money = moneyInput.value == null || moneyInput.value == "" ? 0 : parseFloat(moneyInput.value);
var stockPrice = this.sharePrice;
var stockShares = stockSharesInput.value == null || stockSharesInput.value == "" ? 0 : Math.round(parseFloat(stockSharesInput.value));
if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) {
repGainText.innerText = "ERROR: Invalid value(s) entered";
} else if (this.funds.lt(money)) {
repGainText.innerText = "ERROR: You do not have this much money to bribe with";
} else if (this.stockShares > this.numShares) {
repGainText.innerText = "ERROR: You do not have this many shares to bribe with";
} else {
sellSharesButton = sellShares;
var totalAmount = Number(money) + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio;
repGainText.innerText = "You will gain " + formatNumber(repGain, 0) +
" reputation with " +
factionSelector.options[factionSelector.selectedIndex].value +
" with this bribe";
}
}
// Issue new Shares
appendLineBreaks(companyManagementPanel, 1);
const issueNewShares = createElement("a", {
class: "std-button tooltip",
display: "inline-block",
innerText: "Issue New Shares",
clickListener: () => {
const popupId = "cmpy-mgmt-issue-new-shares-popup";
const maxNewSharesUnrounded = Math.round(this.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>" +
`&nbsp;* You can issue at most ${numeralWrapper.format(maxNewShares, "0.000a")} new shares<br>` +
`&nbsp;* New shares are sold at a 10% discount<br>` +
`&nbsp;* You can only issue new shares once every 12 hours<br>` +
`&nbsp;* Issuing new shares causes dilution, resulting in a decrease in stock price and lower dividends per share<br>` +
`&nbsp;* 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.`,
});
stockSharesInput = createElement("input", {
type:"number", placeholder:"Stock Shares", margin: "5px",
inputListener:()=>{
var money = moneyInput.value == null || moneyInput.value == "" ? 0 : parseFloat(moneyInput.value);
var stockPrice = this.sharePrice;
var stockShares = stockSharesInput.value == null || stockSharesInput.value == "" ? 0 : Math.round(stockSharesInput.value);
if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) {
repGainText.innerText = "ERROR: Invalid value(s) entered";
} else if (this.funds.lt(money)) {
repGainText.innerText = "ERROR: You do not have this much money to bribe with";
} else if (this.stockShares > this.numShares) {
repGainText.innerText = "ERROR: You do not have this many shares to bribe with";
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 {
var totalAmount = money + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio;
console.log("repGain: " + repGain);
repGainText.innerText = "You will gain " + formatNumber(repGain, 0) +
" reputation with " +
factionSelector.options[factionSelector.selectedIndex].value +
" with this bribe";
updateDynamicText(this);
}
}
});
var confirmButton = createElement("a", {
class:"a-link-button", innerText:"Bribe", display:"inline-block",
clickListener:()=>{
var money = moneyInput.value == null || moneyInput.value == "" ? 0 : parseFloat(moneyInput.value);
var stockPrice = this.sharePrice;
var stockShares = stockSharesInput.value == null || stockSharesInput.value == ""? 0 : Math.round(parseFloat(stockSharesInput.value));
var fac = Factions[factionSelector.options[factionSelector.selectedIndex].value];
if (fac == null) {
dialogBoxCreate("ERROR: You must select a faction to bribe");
return false;
issueBtn = createElement("a", {
class: "std-button",
display: "inline-block",
innerText: "Issue New Shares",
clickListener: () => {
const newSharePrice = Math.round(this.sharePrice * 0.9);
let newShares = parseInt(newSharesInput.value);
if (isNaN(newShares)) {
dialogBoxCreate("Invalid input for number of new shares");
return;
}
if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) {
dialogBoxCreate("ERROR: Invalid value(s) entered");
} else if (this.funds.lt(money)) {
dialogBoxCreate("ERROR: You do not have this much money to bribe with");
} else if (stockShares > this.numShares) {
dialogBoxCreate("ERROR: You do not have this many shares to bribe with");
} else {
var totalAmount = money + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio;
dialogBoxCreate("You gained " + formatNumber(repGain, 0) +
" reputation with " + fac.name + " by bribing them.");
fac.playerReputation += repGain;
this.funds = this.funds.minus(money);
this.numShares -= stockShares;
removeElementById(popupId);
return false;
// 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;
}
}
});
var cancelButton = createElement("a", {
class:"a-link-button", innerText:"Cancel", display:"inline-block",
clickListener:()=>{
const profit = newShares * newSharePrice;
this.issueNewSharesCooldown = IssueNewSharesCooldown;
this.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.issuedShares += (newShares - privateShares);
this.funds = this.funds.plus(profit);
this.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.sharePrice)}`);
return false;
}
});
createPopup(popupId, [txt, factionSelector, repGainText,
moneyInput, stockSharesInput, confirmButton, cancelButton]);
const cancelBtn = createPopupCloseButton(popupId, {
class: "std-button",
display: "inline-block",
innerText: "Cancel",
});
createPopup(popupId, [descText, dynamicText, newSharesInput, issueBtn, cancelBtn]);
newSharesInput.focus();
}
});
companyManagementPanel.appendChild(bribeFactions);
issueNewSharesButtonTooltip = createElement("span", {
class: "tooltiptext",
innerText: "Issue new equity shares to raise capital",
});
issueNewShares.appendChild(issueNewSharesButtonTooltip);
companyManagementPanel.appendChild(issueNewShares);
issueNewSharesButton = issueNewShares;
// Set Stock Dividends
const issueDividends = createElement("a", {
class: "a-link-button",
class: "std-button",
display: "inline-block",
innerText: "Issue Dividends",
tooltip: "Manage the dividends that are paid out to shareholders (including yourself)",
@ -3670,7 +3809,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
type: "number",
onkeyup: (e) => {
e.preventDefault();
if (e.keyCode === 13) {allocateBtn.click();}
if (e.keyCode === KEY.ENTER) {allocateBtn.click();}
}
});
@ -3741,6 +3880,120 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
companyManagementPanel.appendChild(goPublic);
}
appendLineBreaks(companyManagementPanel, 1);
//If your Corporation is big enough, buy faction influence through bribes
var canBribe = this.determineValuation() >= BribeThreshold;
var bribeFactions = createElement("a", {
class: canBribe ? "a-link-button" : "a-link-button-inactive",
innerText:"Bribe Factions", display:"inline-block",
tooltip:canBribe
? "Use your Corporations power and influence to bribe Faction leaders in exchange for reputation"
: "Your Corporation is not powerful enough to bribe Faction leaders",
clickListener:()=>{
var popupId = "cmpy-mgmt-bribe-factions-popup";
var txt = createElement("p", {
innerText:"You can use Corporation funds or stock shares to bribe Faction Leaders in exchange for faction reputation"
});
var factionSelector = createElement("select", {margin:"3px"});
for (var i = 0; i < Player.factions.length; ++i) {
var facName = Player.factions[i];
factionSelector.add(createElement("option", {
text:facName, value:facName
}));
}
var repGainText = createElement("p");
var stockSharesInput;
var moneyInput = createElement("input", {
type:"number", placeholder:"Corporation funds", margin:"5px",
inputListener:()=>{
var money = moneyInput.value == null || moneyInput.value == "" ? 0 : parseFloat(moneyInput.value);
var stockPrice = this.sharePrice;
var stockShares = stockSharesInput.value == null || stockSharesInput.value == "" ? 0 : Math.round(parseFloat(stockSharesInput.value));
if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) {
repGainText.innerText = "ERROR: Invalid value(s) entered";
} else if (this.funds.lt(money)) {
repGainText.innerText = "ERROR: You do not have this much money to bribe with";
} else if (this.stockShares > this.numShares) {
repGainText.innerText = "ERROR: You do not have this many shares to bribe with";
} else {
var totalAmount = Number(money) + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio;
repGainText.innerText = "You will gain " + formatNumber(repGain, 0) +
" reputation with " +
factionSelector.options[factionSelector.selectedIndex].value +
" with this bribe";
}
}
});
stockSharesInput = createElement("input", {
type:"number", placeholder:"Stock Shares", margin: "5px",
inputListener:()=>{
var money = moneyInput.value == null || moneyInput.value == "" ? 0 : parseFloat(moneyInput.value);
var stockPrice = this.sharePrice;
var stockShares = stockSharesInput.value == null || stockSharesInput.value == "" ? 0 : Math.round(stockSharesInput.value);
if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) {
repGainText.innerText = "ERROR: Invalid value(s) entered";
} else if (this.funds.lt(money)) {
repGainText.innerText = "ERROR: You do not have this much money to bribe with";
} else if (this.stockShares > this.numShares) {
repGainText.innerText = "ERROR: You do not have this many shares to bribe with";
} else {
var totalAmount = money + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio;
console.log("repGain: " + repGain);
repGainText.innerText = "You will gain " + formatNumber(repGain, 0) +
" reputation with " +
factionSelector.options[factionSelector.selectedIndex].value +
" with this bribe";
}
}
});
var confirmButton = createElement("a", {
class:"a-link-button", innerText:"Bribe", display:"inline-block",
clickListener:()=>{
var money = moneyInput.value == null || moneyInput.value == "" ? 0 : parseFloat(moneyInput.value);
var stockPrice = this.sharePrice;
var stockShares = stockSharesInput.value == null || stockSharesInput.value == ""? 0 : Math.round(parseFloat(stockSharesInput.value));
var fac = Factions[factionSelector.options[factionSelector.selectedIndex].value];
if (fac == null) {
dialogBoxCreate("ERROR: You must select a faction to bribe");
return false;
}
if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) {
dialogBoxCreate("ERROR: Invalid value(s) entered");
} else if (this.funds.lt(money)) {
dialogBoxCreate("ERROR: You do not have this much money to bribe with");
} else if (stockShares > this.numShares) {
dialogBoxCreate("ERROR: You do not have this many shares to bribe with");
} else {
var totalAmount = money + (stockShares * stockPrice);
var repGain = totalAmount / BribeToRepRatio;
dialogBoxCreate("You gained " + formatNumber(repGain, 0) +
" reputation with " + fac.name + " by bribing them.");
fac.playerReputation += repGain;
this.funds = this.funds.minus(money);
this.numShares -= stockShares;
removeElementById(popupId);
return false;
}
}
});
var cancelButton = createElement("a", {
class:"a-link-button", innerText:"Cancel", display:"inline-block",
clickListener:()=>{
removeElementById(popupId);
return false;
}
});
createPopup(popupId, [txt, factionSelector, repGainText,
moneyInput, stockSharesInput, confirmButton, cancelButton]);
}
});
companyManagementPanel.appendChild(bribeFactions);
//Update overview text
this.updateCorporationOverviewContent();
@ -3842,15 +4095,15 @@ Corporation.prototype.updateCorporationOverviewContent = function() {
if (this.dividendPercentage > 0 && profit > 0) {
const totalDividends = (this.dividendPercentage / 100) * profit;
const retainedEarnings = profit - totalDividends;
const dividendsPerShare = totalDividends / TOTALSHARES;
const dividendsPerShare = totalDividends / this.totalShares;
const playerEarnings = this.numShares * dividendsPerShare;
dividendStr = `Dividend Percentage: ${numeralWrapper.format(this.dividendPercentage / 100, "0%")}<br>` +
`Retained Profits (after dividends): ${numeralWrapper.format(retainedEarnings, "$0.000a")} / s<br>` +
dividendStr = `Retained Profits (after dividends): ${numeralWrapper.format(retainedEarnings, "$0.000a")} / s<br><br>` +
`Dividend Percentage: ${numeralWrapper.format(this.dividendPercentage / 100, "0%")}<br>` +
`Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s<br>` +
`Your earnings as a shareholder (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s<br>` +
`Dividend Tax Rate: ${this.dividendTaxPercentage}%<br>` +
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (this.dividendTaxPercentage / 100), "$0.000a")} / s<br>`;
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (this.dividendTaxPercentage / 100), "$0.000a")} / s<br><br>`;
}
var txt = "Total Funds: " + numeralWrapper.format(this.funds.toNumber(), '$0.000a') + "<br>" +
@ -3860,14 +4113,18 @@ Corporation.prototype.updateCorporationOverviewContent = function() {
dividendStr +
"Publicly Traded: " + (this.public ? "Yes" : "No") + "<br>" +
"Owned Stock Shares: " + numeralWrapper.format(this.numShares, '0.000a') + "<br>" +
"Stock Price: " + (this.public ? "$" + formatNumber(this.sharePrice, 2) : "N/A") + "<br><br>";
"Stock Price: " + (this.public ? "$" + formatNumber(this.sharePrice, 2) : "N/A") + "<br>" +
"<p class='tooltip'>Total Stock Shares: " + numeralWrapper.format(this.totalShares, "0.000a") +
"<span class='tooltiptext'>" +
`Outstanding Shares: ${numeralWrapper.format(this.issuedShares, "0.000a")}<br>` +
`Private Shares: ${numeralWrapper.format(this.totalShares - this.issuedShares - this.numShares, "0.000a")}` +
"</span></p><br><br>";
const storedTime = this.storedCycles * CONSTANTS.MilliPerCycle / 1000;
if (storedTime > 15) {
txt += `Bonus Time: ${storedTime} seconds<br><br>`;
}
var prodMult = this.getProductionMultiplier(),
storageMult = this.getStorageMultiplier(),
advMult = this.getAdvertisingMultiplier(),
@ -3887,6 +4144,41 @@ Corporation.prototype.updateCorporationOverviewContent = function() {
if (salesMult > 1) {txt += "Sales Multiplier: " + formatNumber(salesMult, 3) + "<br>";}
if (sciResMult > 1) {txt += "Scientific Research Multiplier: " + formatNumber(sciResMult, 3) + "<br>";}
p.innerHTML = txt;
// Disable buttons for cooldowns
if (sellSharesButton instanceof Element) {
if (this.shareSaleCooldown <= 0) {
sellSharesButton.className = "std-button tooltip";
} else {
sellSharesButton.className = "a-link-button-inactive tooltip";
}
}
if (sellSharesButtonTooltip instanceof Element) {
if (this.shareSaleCooldown <= 0) {
sellSharesButtonTooltip.innerText = "Sell your shares in the company. The money earned from selling your " +
"shares goes into your personal account, not the Corporation's. " +
"This is one of the only ways to profit from your business venture.";
} else {
sellSharesButtonTooltip.innerText = "Cannot sell shares for " + this.convertCooldownToString(this.shareSaleCooldown);
}
}
if (issueNewSharesButton instanceof Element) {
if (this.issueNewSharesCooldown <= 0) {
issueNewSharesButton.className = "std-button tooltip";
} else {
issueNewSharesButton.className = "a-link-button-inactive tooltip";
}
}
if (issueNewSharesButtonTooltip instanceof Element) {
if (this.issueNewSharesCooldown <= 0) {
issueNewSharesButtonTooltip.innerText = "Issue new equity shares to raise capital"
} else {
issueNewSharesButtonTooltip.innerText = "Cannot issue new shares for " + this.convertCooldownToString(this.issueNewSharesCooldown);
}
}
}
Corporation.prototype.displayDivisionContent = function(division, city) {
@ -4363,7 +4655,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
},
onkeyup:(e)=>{
e.preventDefault();
if (e.keyCode === 13) {confirmBtn.click();}
if (e.keyCode === KEY.ENTER) {confirmBtn.click();}
}
});
confirmBtn = createElement("a", {
@ -4739,6 +5031,11 @@ Corporation.prototype.clearUI = function() {
corporationUnlockUpgrades = null;
corporationUpgrades = null;
sellSharesButton = null;
issueNewSharesButton = null;
sellSharesButtonTooltip = null;
issueNewSharesButtonTooltip = null;
industryOverviewPanel = null;
industryOverviewText = null;

@ -16,7 +16,7 @@ export const CorporationUpgrades: IMap<any[]> = {
"Each level of this upgrade increases your global warehouse storage size by 10% (additive)."],
//Advertise through dreams, passive popularity/ awareness gain
"2": [2, 8e9, 1.09, .001,
"2": [2, 4e9, 1.1, .001,
"DreamSense", "Use DreamSense LCC Technologies to advertise your corporation " +
"to consumers through their dreams. Each level of this upgrade provides a passive " +
"increase in awareness of all of your companies (divisions) by 0.004 / market cycle," +
@ -52,7 +52,7 @@ export const CorporationUpgrades: IMap<any[]> = {
"of this upgrade globally increases the efficiency of your employees by 10% (additive)."],
//Improves sales of materials/products
"8": [8, 1e9, 1.08, 0.01,
"8": [8, 1e9, 1.07, 0.01,
"ABC SalesBots", "Always Be Closing. Purchase these robotic salesmen to increase the amount of " +
"materials and products you sell. Each level of this upgrade globally increases your sales " +
"by 1% (additive)."],

@ -29,6 +29,12 @@ import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
yesNoTxtInpBoxGetInput, yesNoBoxClose,
yesNoTxtInpBoxClose} from "../utils/YesNoBox";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import { createPopupCloseButton } from "../utils/uiHelpers/createPopupCloseButton";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
function displayLocationContent() {
var returnToWorld = clearEventListeners("location-return-to-world-button");
@ -1939,40 +1945,80 @@ function initLocationButtons() {
});
cityHallCreateCorporation.addEventListener("click", function() {
var yesBtn = yesNoTxtInpBoxGetYesButton(),
noBtn = yesNoTxtInpBoxGetNoButton();
yesBtn.innerText = "Create Corporation";
noBtn.innerText = "Cancel";
yesBtn.addEventListener("click", function() {
if (Player.money.lt(150e9)) {
dialogBoxCreate("You don't have enough money to create a corporation! You need $150b");
return yesNoTxtInpBoxClose();
}
Player.loseMoney(150e9);
var companyName = yesNoTxtInpBoxGetInput();
if (companyName == null || companyName == "") {
dialogBoxCreate("Invalid company name!");
const popupId = "create-corporation-popup";
const txt = createElement("p", {
innerHTML: "Would you like to start a corporation? This will require $150b for registration " +
"and initial funding. This $150b can either be self-funded, or you can obtain " +
"the seed money from the government in exchange for 500 million shares<br><br>" +
"If you would like to start one, please enter a name for your corporation below:",
});
const nameInput = createElement("input", {
placeholder: "Corporation Name",
});
const selfFundedButton = createElement("button", {
class: "popup-box-button",
innerText: "Self-Fund",
clickListener: () => {
if (Player.money.lt(150e9)) {
dialogBoxCreate("You don't have enough money to create a corporation! You need $150b");
return false;
}
Player.loseMoney(150e9);
const companyName = nameInput.value;
if (companyName == null || companyName == "") {
dialogBoxCreate("Invalid company name!");
return false;
}
Player.corporation = new Corporation({
name: companyName,
});
displayLocationContent();
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
dialogBoxCreate("Congratulations! You just self-funded your own corporation. You can visit " +
"and manage your company in the City");
removeElementById(popupId);
return false;
}
Player.corporation = new Corporation({
name:companyName,
});
displayLocationContent();
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
dialogBoxCreate("Congratulations! You just started your own corporation. You can visit " +
"and manage your company in the City");
return yesNoTxtInpBoxClose();
});
noBtn.addEventListener("click", function() {
return yesNoTxtInpBoxClose();
});
const seedMoneyButton = createElement("button", {
class: "popup-box-button",
innerText: "Use Seed Money",
clickListener: () => {
const companyName = nameInput.value;
if (companyName == null || companyName == "") {
dialogBoxCreate("Invalid company name!");
return false;
}
Player.corporation = new Corporation({
name: companyName,
});
Player.corporation.totalShares += 500e6;
displayLocationContent();
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
dialogBoxCreate("Congratulations! You just started your own corporation with government seed money. " +
"You can visit and manage your company in the City");
removeElementById(popupId);
return false;
}
})
const cancelBtn = createPopupCloseButton(popupId, { class: "popup-box-button" });
if (Player.corporation instanceof Corporation) {
return;
} else {
yesNoTxtInpBoxCreate("Would you like to start a corporation? This will require $150b " +
"for registration and initial funding.<br><br>If so, please enter " +
"a name for your corporation below:");
createPopup(popupId, [txt, nameInput, cancelBtn, selfFundedButton, seedMoneyButton]);
nameInput.focus();
}
});

@ -389,7 +389,6 @@ PlayerObject.prototype.prestigeSourceFile = function() {
this.has4SDataTixApi = false;
//BitNode 3: Corporatocracy
if (this.bitNodeN === 3) {this.money = new Decimal(150e9);}
this.corporation = 0;
this.playtimeSinceLastAug = 0;

@ -251,7 +251,6 @@ function prestigeSourceFile() {
//BitNode 3: Corporatocracy
if (Player.bitNodeN === 3) {
Player.money = new Decimal(150e9);
homeComp.messages.push("corporation-management-handbook.lit");
dialogBoxCreate("You received a copy of the Corporation Management Handbook on your home computer. " +
"Read it if you need help getting started with Corporations!");

@ -39,6 +39,10 @@ class NumeralFormatter {
if (Math.abs(n) < 1e-6) { n = 0; }
return numeral(n).format(format);
}
formatMoney(n: number): string {
return this.format(n, "$0.000a");
}
}
export const numeralWrapper = new NumeralFormatter();