Merge pull request #3802 from stalefishies/corp-dividends

CORPORATION: (BREAKING) Dividend fixes and exposing dividends info via scripts
This commit is contained in:
hydroflame 2022-07-20 15:08:41 -04:00 committed by GitHub
commit 17c2f06ae0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 59 additions and 47 deletions

@ -84,12 +84,12 @@ export function LevelUpgrade(corporation: ICorporation, upgrade: CorporationUpgr
} }
} }
export function IssueDividends(corporation: ICorporation, percent: number): void { export function IssueDividends(corporation: ICorporation, rate: number): void {
if (isNaN(percent) || percent < 0 || percent > CorporationConstants.DividendMaxPercentage) { if (isNaN(rate) || rate < 0 || rate > CorporationConstants.DividendMaxRate) {
throw new Error(`Invalid value. Must be an integer between 0 and ${CorporationConstants.DividendMaxPercentage}`); throw new Error(`Invalid value. Must be an number between 0 and ${CorporationConstants.DividendMaxRate}`);
} }
corporation.dividendPercentage = percent * 100; corporation.dividendRate = rate;
} }
export function SellMaterial(mat: Material, amt: string, price: string): void { export function SellMaterial(mat: Material, amt: string, price: string): void {

@ -35,8 +35,8 @@ export class Corporation {
shareSalesUntilPriceUpdate = CorporationConstants.SHARESPERPRICEUPDATE; shareSalesUntilPriceUpdate = CorporationConstants.SHARESPERPRICEUPDATE;
shareSaleCooldown = 0; // Game cycles until player can sell shares again shareSaleCooldown = 0; // Game cycles until player can sell shares again
issueNewSharesCooldown = 0; // Game cycles until player can issue shares again issueNewSharesCooldown = 0; // Game cycles until player can issue shares again
dividendPercentage = 0; dividendRate = 0;
dividendTaxPercentage = 50; dividendTax = 1 - BitNodeMultipliers.CorporationSoftcap + 0.15;
issuedShares = 0; issuedShares = 0;
sharePrice = 0; sharePrice = 0;
storedCycles = 0; storedCycles = 0;
@ -121,18 +121,19 @@ export class Corporation {
} }
// Process dividends // Process dividends
if (this.dividendPercentage > 0 && cycleProfit > 0) { this.updateDividendTax();
if (this.dividendRate > 0 && cycleProfit > 0) {
// Validate input again, just to be safe // Validate input again, just to be safe
if ( if (
isNaN(this.dividendPercentage) || isNaN(this.dividendRate) ||
this.dividendPercentage < 0 || this.dividendRate < 0 ||
this.dividendPercentage > CorporationConstants.DividendMaxPercentage * 100 this.dividendRate > CorporationConstants.DividendMaxRate
) { ) {
console.error(`Invalid Corporation dividend percentage: ${this.dividendPercentage}`); console.error(`Invalid Corporation dividend rate: ${this.dividendRate}`);
} else { } else {
const totalDividends = (this.dividendPercentage / 100) * cycleProfit; const totalDividends = this.dividendRate * cycleProfit;
const retainedEarnings = cycleProfit - totalDividends; const retainedEarnings = cycleProfit - totalDividends;
player.gainMoney(this.getDividends(), "corporation"); player.gainMoney(this.getCycleDividends(), "corporation");
this.addFunds(retainedEarnings); this.addFunds(retainedEarnings);
} }
} else { } else {
@ -146,20 +147,23 @@ export class Corporation {
} }
} }
getDividends(): number { updateDividendTax(): void {
const profit = this.revenue - this.expenses; this.dividendTax = 1 - BitNodeMultipliers.CorporationSoftcap + 0.15;
const cycleProfit = profit * CorporationConstants.SecsPerMarketCycle;
const totalDividends = (this.dividendPercentage / 100) * cycleProfit;
const dividendsPerShare = totalDividends / this.totalShares;
const dividends = this.numShares * dividendsPerShare;
let upgrades = -0.15;
if (this.unlockUpgrades[5] === 1) { if (this.unlockUpgrades[5] === 1) {
upgrades += 0.05; this.dividendTax -= 0.05;
} }
if (this.unlockUpgrades[6] === 1) { if (this.unlockUpgrades[6] === 1) {
upgrades += 0.1; this.dividendTax -= 0.1;
} }
return Math.pow(dividends, BitNodeMultipliers.CorporationSoftcap + upgrades); }
getCycleDividends(): number {
const profit = this.revenue - this.expenses;
const cycleProfit = profit * CorporationConstants.SecsPerMarketCycle;
const totalDividends = this.dividendRate * cycleProfit;
const dividendsPerShare = totalDividends / this.totalShares;
const dividends = this.numShares * dividendsPerShare;
return Math.pow(dividends, 1 - this.dividendTax);
} }
determineValuation(): number { determineValuation(): number {
@ -167,8 +171,8 @@ export class Corporation {
profit = this.avgProfit; profit = this.avgProfit;
if (this.public) { if (this.public) {
// Account for dividends // Account for dividends
if (this.dividendPercentage > 0) { if (this.dividendRate > 0) {
profit *= (100 - this.dividendPercentage) / 100; profit *= 1 - this.dividendRate;
} }
val = this.funds + profit * 85e3; val = this.funds + profit * 85e3;
@ -277,11 +281,7 @@ export class Corporation {
this.funds = this.funds - price; this.funds = this.funds - price;
// Apply effects for one-time upgrades // Apply effects for one-time upgrades
if (upgN === 5) { this.updateDividendTax();
this.dividendTaxPercentage -= 5;
} else if (upgN === 6) {
this.dividendTaxPercentage -= 10;
}
} }
//Levelable upgrades //Levelable upgrades

@ -19,8 +19,8 @@ export interface ICorporation {
shareSalesUntilPriceUpdate: number; shareSalesUntilPriceUpdate: number;
shareSaleCooldown: number; shareSaleCooldown: number;
issueNewSharesCooldown: number; issueNewSharesCooldown: number;
dividendPercentage: number; dividendRate: number;
dividendTaxPercentage: number; dividendTax: number;
issuedShares: number; issuedShares: number;
sharePrice: number; sharePrice: number;
storedCycles: number; storedCycles: number;
@ -54,6 +54,7 @@ export interface ICorporation {
getSalesMultiplier(): number; getSalesMultiplier(): number;
getScientificResearchMultiplier(): number; getScientificResearchMultiplier(): number;
getStarterGuide(player: IPlayer): void; getStarterGuide(player: IPlayer): void;
updateDividendTax(): void;
getCycleDividends(): number;
toJSON(): any; toJSON(): any;
getDividends(): number;
} }

@ -19,7 +19,7 @@ export const CorporationConstants: {
BribeThreshold: number; BribeThreshold: number;
BribeToRepRatio: number; BribeToRepRatio: number;
ProductProductionCostRatio: number; ProductProductionCostRatio: number;
DividendMaxPercentage: number; DividendMaxRate: number;
EmployeeSalaryMultiplier: number; EmployeeSalaryMultiplier: number;
CyclesPerEmployeeRaise: number; CyclesPerEmployeeRaise: number;
EmployeeRaiseAmount: number; EmployeeRaiseAmount: number;
@ -61,7 +61,7 @@ export const CorporationConstants: {
ProductProductionCostRatio: 5, //Ratio of material cost of a product to its production cost ProductProductionCostRatio: 5, //Ratio of material cost of a product to its production cost
DividendMaxPercentage: 1, DividendMaxRate: 1,
EmployeeSalaryMultiplier: 3, // Employee stats multiplied by this to determine initial salary EmployeeSalaryMultiplier: 3, // Employee stats multiplied by this to determine initial salary
CyclesPerEmployeeRaise: 400, // All employees get a raise every X market cycles CyclesPerEmployeeRaise: 400, // All employees get a raise every X market cycles

@ -275,17 +275,18 @@ interface IDividendsStatsProps {
} }
function DividendsStats({ profit }: IDividendsStatsProps): React.ReactElement { function DividendsStats({ profit }: IDividendsStatsProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
if (corp.dividendPercentage <= 0 || profit <= 0) return <></>; if (corp.dividendRate <= 0 || profit <= 0) return <></>;
const totalDividends = (corp.dividendPercentage / 100) * profit; const totalDividends = corp.dividendRate * profit;
const retainedEarnings = profit - totalDividends; const retainedEarnings = profit - totalDividends;
const dividendsPerShare = totalDividends / corp.totalShares; const dividendsPerShare = totalDividends / corp.totalShares;
const playerEarnings = corp.getCycleDividends() / CorporationConstants.SecsPerMarketCycle;
return ( return (
<StatsTable <StatsTable
rows={[ rows={[
["Retained Profits (after dividends):", <MoneyRate money={retainedEarnings} />], ["Retained Profits (after dividends):", <MoneyRate money={retainedEarnings} />],
["Dividend Percentage:", numeralWrapper.format(corp.dividendPercentage / 100, "0%")], ["Dividend Percentage:", numeralWrapper.format(corp.dividendRate, "0%")],
["Dividends per share:", <MoneyRate money={dividendsPerShare} />], ["Dividends per share:", <MoneyRate money={dividendsPerShare} />],
["Your earnings as a shareholder:", <MoneyRate money={corp.getDividends()} />], ["Your earnings as a shareholder:", <MoneyRate money={playerEarnings} />],
]} ]}
/> />
); );

@ -19,7 +19,7 @@ export function IssueDividendsModal(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const [percent, setPercent] = useState(0); const [percent, setPercent] = useState(0);
const canIssue = !isNaN(percent) && percent >= 0 && percent <= CorporationConstants.DividendMaxPercentage * 100; const canIssue = !isNaN(percent) && percent >= 0 && percent <= CorporationConstants.DividendMaxRate * 100;
function issueDividends(): void { function issueDividends(): void {
if (!canIssue) return; if (!canIssue) return;
if (percent === null) return; if (percent === null) return;

@ -920,14 +920,15 @@ export function NetscriptCorporation(player: IPlayer, workerScript: WorkerScript
}, },
issueDividends: issueDividends:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
(_percent: unknown): void => { (_rate: unknown): void => {
checkAccess(ctx); checkAccess(ctx);
const percent = ctx.helper.number("percent", _percent); const rate = ctx.helper.number("rate", _rate);
if (percent < 0 || percent > 100) const max = CorporationConstants.DividendMaxRate;
throw new Error("Invalid value for percent field! Must be numeric, greater than 0, and less than 100"); if (rate < 0 || rate > max)
throw new Error(`Invalid value for rate field! Must be numeric, greater than 0, and less than ${max}`);
const corporation = getCorporation(); const corporation = getCorporation();
if (!corporation.public) throw ctx.makeRuntimeErrorMsg(`Your company has not gone public!`); if (!corporation.public) throw ctx.makeRuntimeErrorMsg(`Your company has not gone public!`);
IssueDividends(corporation, percent); IssueDividends(corporation, rate);
}, },
// If you modify these objects you will affect them for real, it's not // If you modify these objects you will affect them for real, it's not
@ -954,6 +955,9 @@ export function NetscriptCorporation(player: IPlayer, workerScript: WorkerScript
shareSaleCooldown: corporation.shareSaleCooldown, shareSaleCooldown: corporation.shareSaleCooldown,
issuedShares: corporation.issuedShares, issuedShares: corporation.issuedShares,
sharePrice: corporation.sharePrice, sharePrice: corporation.sharePrice,
dividendRate: corporation.dividendRate,
dividendTax: corporation.dividendTax,
dividendEarnings: corporation.getCycleDividends() / CorporationConstants.SecsPerMarketCycle,
state: corporation.state.getState(), state: corporation.state.getState(),
divisions: corporation.divisions.map((division): NSDivision => getSafeDivision(division)), divisions: corporation.divisions.map((division): NSDivision => getSafeDivision(division)),
}; };

@ -7053,9 +7053,9 @@ export interface Corporation extends WarehouseAPI, OfficeAPI {
levelUpgrade(upgradeName: string): void; levelUpgrade(upgradeName: string): void;
/** /**
* Issue dividends * Issue dividends
* @param percent - Percent of profit to issue as dividends. * @param rate - Fraction of profit to issue as dividends.
*/ */
issueDividends(percent: number): void; issueDividends(rate: number): void;
/** /**
* Buyback Shares * Buyback Shares
* @param amount - Amount of shares to buy back. * @param amount - Amount of shares to buy back.
@ -7105,6 +7105,12 @@ interface CorporationInfo {
issuedShares: number; issuedShares: number;
/** Price of the shares */ /** Price of the shares */
sharePrice: number; sharePrice: number;
/** Fraction of profits issued as dividends */
dividendRate: number;
/** Tax applied on your earnings as a shareholder */
dividendTax: number;
/** Your earnings as a shareholder per second this cycle */
dividendEarnings: number;
/** State of the corporation. Possible states are START, PURCHASE, PRODUCTION, SALE, EXPORT. */ /** State of the corporation. Possible states are START, PURCHASE, PRODUCTION, SALE, EXPORT. */
state: string; state: string;
/** Array of all divisions */ /** Array of all divisions */