Skip to content

Commit

Permalink
Add EOG projections, fix year recap projections
Browse files Browse the repository at this point in the history
  • Loading branch information
nbintertech committed Mar 14, 2024
1 parent 0f2860e commit 6779dbc
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 62 deletions.
4 changes: 2 additions & 2 deletions GAMEPLAY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
34 changes: 33 additions & 1 deletion src/Financing.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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
Expand Down
15 changes: 10 additions & 5 deletions src/Projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}.',
Expand Down Expand Up @@ -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}.',
Expand Down Expand Up @@ -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}.',
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 4 additions & 5 deletions src/components/CurrentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -85,20 +85,19 @@ export class CurrentPage extends PureComponentIgnoreFuncs<CurrentPageProps> {
yearRangeInitialStats={this.props.yearRangeInitialStats}
handleNewYearSetup={this.props.handleNewYearSetupOnProceed}
/>;
case EndGameReport:
case EndGameReportPage:
return (
<Fragment>
<EndGameReport
<EndGameReportPage
{...controlCallbacks}
{...this.props.gameSettings}
trackedStats={this.props.trackedStats}
defaultTrackedStats={this.props.defaultTrackedStats}
capitalFundingState={this.props.capitalFundingState}
implementedRenewableProjects={this.props.implementedRenewableProjects}
implementedFinancedProjects={this.props.implementedFinancedProjects}
completedProjects={this.props.completedProjects}
yearRangeInitialStats={this.props.yearRangeInitialStats}
/>
</Fragment>
);
default:
return <></>;
Expand Down
100 changes: 95 additions & 5 deletions src/components/EndGameReport/EndGameReportPage.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<EndGameReportPageProps> {
Expand All @@ -29,13 +30,15 @@ export default class EndGameReportPage extends React.Component<EndGameReportPage
});

let completedProjects = this.props.completedProjects.concat(completedRenewables);

return (
<Box m={2}>
<EndGameReport
yearRangeInitialStats={yearRangeInitialStats}
implementedFinancedProjects={this.props.implementedFinancedProjects}
renewableProjects={this.props.implementedRenewableProjects}
completedProjects={completedProjects}
mutableStats={mutableStats}
defaultTrackedStats={this.props.defaultTrackedStats}
/>
</Box>
);
Expand Down Expand Up @@ -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 (
<Fragment>
<ParentSize>
Expand All @@ -78,6 +106,64 @@ function EndGameReport(props: ReportProps) {
parentElement={parent}/>
)}
</ParentSize>
<Box m={2}>
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
<List dense={true}>
<ListItem >
<ListItemIcon>
<InfoIcon />
</ListItemIcon>
<ListItemText
primary={
<Typography variant='h5'>
Your company has reduced CO<sub>2</sub>e Emissions by{' '}
<Emphasis>{carbonSavingsPercentFormatted}%</Emphasis>{' '}
</Typography>
}
/>
</ListItem>
<ListItem>
<ListItemIcon>
<InfoIcon />
</ListItemIcon>
<ListItemText
primary={
<Typography variant={'h5'}>
You have spent{' '}<Emphasis>${gameTotalNetCostFormatted}</Emphasis>{' '} throughout the game.
</Typography>
}
/>
</ListItem>
<ListItem>
<ListItemIcon>
<InfoIcon />
</ListItemIcon>
<ListItemText
primary={
<Typography variant={'h5'}>
Your cost per kg reduced was{' '}<Emphasis>${costPerCarbonSavingsFormatted}/kg CO<sub>2</sub>e</Emphasis>{' '}
</Typography>
}
/>
</ListItem>
{endOfGameResults.projectedFinancedSpending > 0 &&
<ListItem >
<ListItemIcon>
<InfoIcon />
</ListItemIcon>
<ListItemText
primary={
<Typography variant={'h5'}>
You are projected to spend {' '}<Emphasis>${projectedFinancedSpendingFormatted}</Emphasis>{' '} on financed and renewed projects in future years
</Typography>
}
/>
</ListItem>
}
</List>
</Box>
</Box>

<Box sx={{ display: 'flex', justifyContent: 'end', marginY: '1rem' }}>
<DownloadPDF yearRangeInitialStats={props.yearRangeInitialStats}
completedProjects={props.completedProjects}
Expand Down Expand Up @@ -106,7 +192,10 @@ function EndGameReport(props: ReportProps) {
}

interface ReportProps extends ReportPDFProps {
mutableStats: TrackedStats
mutableStats: TrackedStats,
defaultTrackedStats: TrackedStats,
implementedFinancedProjects: ImplementedProject[],
renewableProjects: RenewableProject[]
}


Expand Down Expand Up @@ -334,6 +423,7 @@ export interface EndGameReportPageProps extends
GameSettings {
capitalFundingState: CapitalFundingState,
trackedStats: TrackedStats,
defaultTrackedStats: TrackedStats,
completedProjects: CompletedProject[];
implementedRenewableProjects: RenewableProject[];
implementedFinancedProjects: ImplementedProject[];
Expand Down
2 changes: 0 additions & 2 deletions src/components/EnergyUseLineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ export default function EnergyUseLineChart(props: EnergyUseLineChartProps) {
landfillGases: [],
}
props.yearRangeInitialStats.forEach(statYear => {
// 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;
Expand Down
Loading

0 comments on commit 6779dbc

Please sign in to comment.