From acbe9af18a56b3de307b3eb11df0db29ae54d0da Mon Sep 17 00:00:00 2001 From: nbintertech Date: Wed, 13 Mar 2024 11:12:23 -0500 Subject: [PATCH 1/3] Update spending results and fix cost per carbon kg --- GAMEPLAY.md | 5 ++- src/App.tsx | 1 + src/components/YearRecap.tsx | 63 ++++++++++++++++++++++++++++++++---- src/trackedStats.tsx | 7 +++- 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/GAMEPLAY.md b/GAMEPLAY.md index 94cfdf7..536d92a 100644 --- a/GAMEPLAY.md +++ b/GAMEPLAY.md @@ -56,6 +56,9 @@ Are paid for once, regardless of 1 or 2 year gameplay, and are renewed each year - Solar Panels Carport, Rooftop mid-sized Solar + ###### Power Purchase Agreements (PPA): + Are paid for annually over a 10-year term. This is a special payment type and should not be included in most of the gameplay mechanisms and logic related to financing. + ###### Always Carryover cost Savings: Regardless of game settings, these projects carryover cost savings every year - Small solar carport, Rooftop mid-sized Solar, Community Wind @@ -82,7 +85,7 @@ Shows completed projects after their implementation year, as well non-renewable Are only applied in the first year that the project is implemented. ### Capital Funding Rewards -Players will be awarded Capital Funding rewards (one free project) for reaching savings milestones of 5% and 35% throughout the game. Each Capital Funding reward must be used in the following new year. VPPA projects are ineligible. +Players will be awarded Capital Funding rewards (one free project) for reaching savings milestones of 5% and 35% throughout the game. Each Capital Funding reward must be used in the following new year. PPA projects are ineligible. --- diff --git a/src/App.tsx b/src/App.tsx index 2197d4d..226f16d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -591,6 +591,7 @@ export class App extends React.PureComponent { let newYearRangeInitialStats = [...this.state.yearRangeInitialStats, { ...newYearTrackedStats }]; console.log('=============================='); console.log('Total new year budget (financing/renewable costs applied)', newYearTrackedStats.financesAvailable); + console.log('Game Total Spending', newYearTrackedStats.gameTotalSpending); console.log('======== END ================='); const completedYears = this.state.completedYears < this.state.trackedStats.currentGameYear? this.state.completedYears + 1 : this.state.completedYears; diff --git a/src/components/YearRecap.tsx b/src/components/YearRecap.tsx index ae6ba7b..c350a5b 100644 --- a/src/components/YearRecap.tsx +++ b/src/components/YearRecap.tsx @@ -114,7 +114,9 @@ export class YearRecap extends React.Component { const unspentBudgetFormatted: string = noDecimalsFormatter.format(recapResults.unspentBudget); const yearEndTotalSpendingFormatted: string = noDecimalsFormatter.format(recapResults.yearEndTotalSpending); // formatting new value? or existing - const gameTotalNetCostFormatted: string = noDecimalsFormatter.format(mutableStats.yearEndTotalSpending); + const gameTotalNetCostFormatted: string = noDecimalsFormatter.format(mutableStats.gameTotalSpending); + const projectedFinancedSpendingFormatted: string = noDecimalsFormatter.format(recapResults.projectedFinancedSpending); + const gameCurrentAndProjectedSpendingFormatted: string = noDecimalsFormatter.format(recapResults.gameCurrentAndProjectedSpending); const costPerCarbonSavingsFormatted: string = mutableStats.costPerCarbonSavings !== undefined ? Intl.NumberFormat('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 2, @@ -218,6 +220,21 @@ export class YearRecap extends React.Component { } /> + {recapResults.projectedFinancedSpending > 0 && + + + + + + You are projected to spend {' '}${projectedFinancedSpendingFormatted}{' '} on financed and renewed projects for the + remaining years of the game. You are projected to spend {' '}${gameCurrentAndProjectedSpendingFormatted}{' '} total over the course of the game. + + } + /> + + } @@ -335,6 +352,8 @@ export class YearRecap extends React.Component { projectRecapCards: [], unspentBudget: props.financesAvailable, yearEndTotalSpending: 0, + projectedFinancedSpending: 0, + gameCurrentAndProjectedSpending: 0, yearCostSavings: { naturalGas: 0, electricity: 0, @@ -409,8 +428,11 @@ export class YearRecap extends React.Component { recapResults.yearEndTotalSpending += this.getOngoingFinancingCosts(props.completedProjects, mutableStats); setCapitalFundingExpired(mutableCapitalFundingState, mutableStats); this.addCapitalFundingRewardCard(recapResults.projectRecapCards, mutableCapitalFundingState, mutableStats); - mutableStats.yearEndTotalSpending = initialCurrentYearStats.yearEndTotalSpending + recapResults.yearEndTotalSpending; - this.setCostPerCarbonSavings(mutableStats); + mutableStats.yearEndTotalSpending = recapResults.yearEndTotalSpending; + mutableStats.gameTotalSpending = initialCurrentYearStats.gameTotalSpending + recapResults.yearEndTotalSpending; + recapResults.projectedFinancedSpending = this.getProjectedFinancedSpending(implementedFinancedProjects, implementedRenewableProjectsCopy, mutableStats); + recapResults.gameCurrentAndProjectedSpending = mutableStats.gameTotalSpending + recapResults.projectedFinancedSpending; + this.setCostPerCarbonSavings(mutableStats, recapResults.gameCurrentAndProjectedSpending); recapResults.yearCostSavings = getYearCostSavings(initialCurrentYearStats, mutableStats); this.setRenewableProjectResults(implementedRenewableProjectsCopy, mutableStats, initialCurrentYearStats, recapResults.yearCostSavings); @@ -420,10 +442,10 @@ export class YearRecap extends React.Component { /** * Set mutable stats costPerCarbonSavings */ - setCostPerCarbonSavings(mutableStats: TrackedStats) { + setCostPerCarbonSavings(mutableStats: TrackedStats, gameCurrentAndProjectedSpending: number) { let costPerCarbonSavings = 0; - if (mutableStats.yearEndTotalSpending > 0 && mutableStats.carbonSavingsPerKg > 0) { - costPerCarbonSavings = mutableStats.yearEndTotalSpending / mutableStats.carbonSavingsPerKg; + if (gameCurrentAndProjectedSpending > 0 && mutableStats.carbonSavingsPerKg > 0) { + costPerCarbonSavings = gameCurrentAndProjectedSpending / mutableStats.carbonSavingsPerKg; } mutableStats.costPerCarbonSavings = costPerCarbonSavings; } @@ -446,6 +468,31 @@ export class YearRecap extends React.Component { return yearFinancingCosts; } + /** + * Future costs from financed projects and renewed PPAs still being paid on + */ + getProjectedFinancedSpending(implementedFinancedProjects: ImplementedProject[], renewableProjects: ImplementedProject[], mutableStats: TrackedStats) { + let futureFinancedSpending: number = implementedFinancedProjects.reduce((totalFutureSpending: number, project: ImplementedProject) => { + return this.getRemainingProjectCosts(project, mutableStats, totalFutureSpending); + }, 0); + let PPAProjects = renewableProjects.filter(project => Projects[project.page].isPPA); + futureFinancedSpending += PPAProjects.reduce((totalFutureSpending: number, project: ImplementedProject) => { + return this.getRemainingProjectCosts(project, mutableStats, totalFutureSpending); + }, 0); + return futureFinancedSpending; + } + + getRemainingProjectCosts(project: ImplementedProject, mutableStats: TrackedStats, totalFutureSpending: number) { + let yearsRemaining = 10 - mutableStats.currentGameYear; + let projectControl = Projects[project.page]; + let isAnnuallyFinanced = getIsAnnuallyFinanced(project.financingOption.financingType.id); + if ((isAnnuallyFinanced || projectControl.isPPA) && yearsRemaining) { + let annualCost = projectControl.getImplementationCost(project.financingOption.financingType.id, mutableStats.gameYearInterval); + let futureCosts = annualCost * yearsRemaining; + totalFutureSpending += futureCosts; + } + return totalFutureSpending; + } /** * WARNING - Directly mutates renewable project in first year. This is a workaround to get correct stats display and state given some of the other game mechanics and logic @@ -1140,6 +1187,10 @@ export interface YearRecapResults { projectRecapCards: JSX.Element[], unspentBudget: number, yearEndTotalSpending: number, + // projects costs of financing projects still being paid on + projectedFinancedSpending: number, + // all past and future projected spending, including above + gameCurrentAndProjectedSpending: number, yearCostSavings: YearCostSavings } diff --git a/src/trackedStats.tsx b/src/trackedStats.tsx index da2b789..0b064ab 100644 --- a/src/trackedStats.tsx +++ b/src/trackedStats.tsx @@ -83,8 +83,13 @@ export interface TrackedStats { hiddenSpending: number; /** * End of year total spending, adusted for hidden costs and rebates + * NOTE: at YearRecap this value is the cumulative spend for the whole game */ yearEndTotalSpending: number; + /** + * Total spending so far throughout the game + */ + gameTotalSpending: number; /** * Current year of game */ @@ -141,6 +146,7 @@ export const initialTrackedStats: TrackedStats = { implementationSpending: 0, hiddenSpending: 0, yearEndTotalSpending: 0, + gameTotalSpending: 0, currentGameYear: 1, gameYearDisplayOffset: 1, gameYearInterval: 1 @@ -172,7 +178,6 @@ export function setCarbonEmissionsAndSavings(newStats: TrackedStats, defaultTrac } let carbonSavingsPercent = (defaultTrackedStats.carbonEmissions - newEmissions) / (defaultTrackedStats.carbonEmissions); - // * % CO2 saved * total initial emissions; newStats.carbonSavingsPerKg = carbonSavingsPercent * defaultTrackedStats.carbonEmissions; newStats.carbonSavingsPercent = carbonSavingsPercent; return newStats; From 0f2860e5c199a87438d5194fe0a660273fc15e70 Mon Sep 17 00:00:00 2001 From: nbintertech Date: Wed, 13 Mar 2024 11:15:48 -0500 Subject: [PATCH 2/3] Update yearEndtotalSpending comment --- src/trackedStats.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/trackedStats.tsx b/src/trackedStats.tsx index 0b064ab..b582de0 100644 --- a/src/trackedStats.tsx +++ b/src/trackedStats.tsx @@ -83,7 +83,6 @@ export interface TrackedStats { hiddenSpending: number; /** * End of year total spending, adusted for hidden costs and rebates - * NOTE: at YearRecap this value is the cumulative spend for the whole game */ yearEndTotalSpending: number; /** From 6779dbca471ed1cce01a6954107f416a1f165caa Mon Sep 17 00:00:00 2001 From: nbintertech Date: Thu, 14 Mar 2024 08:53:52 -0500 Subject: [PATCH 3/3] Add EOG projections, fix year recap projections --- GAMEPLAY.md | 4 +- src/Financing.tsx | 34 +++++- src/Projects.tsx | 15 ++- src/components/CurrentPage.tsx | 9 +- .../EndGameReport/EndGameReportPage.tsx | 100 +++++++++++++++++- src/components/EnergyUseLineChart.tsx | 2 - src/components/YearRecap.tsx | 47 +------- src/trackedStats.tsx | 11 ++ 8 files changed, 160 insertions(+), 62 deletions(-) diff --git a/GAMEPLAY.md b/GAMEPLAY.md index 536d92a..c2f9ad4 100644 --- a/GAMEPLAY.md +++ b/GAMEPLAY.md @@ -53,14 +53,14 @@ - Mid-Sized Solar, Community Wind, Utility-PPPA Wind ###### One-time Payment Renewables: - Are paid for once, regardless of 1 or 2 year gameplay, and are renewed each year + Can be paid for once (or financed), regardless of 1 or 2 year gameplay, and are renewed each year - Solar Panels Carport, Rooftop mid-sized Solar ###### Power Purchase Agreements (PPA): Are paid for annually over a 10-year term. This is a special payment type and should not be included in most of the gameplay mechanisms and logic related to financing. ###### Always Carryover cost Savings: - Regardless of game settings, these projects carryover cost savings every year + **!!!!** Regardless of game settings, these projects carryover cost savings every year - Small solar carport, Rooftop mid-sized Solar, Community Wind diff --git a/src/Financing.tsx b/src/Financing.tsx index f86a831..70a7e7d 100644 --- a/src/Financing.tsx +++ b/src/Financing.tsx @@ -1,4 +1,4 @@ -import type { ImplementedProject, ProjectControl, RecapSurprise } from './ProjectControl'; +import type { ImplementedProject, ProjectControl, RecapSurprise, RenewableProject } from './ProjectControl'; import Projects from './Projects'; import type { TrackedStats } from './trackedStats'; import AttachMoneyIcon from '@mui/icons-material/AttachMoney'; @@ -144,21 +144,53 @@ export function getRoundIsExpired(round: FundingRound, stats: TrackedStats) { export function getDefaultFinancingOption(projectControl: ProjectControl, hasFinancingOptions: boolean, baseCost: number): FinancingOption { let name = 'Pay with Existing Budget'; let description = hasFinancingOptions ? 'Reduce energy use with a one-time payment' : 'Pay for project with funds from current budget'; + let term; if (projectControl.customBudgetType) { name = projectControl.customBudgetType.name; description = projectControl.customBudgetType.description; + term = projectControl.customBudgetType.loanTerm; } return { financingType: { name: name, description: description, id: 'budget', + loanTerm: term }, financedTotalCost: baseCost, financedAnnualCost: undefined, } } +export function getProjectedFinancedSpending(implementedFinancedProjects: ImplementedProject[], renewableProjects: RenewableProject[], mutableStats: TrackedStats) { + let futureFinancedSpending: number = 0; + futureFinancedSpending = implementedFinancedProjects.reduce((totalFutureSpending: number, project: ImplementedProject) => { + return getRemainingProjectCosts(project, mutableStats, totalFutureSpending); + }, 0); + futureFinancedSpending += renewableProjects.reduce((totalFutureSpending: number, project: ImplementedProject) => { + return getRemainingProjectCosts(project, mutableStats, totalFutureSpending); + }, 0); + return futureFinancedSpending; +} + + +export function getRemainingProjectCosts(project: ImplementedProject, mutableStats: TrackedStats, totalFutureSpending: number) { + let finishedGameYear = mutableStats.currentGameYear + 1; + let yearsPaid = finishedGameYear - project.yearStarted; + yearsPaid = yearsPaid * mutableStats.gameYearInterval + let yearsRemaining = project.financingOption.financingType.loanTerm - yearsPaid; + let projectControl = Projects[project.page]; + let isAnnuallyFinanced = getIsAnnuallyFinanced(project.financingOption.financingType.id); + if ((isAnnuallyFinanced || projectControl.isPPA) && yearsRemaining) { + // we always want this cost to be expressed in single year + let yearInterval = 1 + let annualCost = projectControl.getImplementationCost(project.financingOption.financingType.id, yearInterval); + let futureCosts = annualCost * yearsRemaining; + totalFutureSpending += futureCosts; + } + return totalFutureSpending; +} + /** * Set milestone round is earned diff --git a/src/Projects.tsx b/src/Projects.tsx index 6367819..0a4affe 100644 --- a/src/Projects.tsx +++ b/src/Projects.tsx @@ -1093,7 +1093,8 @@ Projects[Pages.smallVPPA] = new ProjectControl({ customBudgetType: { name: "Power Purchase Agreement", description: "Pay Annually", - id: 'budget' + id: 'budget', + loanTerm: 10 }, title: 'Invest in wind VPPA', shortTitle: 'Invest in wind VPPA to offset {10%} of your electricity emissions. {THIS PROJECT WILL BE RENEWED ANNUALLY}.', @@ -1126,7 +1127,8 @@ Projects[Pages.midVPPA] = new ProjectControl({ customBudgetType: { name: "Power Purchase Agreement", description: "Pay Annually", - id: 'budget' + id: 'budget', + loanTerm: 10 }, title: 'Invest in wind VPPA', shortTitle: 'Invest in wind VPPA to offset {20%} of your electricity emissions. {THIS PROJECT WILL BE RENEWED ANNUALLY}.', @@ -1159,7 +1161,8 @@ Projects[Pages.largeVPPA] = new ProjectControl({ customBudgetType: { name: "Power Purchase Agreement", description: "Pay Annually", - id: 'budget' + id: 'budget', + loanTerm: 10 }, title: 'Invest in wind VPPA', shortTitle: 'Invest in wind VPPA to offset {30%} of your electricity emissions. {THIS PROJECT WILL BE RENEWED ANNUALLY}.', @@ -1189,7 +1192,8 @@ Projects[Pages.midSolar] = new ProjectControl({ customBudgetType: { name: "Power Purchase Agreement", description: "Pay Annually", - id: 'budget' + id: 'budget', + loanTerm: 10 }, statsInfoAppliers: { absoluteCarbonSavings: absolute(-1_717_000) @@ -1233,7 +1237,8 @@ Projects[Pages.largeWind] = new ProjectControl({ customBudgetType: { name: "Power Purchase Agreement", description: "Pay Annually", - id: 'budget' + id: 'budget', + loanTerm: 10 }, statsInfoAppliers: { absoluteCarbonSavings: absolute(-4_292_000) diff --git a/src/components/CurrentPage.tsx b/src/components/CurrentPage.tsx index 48ae1c4..46b95ac 100644 --- a/src/components/CurrentPage.tsx +++ b/src/components/CurrentPage.tsx @@ -11,7 +11,7 @@ import type { StartPageProps } from './StartPage'; import { YearRecap } from './YearRecap'; import type { PageControlProps, ControlCallbacks } from './controls'; import { CapitalFundingState } from '../Financing'; -import EndGameReport from './EndGameReport/EndGameReportPage'; +import EndGameReportPage from './EndGameReport/EndGameReportPage'; interface CurrentPageProps extends ControlCallbacks, PageControlProps { @@ -85,20 +85,19 @@ export class CurrentPage extends PureComponentIgnoreFuncs { yearRangeInitialStats={this.props.yearRangeInitialStats} handleNewYearSetup={this.props.handleNewYearSetupOnProceed} />; - case EndGameReport: + case EndGameReportPage: return ( - - - ); default: return <>; diff --git a/src/components/EndGameReport/EndGameReportPage.tsx b/src/components/EndGameReport/EndGameReportPage.tsx index 08c7a82..a7a8466 100644 --- a/src/components/EndGameReport/EndGameReportPage.tsx +++ b/src/components/EndGameReport/EndGameReportPage.tsx @@ -1,9 +1,9 @@ import React, { Fragment } from 'react'; -import { TrackedStats } from '../../trackedStats'; +import { TrackedStats, setCarbonEmissionsAndSavings, setCostPerCarbonSavings } from '../../trackedStats'; import { GameSettings } from '../SelectGameSettings'; -import { CapitalFundingState, FinancingOption, isProjectFullyFunded } from '../../Financing'; +import { CapitalFundingState, FinancingOption, getIsAnnuallyFinanced, getProjectedFinancedSpending, isProjectFullyFunded } from '../../Financing'; import { ControlCallbacks, Emphasis, PageControl } from '../controls'; -import { Box, Button, Card, CardContent, CardHeader, Grid, Link, List, ListItem, ListItemText, Tooltip, TooltipProps, Typography, styled, tooltipClasses } from '@mui/material'; +import { Box, Button, Card, CardContent, CardHeader, Grid, Link, List, ListItem, ListItemIcon, ListItemText, Tooltip, TooltipProps, Typography, styled, tooltipClasses } from '@mui/material'; import { ParentSize } from '@visx/responsive'; import { CompletedProject, ImplementedProject, ProjectControl, RenewableProject } from '../../ProjectControl'; import { ImplementedFinancingData } from '../YearRecap'; @@ -12,6 +12,7 @@ import { DialogFinancingOptionCard } from '../Dialogs/ProjectDialog'; import { parseSpecialText, truncate } from '../../functions-and-types'; import EnergyUseLineChart from '../EnergyUseLineChart'; import DownloadPDF, { ReportPDFProps } from './DownloadPDF'; +import InfoIcon from '@mui/icons-material/Info'; export default class EndGameReportPage extends React.Component { @@ -29,13 +30,15 @@ export default class EndGameReportPage extends React.Component ); @@ -69,6 +72,31 @@ function EndGameReport(props: ReportProps) { }); + let projectedFinancedSpending = getProjectedFinancedSpending(props.implementedFinancedProjects, props.renewableProjects, props.mutableStats); + let gameCurrentAndProjectedSpending = props.mutableStats.gameTotalSpending + projectedFinancedSpending; + setCarbonEmissionsAndSavings(props.mutableStats, props.defaultTrackedStats); + + setCostPerCarbonSavings(props.mutableStats, gameCurrentAndProjectedSpending); + + let endOfGameResults = { + carbonSavingsPercent: props.mutableStats.carbonSavingsPercent, + gameTotalSpending: props.mutableStats.gameTotalSpending, + projectedFinancedSpending: projectedFinancedSpending, + gameCurrentAndProjectedSpending: gameCurrentAndProjectedSpending, + costPerCarbonSavings: props.mutableStats.costPerCarbonSavings + } + const noDecimalsFormatter = Intl.NumberFormat('en-US', { + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }); + const carbonSavingsPercentFormatted: string = (endOfGameResults.carbonSavingsPercent * 100).toFixed(2); + const gameTotalNetCostFormatted: string = noDecimalsFormatter.format(endOfGameResults.gameTotalSpending); + const projectedFinancedSpendingFormatted: string = noDecimalsFormatter.format(endOfGameResults.projectedFinancedSpending); + const costPerCarbonSavingsFormatted: string = endOfGameResults.costPerCarbonSavings !== undefined ? Intl.NumberFormat('en-US', { + minimumFractionDigits: 0, + maximumFractionDigits: 2, + }).format(endOfGameResults.costPerCarbonSavings) : '0'; + return ( @@ -78,6 +106,64 @@ function EndGameReport(props: ReportProps) { parentElement={parent}/> )} + + + + + + + + + Your company has reduced CO2e Emissions by{' '} + {carbonSavingsPercentFormatted}%{' '} + + } + /> + + + + + + + You have spent{' '}${gameTotalNetCostFormatted}{' '} throughout the game. + + } + /> + + + + + + + Your cost per kg reduced was{' '}${costPerCarbonSavingsFormatted}/kg CO2e{' '} + + } + /> + + {endOfGameResults.projectedFinancedSpending > 0 && + + + + + + You are projected to spend {' '}${projectedFinancedSpendingFormatted}{' '} on financed and renewed projects in future years + + } + /> + + } + + + + { - // 1 MMBTU = 293.07107kw - let conversionFactorKwH = 293.07107; let electricityEmissions = statYear.electricityUseKWh * getElectricityEmissionsFactor(statYear.currentGameYear, statYear.gameYearInterval, statYear.gameYearDisplayOffset); let natGasEmissions = statYear.naturalGasMMBTU * statYear.naturalGasEmissionsPerMMBTU; let landfillGasEmissions = statYear.hydrogenMMBTU * statYear.hydrogenEmissionsPerMMBTU; diff --git a/src/components/YearRecap.tsx b/src/components/YearRecap.tsx index b356cd8..7db95fb 100644 --- a/src/components/YearRecap.tsx +++ b/src/components/YearRecap.tsx @@ -32,7 +32,7 @@ import { import type { ControlCallbacks, PageControl } from './controls'; import { Emphasis } from './controls'; import type { TrackedStats, YearCostSavings } from '../trackedStats'; -import { statsGaugeProperties, getYearCostSavings, setCarbonEmissionsAndSavings } from '../trackedStats'; +import { statsGaugeProperties, getYearCostSavings, setCarbonEmissionsAndSavings, setCostPerCarbonSavings } from '../trackedStats'; import type { CompletedProject, NumberApplier, RenewableProject, ProjectControl, RecapSurprise, ImplementedProject } from '../ProjectControl'; import { clampRatio, @@ -50,7 +50,7 @@ import YearRecapCharts from './YearRecapCharts'; import Projects from '../Projects'; import { ParentSize } from '@visx/responsive'; import { GameSettings } from './SelectGameSettings'; -import { CapitalFundingState, FinancingOption, getCanUseCapitalFunding, getCapitalFundingSurprise, getIsAnnuallyFinanced, isProjectFullyFunded, setCapitalFundingExpired, setCapitalFundingMilestone } from '../Financing'; +import { CapitalFundingState, FinancingOption, getCanUseCapitalFunding, getCapitalFundingSurprise, getIsAnnuallyFinanced, getProjectedFinancedSpending, isProjectFullyFunded, setCapitalFundingExpired, setCapitalFundingMilestone } from '../Financing'; import { findFinancingOptionFromProject } from '../Financing'; import { DialogFinancingOptionCard } from './Dialogs/ProjectDialog'; @@ -228,8 +228,7 @@ export class YearRecap extends React.Component { - You are projected to spend {' '}${projectedFinancedSpendingFormatted}{' '} on financed and renewed projects for the - remaining years of the game. You are projected to spend {' '}${gameCurrentAndProjectedSpendingFormatted}{' '} total over the course of the game. + You are projected to spend {' '}${projectedFinancedSpendingFormatted}{' '} on financed and renewed projects. Your total projected spend is {' '}${gameCurrentAndProjectedSpendingFormatted}{' '}. } /> @@ -443,25 +442,15 @@ export class YearRecap extends React.Component { this.addCapitalFundingRewardCard(recapResults.projectRecapCards, mutableCapitalFundingState, mutableStats); mutableStats.yearEndTotalSpending = recapResults.yearEndTotalSpending; mutableStats.gameTotalSpending = initialCurrentYearStats.gameTotalSpending + recapResults.yearEndTotalSpending; - recapResults.projectedFinancedSpending = this.getProjectedFinancedSpending(implementedFinancedProjects, implementedRenewableProjectsCopy, mutableStats); + recapResults.projectedFinancedSpending = getProjectedFinancedSpending(implementedFinancedProjects, implementedRenewableProjectsCopy, mutableStats); recapResults.gameCurrentAndProjectedSpending = mutableStats.gameTotalSpending + recapResults.projectedFinancedSpending; - this.setCostPerCarbonSavings(mutableStats, recapResults.gameCurrentAndProjectedSpending); + setCostPerCarbonSavings(mutableStats, recapResults.gameCurrentAndProjectedSpending); recapResults.yearCostSavings = getYearCostSavings(initialCurrentYearStats, mutableStats); this.setRenewableProjectResults(implementedRenewableProjectsCopy, mutableStats, initialCurrentYearStats, recapResults.yearCostSavings); return recapResults; } - /** - * Set mutable stats costPerCarbonSavings - */ - setCostPerCarbonSavings(mutableStats: TrackedStats, gameCurrentAndProjectedSpending: number) { - let costPerCarbonSavings = 0; - if (gameCurrentAndProjectedSpending > 0 && mutableStats.carbonSavingsPerKg > 0) { - costPerCarbonSavings = gameCurrentAndProjectedSpending / mutableStats.carbonSavingsPerKg; - } - mutableStats.costPerCarbonSavings = costPerCarbonSavings; - } /** * Costs from completed projects still in financing @@ -481,32 +470,6 @@ export class YearRecap extends React.Component { return yearFinancingCosts; } - /** - * Future costs from financed projects and renewed PPAs still being paid on - */ - getProjectedFinancedSpending(implementedFinancedProjects: ImplementedProject[], renewableProjects: ImplementedProject[], mutableStats: TrackedStats) { - let futureFinancedSpending: number = implementedFinancedProjects.reduce((totalFutureSpending: number, project: ImplementedProject) => { - return this.getRemainingProjectCosts(project, mutableStats, totalFutureSpending); - }, 0); - let PPAProjects = renewableProjects.filter(project => Projects[project.page].isPPA); - futureFinancedSpending += PPAProjects.reduce((totalFutureSpending: number, project: ImplementedProject) => { - return this.getRemainingProjectCosts(project, mutableStats, totalFutureSpending); - }, 0); - return futureFinancedSpending; - } - - getRemainingProjectCosts(project: ImplementedProject, mutableStats: TrackedStats, totalFutureSpending: number) { - let yearsRemaining = 10 - mutableStats.currentGameYear; - let projectControl = Projects[project.page]; - let isAnnuallyFinanced = getIsAnnuallyFinanced(project.financingOption.financingType.id); - if ((isAnnuallyFinanced || projectControl.isPPA) && yearsRemaining) { - let annualCost = projectControl.getImplementationCost(project.financingOption.financingType.id, mutableStats.gameYearInterval); - let futureCosts = annualCost * yearsRemaining; - totalFutureSpending += futureCosts; - } - return totalFutureSpending; - } - /** * WARNING - Directly mutates renewable project in first year. This is a workaround to get correct stats display and state given some of the other game mechanics and logic * we need to assign/save individualized project savings to be applied in each renewable year recap - later years don't change savings state, only display values diff --git a/src/trackedStats.tsx b/src/trackedStats.tsx index b582de0..87d3366 100644 --- a/src/trackedStats.tsx +++ b/src/trackedStats.tsx @@ -182,6 +182,17 @@ export function setCarbonEmissionsAndSavings(newStats: TrackedStats, defaultTrac return newStats; } +/** +* Set mutable stats costPerCarbonSavings +*/ +export function setCostPerCarbonSavings(mutableStats: TrackedStats, gameCurrentAndProjectedSpending: number) { + let costPerCarbonSavings = 0; + if (gameCurrentAndProjectedSpending > 0 && mutableStats.carbonSavingsPerKg > 0) { + costPerCarbonSavings = gameCurrentAndProjectedSpending / mutableStats.carbonSavingsPerKg; + } + mutableStats.costPerCarbonSavings = costPerCarbonSavings; +} + export function getYearCostSavings(oldStats: TrackedStats, newStats: TrackedStats): YearCostSavings { let oldNgCost = oldStats.naturalGasCostPerMMBTU * oldStats.naturalGasMMBTU; let newNgCost = newStats.naturalGasCostPerMMBTU * newStats.naturalGasMMBTU;