diff --git a/GAMEPLAY.md b/GAMEPLAY.md index e3cb1e5..24e5b19 100644 --- a/GAMEPLAY.md +++ b/GAMEPLAY.md @@ -5,6 +5,7 @@ ## General - The game is played in 1 or 2 year budget periods (intervals) + - Project info cards will always display the annual cost - Players may move backwards a single budget period to make changes. - Players receive additional budget at each year start: 1: 75_000, @@ -18,7 +19,7 @@ 9: 112_500, 10: 112_500 -- Yearly emissions factor defaults are used to calculate emissions. For 2 year gameplay the later year is used (i.e. year 4 in 3/4) +- Yearly emissions factor defaults are used to calculate emissions. For 2 year gameplay the later year is used (i.e. year 4 in 3/4). 1: .371, 2: .358, 3: .324, @@ -101,13 +102,10 @@ All normal projects are now added to `implementedFinancedProjects` and `implemen Capital Funding pays projects in full and has it's own state object, but is also considered a financing type so that the feature can follow the app's pattern for project implementation. -#### Opportunities +#### Game Gotcha's / Quirks +**!!!!** For 2 year gameplay the later emissions factor year is used (i.e. year 4 in 3/4). This leads to slightly different emissions calculations given the same inputs -Implemented projects are being added to a number of arrays to track state between years, including implementedProjectIds, implementedFinancedProjects, implementedRenewableProjects, and so on. This was done to stay within existing app patterns. We should refactor for a single source of truth where implemented project objects have knowledge of their own state. -#### TODO -- gameYearsImplemented - renewable projects cannot be unimplemented after first year so we shouldn't need this anymore - diff --git a/src/App.tsx b/src/App.tsx index 671dae1..73682a7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -568,7 +568,7 @@ export class App extends React.PureComponent { console.log('Game Total Spending', newYearTrackedStats.gameTotalSpending); console.log('======== END ================='); - this.setState({ + let nextState = { completedProjects: newCompletedProjects, completedYears: completedYears, implementedProjectsIds: [], @@ -579,18 +579,30 @@ export class App extends React.PureComponent { yearRangeInitialStats: newYearRangeInitialStats, capitalFundingState: newCapitalFundingState, yearlyCostSavings: yearlyCostSavings - }); + }; - if (newYearTrackedStats.carbonSavingsPercent >= 0.5) { - this.setPage(Pages.winScreen); - } else if (newYearTrackedStats.currentGameYear === this.state.gameSettings.totalGameYears + 1) { - this.setPage(Pages.loseScreen); + const isGameWon = newYearTrackedStats.carbonSavingsPercent >= 0.5; + const isEndOfGame = newYearTrackedStats.currentGameYear === this.state.gameSettings.totalGameYears + 1 + + if (isGameWon) { + this.endGame(Pages.winScreen, newYearTrackedStats, nextState); + } else if (isEndOfGame) { + this.endGame(Pages.loseScreen, newYearTrackedStats, nextState); } else { + this.setState(nextState); this.setPage(Pages.scope1Projects); } } + endGame(page: symbol, newYearTrackedStats: TrackedStats, nextState) { + // Game is over - don't advance year + newYearTrackedStats.currentGameYear -= 1; + nextState.trackedStats = newYearTrackedStats; + this.setState(nextState); + this.setPage(page); + } + setNewYearStats(newYearTrackedStats: TrackedStats, newBudget: number, currentYearStats: TrackedStats) { newYearTrackedStats.yearBudget = newBudget; console.log('Total new year budget: ', newBudget); diff --git a/src/components/EndGameReport/EndGameReportPage.tsx b/src/components/EndGameReport/EndGameReportPage.tsx index 496ed95..de80a84 100644 --- a/src/components/EndGameReport/EndGameReportPage.tsx +++ b/src/components/EndGameReport/EndGameReportPage.tsx @@ -18,10 +18,10 @@ import InfoIcon from '@mui/icons-material/Info'; export default class EndGameReportPage extends React.Component { render() { const yearRangeInitialStats: TrackedStats[] = [...this.props.yearRangeInitialStats]; - const mutableStats: TrackedStats = { ...this.props.yearRangeInitialStats[this.props.trackedStats.currentGameYear - 1] }; + const year10Stats: TrackedStats = { ...this.props.trackedStats} let completedRenewables = [...this.props.implementedRenewableProjects].map(renewable => { return { - completedYear: mutableStats.currentGameYear - 1, + completedYear: year10Stats.currentGameYear - 1, gameYearsImplemented: renewable.gameYearsImplemented, yearStarted: renewable.yearStarted, financingOption: renewable.financingOption, @@ -37,7 +37,7 @@ export default class EndGameReportPage extends React.Component @@ -54,17 +54,17 @@ function EndGameReport(props: ReportProps) { let isFinanced = implementationFinancing.financingType.id !== 'budget'; let financingData: ImplementedFinancingData = { option: implementationFinancing, - isPaidOff: isFinanced ? isProjectFullyFunded(project, props.mutableStats) : false, + isPaidOff: isFinanced ? isProjectFullyFunded(project, props.year10Stats) : false, isFinanced: isFinanced, } - let projectNetCost = implementedProject.getYearEndTotalSpending(financingData.option, props.mutableStats.gameYearInterval); + let projectNetCost = implementedProject.getYearEndTotalSpending(financingData.option, props.year10Stats.gameYearInterval); let totalProjectExtraCosts = implementedProject.getHiddenCost(); projectRecapCards.push( getProjectCard( implementedProject, - props.mutableStats, + props.year10Stats, projectNetCost, totalProjectExtraCosts, financingData, @@ -72,18 +72,18 @@ 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); - + // * sub year to get projections + props.year10Stats.currentGameYear - 1; + let projectedFinancedSpending = getProjectedFinancedSpending(props.implementedFinancedProjects, props.renewableProjects, props.year10Stats); + let gameCurrentAndProjectedSpending = props.year10Stats.gameTotalSpending + projectedFinancedSpending; + setCarbonEmissionsAndSavings(props.year10Stats, props.defaultTrackedStats); + setCostPerCarbonSavings(props.year10Stats, gameCurrentAndProjectedSpending); let endOfGameResults = { - carbonSavingsPercent: props.mutableStats.carbonSavingsPercent, - gameTotalSpending: props.mutableStats.gameTotalSpending, + carbonSavingsPercent: props.year10Stats.carbonSavingsPercent, + gameTotalSpending: props.year10Stats.gameTotalSpending, projectedFinancedSpending: projectedFinancedSpending, gameCurrentAndProjectedSpending: gameCurrentAndProjectedSpending, - costPerCarbonSavings: props.mutableStats.costPerCarbonSavings + costPerCarbonSavings: props.year10Stats.costPerCarbonSavings } const noDecimalsFormatter = Intl.NumberFormat('en-US', { minimumFractionDigits: 0, @@ -92,28 +92,13 @@ function EndGameReport(props: ReportProps) { const carbonSavingsPercentFormatted: string = (endOfGameResults.carbonSavingsPercent * 100).toFixed(2); const gameTotalNetCostFormatted: string = noDecimalsFormatter.format(endOfGameResults.gameTotalSpending); const projectedFinancedSpendingFormatted: string = noDecimalsFormatter.format(endOfGameResults.projectedFinancedSpending); - const gameCurrentAndProjectedSpendingFormatted: string = noDecimalsFormatter.format(endOfGameResults.gameCurrentAndProjectedSpending); const costPerCarbonSavingsFormatted: string = endOfGameResults.costPerCarbonSavings !== undefined ? Intl.NumberFormat('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 2, }).format(endOfGameResults.costPerCarbonSavings) : '0'; - - console.log('props.mutableStats.currentGameYear '+ props.mutableStats.currentGameYear); - console.log('props.mutableStats.gameYearInterval '+ props.mutableStats.gameYearInterval); - console.log('props.mutableStats.gameYearDisplayOffset '+ props.mutableStats.gameYearDisplayOffset); - + return ( - {props.mutableStats.gameYearInterval == 1 && - - Looking Forward - Year {props.mutableStats.currentGameYear} - - } - {props.mutableStats.gameYearInterval == 2 && - - Looking Forward - Years {props.mutableStats.gameYearDisplayOffset} & {props.mutableStats.gameYearDisplayOffset + 1} - - } {(parent) => ( - You have spent{' '}${gameTotalNetCostFormatted}{' '} on GHG reduction measures. + You have spent{' '}${gameTotalNetCostFormatted}{' '} throughout the game. } /> @@ -169,7 +154,7 @@ function EndGameReport(props: ReportProps) { - After this budget period, you still have {' '}${gameCurrentAndProjectedSpendingFormatted}{' '} on financed and renewable projects in the coming years. + You are projected to spend {' '}${projectedFinancedSpendingFormatted}{' '} on financed and renewed projects in future years } /> @@ -207,7 +192,7 @@ function EndGameReport(props: ReportProps) { } interface ReportProps extends ReportPDFProps { - mutableStats: TrackedStats, + year10Stats: TrackedStats, defaultTrackedStats: TrackedStats, implementedFinancedProjects: ImplementedProject[], renewableProjects: RenewableProject[] @@ -215,7 +200,7 @@ interface ReportProps extends ReportPDFProps { function getProjectCard(implementedProject: ProjectControl, - mutableStats: TrackedStats, + year10Stats: TrackedStats, projectNetCost: number, totalExtraCosts: number, financingData: ImplementedFinancingData @@ -237,7 +222,7 @@ function getProjectCard(implementedProject: ProjectControl, let yearMultiplier = 1; if (implementedProject.isRenewable) { - yearMultiplier = mutableStats.gameYearInterval; + yearMultiplier = year10Stats.gameYearInterval; } let initialCost = implementedProject.financedAnnualCost ? implementedProject.financedAnnualCost : implementedProject.baseCost; initialCost *= yearMultiplier; diff --git a/src/components/EnergyUseLineChart.tsx b/src/components/EnergyUseLineChart.tsx index 66c608c..78bf45e 100644 --- a/src/components/EnergyUseLineChart.tsx +++ b/src/components/EnergyUseLineChart.tsx @@ -5,9 +5,14 @@ import React from 'react'; export default function EnergyUseLineChart(props: EnergyUseLineChartProps) { const data = []; - let xYears: number[] = Array.from([...props.yearRangeInitialStats], statYear => statYear.currentGameYear); + const yearlyStats = [...props.yearRangeInitialStats] + // remove end of game year + yearlyStats.pop() + let xYears: number[] = Array.from(yearlyStats, statYear => { + return statYear.currentGameYear; + }); let xTicks: string[] = Array.from(xYears, year => { - return 'Budget Period ' + String(year); + return 'Year ' + String(year * props.yearRangeInitialStats[0].gameYearInterval); }); let energyTypeYearValues = { @@ -15,7 +20,7 @@ export default function EnergyUseLineChart(props: EnergyUseLineChartProps) { naturalGas: [], landfillGases: [], } - props.yearRangeInitialStats.forEach(statYear => { + yearlyStats.forEach(statYear => { let electricityEmissions = statYear.electricityUseKWh * getElectricityEmissionsFactor(statYear.currentGameYear, statYear.gameYearInterval, statYear.gameYearDisplayOffset); let natGasEmissions = statYear.naturalGasMMBTU * statYear.naturalGasEmissionsPerMMBTU; let landfillGasEmissions = statYear.hydrogenMMBTU * statYear.hydrogenEmissionsPerMMBTU; @@ -65,7 +70,7 @@ export default function EnergyUseLineChart(props: EnergyUseLineChartProps) { const layout = { width: props.parentElement.width, title: { - text: `GHG Reductions through Budget Period ${xYears.length}`, + text: `${xYears.length * props.yearRangeInitialStats[0].gameYearInterval} Year GHG Reduction`, font: { family: 'Roboto', size: 24 diff --git a/src/trackedStats.tsx b/src/trackedStats.tsx index 0d36716..44b01da 100644 --- a/src/trackedStats.tsx +++ b/src/trackedStats.tsx @@ -161,14 +161,15 @@ export const initialTrackedStats: TrackedStats = { initialTrackedStats.carbonEmissions = calculateEmissions(initialTrackedStats); export function getElectricityEmissionsFactor(currentGameYear: number, gameYearInterval: number, gameYearDisplayOffset: number): number { - let isEndOfGame = gameYearInterval > 1? currentGameYear > 5 : currentGameYear > 10; + let isEndOfGame = gameYearInterval > 1? gameYearDisplayOffset >= 10 : currentGameYear > 10; let year = currentGameYear; if (isEndOfGame) { year = 10; } else if (gameYearInterval > 1) { year = gameYearDisplayOffset + 1; } - return ElectricityEmissionsFactors[year]; + let emissionsFactor = ElectricityEmissionsFactors[year]; + return emissionsFactor; } export function calculateEmissions(stats: TrackedStats): number {