diff --git a/distribution/changelog.txt b/distribution/changelog.txt index f7cbe597a8..1a4496359b 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,8 +1,11 @@ 0.4.14 (in development) ------------------------------------------------------------------------ - Feature: [#15750] Allow using different types of park entrance in one park. +- Feature: [#22414] Finance graphs can be resized. - Change: [#21659] Increase the Hybrid Roller Coaster’s maximum lift speed to 17 km/h (11 mph). - Change: [#22466] The Clear Scenery tool now uses a bulldozer cursor instead of a generic crosshair. +- Fix: [#22307] Hover tooltips in financial charts are not invalidated properly. +- Fix: [#22395, #22396] Misaligned tick marks in financial and guest count graphs (original bug). 0.4.13 (2024-08-04) ------------------------------------------------------------------------ diff --git a/src/openrct2-ui/interface/Graph.cpp b/src/openrct2-ui/interface/Graph.cpp index 8d146cd3a4..b67f35cdaf 100644 --- a/src/openrct2-ui/interface/Graph.cpp +++ b/src/openrct2-ui/interface/Graph.cpp @@ -10,282 +10,189 @@ #include "../UiStringIds.h" #include -#include #include #include +#include #include - -using namespace OpenRCT2; +#include namespace OpenRCT2::Graph { - static void DrawMonths(DrawPixelInfo& dpi, const uint8_t* history, int32_t count, const ScreenCoordsXY& origCoords) + constexpr int32_t kDashLength = 2; + + template + static void DrawYLabels( + DrawPixelInfo& dpi, const ScreenRect& internalBounds, const T min, const T max, const int32_t numYLabels, + const int32_t yLabelStepPx, const ColourWithFlags lineCol, const FmtString& fmt) + { + T curLabel = max; + int32_t curScreenPos = internalBounds.GetTop() - 5; + const T yLabelStep = (max - min) / (numYLabels - 1); + for (int32_t i = 0; i < numYLabels; i++) + { + char buffer[64]{}; + FormatStringToBuffer(buffer, sizeof(buffer), fmt, curLabel); + DrawText(dpi, { internalBounds.GetLeft(), curScreenPos }, { FontStyle::Small, TextAlignment::RIGHT }, buffer); + GfxFillRectInset( + dpi, { { internalBounds.GetLeft(), curScreenPos + 5 }, { internalBounds.GetRight(), curScreenPos + 5 } }, + lineCol, INSET_RECT_FLAG_BORDER_INSET); + curScreenPos += yLabelStepPx; + curLabel -= yLabelStep; + } + } + + template + static void DrawMonths(DrawPixelInfo& dpi, const T* series, int32_t count, const ScreenRect& bounds, const int32_t xStep) { auto& date = GetDate(); int32_t currentMonth = date.GetMonth(); int32_t currentDay = date.GetMonthTicks(); int32_t yearOver32 = (currentMonth * 4) + (currentDay >> 14) - 31; - auto screenCoords = origCoords; + auto screenCoords = bounds.Point1; for (int32_t i = count - 1; i >= 0; i--) { - if (history[i] != 255 && yearOver32 % 4 == 0) - { - // Draw month text - auto ft = Formatter(); - ft.Add(DateGameShortMonthNames[DateGetMonth((yearOver32 / 4) + MONTH_COUNT)]); - DrawTextBasic( - dpi, screenCoords - ScreenCoordsXY{ 0, 10 }, STR_GRAPH_LABEL, ft, - { FontStyle::Small, TextAlignment::CENTRE }); - - // Draw month mark - GfxFillRect(dpi, { screenCoords, screenCoords + ScreenCoordsXY{ 0, 3 } }, PALETTE_INDEX_10); - } - - yearOver32 = (yearOver32 + 1) % 32; - screenCoords.x += 6; - } - } - - static void DrawLineA(DrawPixelInfo& dpi, const uint8_t* history, int32_t count, const ScreenCoordsXY& origCoords) - { - auto lastCoords = ScreenCoordsXY{ -1, -1 }; - auto coords = origCoords; - for (int32_t i = count - 1; i >= 0; i--) - { - if (history[i] != 255) - { - coords.y = origCoords.y + ((255 - history[i]) * 100) / 256; - - if (lastCoords.x != -1) - { - auto leftTop1 = lastCoords + ScreenCoordsXY{ 1, 1 }; - auto rightBottom1 = coords + ScreenCoordsXY{ 1, 1 }; - auto leftTop2 = lastCoords + ScreenCoordsXY{ 0, 1 }; - auto rightBottom2 = coords + ScreenCoordsXY{ 0, 1 }; - GfxDrawLine(dpi, { leftTop1, rightBottom1 }, PALETTE_INDEX_10); - GfxDrawLine(dpi, { leftTop2, rightBottom2 }, PALETTE_INDEX_10); - } - if (i == 0) - GfxFillRect(dpi, { coords, coords + ScreenCoordsXY{ 2, 2 } }, PALETTE_INDEX_10); - - lastCoords = coords; - } - coords.x += 6; - } - } - - static void DrawLineB(DrawPixelInfo& dpi, const uint8_t* history, int32_t count, const ScreenCoordsXY& origCoords) - { - auto lastCoords = ScreenCoordsXY{ -1, -1 }; - auto coords = origCoords; - for (int32_t i = count - 1; i >= 0; i--) - { - if (history[i] != 255) - { - coords.y = origCoords.y + ((255 - history[i]) * 100) / 256; - - if (lastCoords.x != -1) - { - auto leftTop = lastCoords; - auto rightBottom = coords; - GfxDrawLine(dpi, { leftTop, rightBottom }, PALETTE_INDEX_21); - } - if (i == 0) - GfxFillRect(dpi, { coords - ScreenCoordsXY{ 1, 1 }, coords + ScreenCoordsXY{ 1, 1 } }, PALETTE_INDEX_21); - - lastCoords = coords; - } - coords.x += 6; - } - } - - void Draw(DrawPixelInfo& dpi, uint8_t* history, int32_t count, const ScreenCoordsXY& screenPos) - { - DrawMonths(dpi, history, count, screenPos); - DrawLineA(dpi, history, count, screenPos); - DrawLineB(dpi, history, count, screenPos); - } -} // namespace OpenRCT2::Graph - -struct FinancialTooltipInfo -{ - const ScreenCoordsXY coords; - const money64 money{}; -}; - -static constexpr auto ChartMaxDataCount = 64; -static constexpr auto ChartMaxIndex = ChartMaxDataCount - 1; -static constexpr auto ChartDataWidth = 6; -static constexpr auto ChartMaxWidth = ChartMaxIndex * ChartDataWidth; -static constexpr auto ChartMaxHeight = 164; -static constexpr auto CursorXOffset = 3; -static constexpr auto DefaultDashedLength = 2; - -static int32_t IndexForCursorAndHistory(const int32_t historyCount, const int32_t cursorX, const int32_t chartX) -{ - const auto offsettedCursorX = cursorX + CursorXOffset; - return (historyCount - 1) - (offsettedCursorX - chartX) / ChartDataWidth; -} - -static const ScreenCoordsXY ScreenCoordsForHistoryIndex( - const int32_t index, const money64* history, const int32_t chartX, const int32_t chartY, const int32_t modifier, - const int32_t offset) -{ - auto coords = ScreenCoordsXY{ chartX + ChartDataWidth * (ChartMaxIndex - index), - chartY + ChartMaxHeight - - (((static_cast(history[index] >> modifier) + offset) * 170) / 256) }; - return coords; -} - -static const FinancialTooltipInfo FinanceTooltipInfoFromMoney( - const money64* history, const int32_t historyCount, const int32_t modifier, const int32_t offset, - const ScreenRect& chartFrame, const ScreenCoordsXY& cursorPosition) -{ - if (!chartFrame.Contains(cursorPosition)) - { - return { {}, kMoney64Undefined }; - } - - const auto historyIndex = IndexForCursorAndHistory(historyCount, cursorPosition.x, chartFrame.GetLeft()); - const auto coords = ScreenCoordsForHistoryIndex( - historyIndex, history, chartFrame.GetLeft(), chartFrame.GetTop(), modifier, offset); - - return { coords, history[historyIndex] }; -} - -namespace OpenRCT2::Graph -{ - static void DrawMonths(DrawPixelInfo& dpi, const money64* history, int32_t count, const ScreenCoordsXY& origCoords) - { - auto& date = GetDate(); - int32_t currentMonth = date.GetMonth(); - int32_t currentDay = date.GetMonthTicks(); - int32_t yearOver32 = (currentMonth * 4) + (currentDay >> 14) - 31; - auto screenCoords = origCoords; - for (int32_t i = count - 1; i >= 0; i--) - { - if (history[i] != kMoney64Undefined && yearOver32 % 4 == 0) + if (series[i] != TkNoValue && yearOver32 % 4 == 0) { // Draw month text auto ft = Formatter(); ft.Add(DateGameShortMonthNames[DateGetMonth((yearOver32 / 4) + MONTH_COUNT)]); DrawTextBasic( - dpi, screenCoords - ScreenCoordsXY{ 0, 10 }, STR_GRAPH_LABEL, ft, + dpi, screenCoords - ScreenCoordsXY{ 0, 14 }, STR_GRAPH_LABEL, ft, { FontStyle::Small, TextAlignment::CENTRE }); // Draw month mark - GfxFillRect(dpi, { screenCoords, screenCoords + ScreenCoordsXY{ 0, 3 } }, PALETTE_INDEX_10); + GfxFillRect( + dpi, { screenCoords - ScreenCoordsXY{ 0, 4 }, screenCoords - ScreenCoordsXY{ 0, 1 } }, PALETTE_INDEX_10); } yearOver32 = (yearOver32 + 1) % 32; - screenCoords.x += 6; - } - } - - static void DrawLineA( - DrawPixelInfo& dpi, const money64* history, int32_t count, const ScreenCoordsXY& origCoords, int32_t modifier, - int32_t offset) - { - ScreenCoordsXY lastCoords; - bool lastCoordsValid = false; - auto coords = origCoords; - for (int32_t i = count - 1; i >= 0; i--) - { - if (history[i] != kMoney64Undefined) - { - coords.y = origCoords.y + 170 - 6 - ((((history[i] >> modifier) + offset) * 170) / 256); - - if (lastCoordsValid) - { - auto leftTop1 = lastCoords + ScreenCoordsXY{ 1, 1 }; - auto rightBottom1 = coords + ScreenCoordsXY{ 1, 1 }; - auto leftTop2 = lastCoords + ScreenCoordsXY{ 0, 1 }; - auto rightBottom2 = coords + ScreenCoordsXY{ 0, 1 }; - GfxDrawLine(dpi, { leftTop1, rightBottom1 }, PALETTE_INDEX_10); - GfxDrawLine(dpi, { leftTop2, rightBottom2 }, PALETTE_INDEX_10); - } - if (i == 0) - GfxFillRect(dpi, { coords, coords + ScreenCoordsXY{ 2, 2 } }, PALETTE_INDEX_10); - - lastCoords = coords; - lastCoordsValid = true; - } - coords.x += 6; - } - } - - static void DrawLineB( - DrawPixelInfo& dpi, const money64* history, int32_t count, const ScreenCoordsXY& origCoords, int32_t modifier, - int32_t offset) - { - ScreenCoordsXY lastCoords; - bool lastCoordsValid = false; - auto coords = origCoords; - for (int32_t i = count - 1; i >= 0; i--) - { - if (history[i] != kMoney64Undefined) - { - coords.y = origCoords.y + 170 - 6 - ((((history[i] >> modifier) + offset) * 170) / 256); - - if (lastCoordsValid) - { - auto leftTop = lastCoords; - auto rightBottom = coords; - GfxDrawLine(dpi, { leftTop, rightBottom }, PALETTE_INDEX_21); - } - if (i == 0) - GfxFillRect(dpi, { coords - ScreenCoordsXY{ 1, 1 }, coords + ScreenCoordsXY{ 1, 1 } }, PALETTE_INDEX_21); - - lastCoords = coords; - lastCoordsValid = true; - } - coords.x += 6; + screenCoords.x += xStep; } } + template static void DrawHoveredValue( - DrawPixelInfo& dpi, const money64* history, const int32_t historyCount, const ScreenCoordsXY& screenCoords, - const int32_t modifier, const int32_t offset) + DrawPixelInfo& dpi, const T value, const int32_t hoverIdx, const ScreenRect& bounds, const int32_t xStep, + const T minValue, const T maxValue, const_utf8string text, ColourWithFlags textCol) { - const auto cursorPosition = ContextGetCursorPositionScaled(); - const ScreenRect chartFrame{ screenCoords, screenCoords + ScreenCoordsXY{ ChartMaxWidth, ChartMaxHeight } }; + const int32_t screenRange = bounds.GetHeight(); + const int32_t valueRange = maxValue - minValue; - if (!chartFrame.Contains(cursorPosition)) - { - return; - } + const int32_t yPosition = bounds.GetBottom() - ((value - minValue) * screenRange) / valueRange; + ScreenCoordsXY coords = { bounds.GetRight() - hoverIdx * xStep, yPosition }; - const auto info = FinanceTooltipInfoFromMoney(history, ChartMaxDataCount, modifier, offset, chartFrame, cursorPosition); + GfxDrawDashedLine( + dpi, + { + { coords.x, bounds.GetTop() }, + { coords.x, bounds.GetBottom() }, + }, + kDashLength, PALETTE_INDEX_10); + GfxDrawDashedLine(dpi, { { bounds.GetLeft(), coords.y }, coords }, kDashLength, PALETTE_INDEX_10); - if (info.money == kMoney64Undefined) - { - return; - } - GfxDrawDashedLine(dpi, { { info.coords.x, chartFrame.GetTop() }, info.coords }, DefaultDashedLength, 0); - GfxDrawDashedLine(dpi, { { chartFrame.GetLeft() - 10, info.coords.y }, info.coords }, DefaultDashedLength, 0); + Formatter ft; + ft.Add(value); + DrawText(dpi, coords - ScreenCoordsXY{ 0, 16 }, { textCol, TextAlignment::CENTRE }, text); - if (cursorPosition.y > info.coords.y) - { - GfxDrawDashedLine(dpi, { info.coords, { info.coords.x, cursorPosition.y } }, DefaultDashedLength, 0); - } - - auto ft = Formatter(); - ft.Add(info.money); - DrawTextBasic( - dpi, info.coords - ScreenCoordsXY{ 0, 16 }, STR_FINANCES_SUMMARY_EXPENDITURE_VALUE, ft, { TextAlignment::CENTRE }); - - GfxFillRect(dpi, { { info.coords - ScreenCoordsXY{ 2, 2 } }, info.coords + ScreenCoordsXY{ 2, 2 } }, PALETTE_INDEX_10); - GfxFillRect( - dpi, { { info.coords - ScreenCoordsXY{ 1, 1 } }, { info.coords + ScreenCoordsXY{ 1, 1 } } }, PALETTE_INDEX_21); + GfxFillRect(dpi, { { coords - ScreenCoordsXY{ 2, 2 } }, coords + ScreenCoordsXY{ 2, 2 } }, PALETTE_INDEX_10); + GfxFillRect(dpi, { { coords - ScreenCoordsXY{ 1, 1 } }, { coords + ScreenCoordsXY{ 1, 1 } } }, PALETTE_INDEX_21); } - void Draw( - DrawPixelInfo& dpi, const money64* history, const int32_t count, const ScreenCoordsXY& screenCoords, - const int32_t modifier, const int32_t offset) + template + static void DrawLine( + DrawPixelInfo& dpi, const T* series, const int32_t count, const ScreenRect& bounds, const int32_t xStep, + const T minValue, const T maxValue) { - DrawMonths(dpi, history, count, screenCoords); - DrawLineA(dpi, history, count, screenCoords, modifier, offset); - DrawLineB(dpi, history, count, screenCoords, modifier, offset); - DrawHoveredValue(dpi, history, count, screenCoords, modifier, offset); + const int32_t screenRange = bounds.GetHeight(); + const int32_t valueRange = maxValue - minValue; + + ScreenCoordsXY lastCoords; + bool lastCoordsValid = false; + ScreenCoordsXY coords = bounds.Point1; + for (int32_t i = count - 1; i >= 0; i--) + { + auto value = series[i]; + if (value != TkNoValue) + { + coords.y = bounds.GetBottom() - ((value - minValue) * screenRange) / valueRange; + + if constexpr (TbackgroundLine) + { + if (lastCoordsValid) + { + auto leftTop1 = lastCoords + ScreenCoordsXY{ 1, 1 }; + auto rightBottom1 = coords + ScreenCoordsXY{ 1, 1 }; + auto leftTop2 = lastCoords + ScreenCoordsXY{ 0, 1 }; + auto rightBottom2 = coords + ScreenCoordsXY{ 0, 1 }; + GfxDrawLine(dpi, { leftTop1, rightBottom1 }, PALETTE_INDEX_10); + GfxDrawLine(dpi, { leftTop2, rightBottom2 }, PALETTE_INDEX_10); + } + if (i == 0) + { + GfxFillRect(dpi, { coords, coords + ScreenCoordsXY{ 2, 2 } }, PALETTE_INDEX_10); + } + } + else + { + if (lastCoordsValid) + { + auto leftTop = lastCoords; + auto rightBottom = coords; + GfxDrawLine(dpi, { leftTop, rightBottom }, PALETTE_INDEX_21); + } + if (i == 0) + { + GfxFillRect( + dpi, { coords - ScreenCoordsXY{ 1, 1 }, coords + ScreenCoordsXY{ 1, 1 } }, PALETTE_INDEX_21); + } + } + + lastCoords = coords; + lastCoordsValid = true; + } + coords.x += xStep; + } + } + + void DrawFinanceGraph(DrawPixelInfo& dpi, const GraphProperties& p) + { + const FmtString fmt("{BLACK}{CURRENCY2DP} -"); + DrawYLabels(dpi, p.internalBounds, p.min, p.max, p.numYLabels, p.yLabelStepPx, p.lineCol, fmt); + DrawMonths(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx); + DrawLine(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx, p.min, p.max); + DrawLine(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx, p.min, p.max); + if (p.hoverIdx >= 0 && p.hoverIdx < p.numPoints) + { + const money64 value = p.series[p.hoverIdx]; + if (value != kMoney64Undefined) + { + char buffer[64]{}; + FormatStringToBuffer(buffer, sizeof(buffer), "{CURRENCY2DP}", value); + DrawHoveredValue( + dpi, value, p.hoverIdx, p.internalBounds, p.xStepPx, p.min, p.max, buffer, + p.lineCol.withFlag(ColourFlag::withOutline, true)); + } + } + } + + void DrawRatingGraph(DrawPixelInfo& dpi, const GraphProperties& p) + { + constexpr uint8_t noValue = ParkRatingHistoryUndefined; + const FmtString fmt("{BLACK}{COMMA32} -"); + // Since the park rating rating history is divided by 4, we have to fudge the max number here. + DrawYLabels(dpi, p.internalBounds, p.min, 1000, p.numYLabels, p.yLabelStepPx, p.lineCol, fmt); + DrawMonths(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx); + DrawLine(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx, p.min, p.max); + DrawLine(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx, p.min, p.max); + } + + void DrawGuestGraph(DrawPixelInfo& dpi, const GraphProperties& p) + { + constexpr uint32_t noValue = GuestsInParkHistoryUndefined; + const FmtString fmt("{BLACK}{COMMA32} -"); + DrawYLabels(dpi, p.internalBounds, p.min, p.max, p.numYLabels, p.yLabelStepPx, p.lineCol, fmt); + DrawMonths(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx); + DrawLine(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx, p.min, p.max); + DrawLine(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx, p.min, p.max); } } // namespace OpenRCT2::Graph diff --git a/src/openrct2-ui/interface/Graph.h b/src/openrct2-ui/interface/Graph.h index 2c3c8fe425..411a2a8b38 100644 --- a/src/openrct2-ui/interface/Graph.h +++ b/src/openrct2-ui/interface/Graph.h @@ -9,14 +9,62 @@ #pragma once +#include #include #include #include namespace OpenRCT2::Graph { - void Draw(DrawPixelInfo& dpi, uint8_t* history, int32_t count, const ScreenCoordsXY& screenPos); - void Draw( - DrawPixelInfo& dpi, const money64* history, const int32_t count, const ScreenCoordsXY& coords, const int32_t modifier, - const int32_t offset); + template struct GraphProperties + { + ScreenRect internalBounds; + const T* series; + T min; + T max; + int32_t hoverIdx; + int32_t numPoints; + int32_t numYLabels; + int32_t xStepPx; + int32_t yLabelStepPx; + ColourWithFlags lineCol; + + void RecalculateLayout(const ScreenRect newBounds, const int32_t newNumYLabels, const int32_t newNumPoints) + { + yLabelStepPx = (newBounds.GetBottom() - newBounds.GetTop()) / (newNumYLabels - 1); + xStepPx = (newBounds.GetRight() - newBounds.GetLeft()) / (newNumPoints - 1); + // adjust bounds to be exact multiples of the steps. + internalBounds = { newBounds.Point1, + newBounds.Point1 + + ScreenCoordsXY{ xStepPx * (newNumPoints - 1), yLabelStepPx * (newNumYLabels - 1) } }; + numPoints = newNumPoints; + numYLabels = newNumYLabels; + } + + bool UpdateHoverIndex() + { + const ScreenCoordsXY cursorPos = ContextGetCursorPositionScaled(); + + int32_t i = -1; + if (internalBounds.Contains(cursorPos)) + { + i = (numPoints - 1) - (cursorPos.x - internalBounds.GetLeft() + (xStepPx / 2)) / xStepPx; + if (i < 0) + i = 1; + if (i > numPoints - 1) + i = numPoints - 1; + } + + if (hoverIdx != i) + { + hoverIdx = i; + return true; + } + return false; + } + }; + + void DrawFinanceGraph(DrawPixelInfo& dpi, const GraphProperties& p); + void DrawRatingGraph(DrawPixelInfo& dpi, const GraphProperties& p); + void DrawGuestGraph(DrawPixelInfo& dpi, const GraphProperties& p); } // namespace OpenRCT2::Graph diff --git a/src/openrct2-ui/windows/Finances.cpp b/src/openrct2-ui/windows/Finances.cpp index c879a6484b..8de32b5e06 100644 --- a/src/openrct2-ui/windows/Finances.cpp +++ b/src/openrct2-ui/windows/Finances.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -178,6 +177,8 @@ static Widget _windowFinancesResearchWidgets[] = #pragma endregion +#pragma region Constants + static constexpr StringId _windowFinancesSummaryRowLabels[EnumValue(ExpenditureType::Count)] = { STR_FINANCES_SUMMARY_RIDE_CONSTRUCTION, STR_FINANCES_SUMMARY_RIDE_RUNNING_COSTS, @@ -218,12 +219,19 @@ static Widget _windowFinancesResearchWidgets[] = }; static_assert(std::size(_windowFinancesPageHoldDownWidgets) == WINDOW_FINANCES_PAGE_COUNT); + static constexpr ScreenCoordsXY kGraphTopLeftPadding{ 88, 20 }; + static constexpr ScreenCoordsXY kGraphBottomRightPadding{ 15, 18 }; + static constexpr uint8_t kGraphNumYLabels = 5; + static constexpr int32_t kGraphNumPoints = 64; + #pragma endregion class FinancesWindow final : public Window { private: - uint32_t _lastPaintedMonth; + uint32_t _lastPaintedMonth = std::numeric_limits::max(); + ScreenRect _graphBounds; + Graph::GraphProperties _graphProps{}; void SetDisabledTabs() { @@ -236,12 +244,22 @@ static Widget _windowFinancesResearchWidgets[] = SetPage(WINDOW_FINANCES_PAGE_SUMMARY); _lastPaintedMonth = std::numeric_limits::max(); ResearchUpdateUncompletedTypes(); + _graphProps.hoverIdx = -1; } void OnUpdate() override { frame_no++; InvalidateWidget(WIDX_TAB_1 + page); + + if (page == WINDOW_FINANCES_PAGE_VALUE_GRAPH || page == WINDOW_FINANCES_PAGE_PROFIT_GRAPH + || page == WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH) + { + if (_graphProps.UpdateHoverIndex()) + { + InvalidateWidget(WIDX_BACKGROUND); + } + } } void OnMouseDown(WidgetIndex widgetIndex) override @@ -319,6 +337,34 @@ static Widget _windowFinancesResearchWidgets[] = case WINDOW_FINANCES_PAGE_RESEARCH: WindowResearchFundingPrepareDraw(this, WIDX_RESEARCH_FUNDING); break; + case WINDOW_FINANCES_PAGE_VALUE_GRAPH: + case WINDOW_FINANCES_PAGE_PROFIT_GRAPH: + case WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH: + { + // recalculate size for graph pages only. + Widget* pageWidget; + switch (page) + { + case WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH: + pageWidget = &_windowFinancesCashWidgets[WIDX_PAGE_BACKGROUND]; + break; + case WINDOW_FINANCES_PAGE_VALUE_GRAPH: + pageWidget = &_windowFinancesParkValueWidgets[WIDX_PAGE_BACKGROUND]; + break; + case WINDOW_FINANCES_PAGE_PROFIT_GRAPH: + pageWidget = &_windowFinancesProfitWidgets[WIDX_PAGE_BACKGROUND]; + break; + default: + return; + } + _graphBounds = { windowPos + ScreenCoordsXY{ pageWidget->left + 4, pageWidget->top + 15 }, + windowPos + ScreenCoordsXY{ pageWidget->right - 4, pageWidget->bottom - 4 } }; + _graphProps.RecalculateLayout( + { _graphBounds.Point1 + kGraphTopLeftPadding, _graphBounds.Point2 - kGraphBottomRightPadding }, + kGraphNumYLabels, kGraphNumPoints); + _graphProps.lineCol = colours[2]; + } + break; } } @@ -333,14 +379,28 @@ static Widget _windowFinancesResearchWidgets[] = OnDrawSummary(dpi); break; case WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH: - OnDrawFinancialGraph(dpi); + { + auto& gameState = GetGameState(); + auto cashLessLoan = gameState.Cash - gameState.BankLoan; + auto fmt = cashLessLoan >= 0 ? STR_FINANCES_FINANCIAL_GRAPH_CASH_LESS_LOAN_POSITIVE + : STR_FINANCES_FINANCIAL_GRAPH_CASH_LESS_LOAN_NEGATIVE; + OnDrawGraph(dpi, cashLessLoan, gameState.CashHistory, fmt, true); break; + } case WINDOW_FINANCES_PAGE_VALUE_GRAPH: - OnDrawParkValueGraph(dpi); + { + auto& gameState = GetGameState(); + OnDrawGraph(dpi, gameState.Park.Value, gameState.Park.ValueHistory, STR_FINANCES_PARK_VALUE, false); break; + } case WINDOW_FINANCES_PAGE_PROFIT_GRAPH: - OnDrawProfitGraph(dpi); + { + auto& gameState = GetGameState(); + auto fmt = gameState.CurrentProfit >= 0 ? STR_FINANCES_WEEKLY_PROFIT_POSITIVE + : STR_FINANCES_WEEKLY_PROFIT_LOSS; + OnDrawGraph(dpi, gameState.CurrentProfit, gameState.WeeklyProfitHistory, fmt, true); break; + } case WINDOW_FINANCES_PAGE_MARKETING: OnDrawMarketing(dpi); break; @@ -456,16 +516,26 @@ static Widget _windowFinancesResearchWidgets[] = { width = WW_RESEARCH; height = WH_RESEARCH; + flags &= ~WF_RESIZABLE; } else if (p == WINDOW_FINANCES_PAGE_SUMMARY) { width = WW_OTHER_TABS; height = WH_SUMMARY; + flags &= ~WF_RESIZABLE; + } + else if ( + p == WINDOW_FINANCES_PAGE_VALUE_GRAPH || p == WINDOW_FINANCES_PAGE_PROFIT_GRAPH + || p == WINDOW_FINANCES_PAGE_FINANCIAL_GRAPH) + { + flags |= WF_RESIZABLE; + WindowSetResize(*this, WW_OTHER_TABS, WH_OTHER_TABS, 2000, 2000); } else { width = WW_OTHER_TABS; height = WH_OTHER_TABS; + flags &= ~WF_RESIZABLE; } OnResize(); OnPrepareDraw(); @@ -607,190 +677,6 @@ static Widget _windowFinancesResearchWidgets[] = #pragma endregion -#pragma region Financial Graph Events - - void OnDrawFinancialGraph(DrawPixelInfo& dpi) - { - Widget* pageWidget = &_windowFinancesCashWidgets[WIDX_PAGE_BACKGROUND]; - auto graphTopLeft = windowPos + ScreenCoordsXY{ pageWidget->left + 4, pageWidget->top + 15 }; - auto graphBottomRight = windowPos + ScreenCoordsXY{ pageWidget->right - 4, pageWidget->bottom - 4 }; - - const auto& gameState = GetGameState(); - - // Cash (less loan) - auto cashLessLoan = gameState.Cash - gameState.BankLoan; - auto ft = Formatter(); - ft.Add(cashLessLoan); - - DrawTextBasic( - dpi, graphTopLeft - ScreenCoordsXY{ 0, 11 }, - cashLessLoan >= 0 ? STR_FINANCES_FINANCIAL_GRAPH_CASH_LESS_LOAN_POSITIVE - : STR_FINANCES_FINANCIAL_GRAPH_CASH_LESS_LOAN_NEGATIVE, - ft); - - // Graph - GfxFillRectInset(dpi, { graphTopLeft, graphBottomRight }, colours[1], INSET_RECT_F_30); - - // Calculate the Y axis scale (log2 of highest [+/-]balance) - int32_t yAxisScale = 0; - for (int32_t i = 0; i < 64; i++) - { - auto balance = gameState.CashHistory[i]; - if (balance == kMoney64Undefined) - continue; - - // Modifier balance then keep halving until less than 127 pixels - balance = std::abs(balance) >> yAxisScale; - while (balance > 127) - { - balance /= 2; - yAxisScale++; - } - } - - // Y axis labels - auto coords = graphTopLeft + ScreenCoordsXY{ 18, 14 }; - money64 axisBase; - for (axisBase = 12.00_GBP; axisBase >= -12.00_GBP; axisBase -= 6.00_GBP) - { - auto axisValue = axisBase << yAxisScale; - ft = Formatter(); - ft.Add(axisValue); - DrawTextBasic( - dpi, coords + ScreenCoordsXY{ 70, 0 }, STR_FINANCES_FINANCIAL_GRAPH_CASH_VALUE, ft, - { FontStyle::Small, TextAlignment::RIGHT }); - GfxFillRectInset( - dpi, { coords + ScreenCoordsXY{ 70, 5 }, { graphTopLeft.x + 482, coords.y + 5 } }, colours[2], - INSET_RECT_FLAG_BORDER_INSET); - coords.y += 39; - } - - // X axis labels and values - coords = graphTopLeft + ScreenCoordsXY{ 98, 17 }; - Graph::Draw(dpi, gameState.CashHistory, 64, coords, yAxisScale, 128); - } - -#pragma endregion - -#pragma region Park Value Graph Events - - void OnDrawParkValueGraph(DrawPixelInfo& dpi) - { - Widget* pageWidget = &_windowFinancesCashWidgets[WIDX_PAGE_BACKGROUND]; - auto graphTopLeft = windowPos + ScreenCoordsXY{ pageWidget->left + 4, pageWidget->top + 15 }; - auto graphBottomRight = windowPos + ScreenCoordsXY{ pageWidget->right - 4, pageWidget->bottom - 4 }; - - const auto& gameState = GetGameState(); - - // Park value - auto ft = Formatter(); - ft.Add(gameState.Park.Value); - DrawTextBasic(dpi, graphTopLeft - ScreenCoordsXY{ 0, 11 }, STR_FINANCES_PARK_VALUE, ft); - - // Graph - GfxFillRectInset(dpi, { graphTopLeft, graphBottomRight }, colours[1], INSET_RECT_F_30); - - // Calculate the Y axis scale (log2 of highest [+/-]balance) - int32_t yAxisScale = 0; - for (int32_t i = 0; i < 64; i++) - { - auto balance = gameState.Park.ValueHistory[i]; - if (balance == kMoney64Undefined) - continue; - - // Modifier balance then keep halving until less than 255 pixels - balance = std::abs(balance) >> yAxisScale; - while (balance > 255) - { - balance /= 2; - yAxisScale++; - } - } - - // Y axis labels - auto coords = graphTopLeft + ScreenCoordsXY{ 18, 14 }; - money64 axisBase; - for (axisBase = 24.00_GBP; axisBase >= 0.00_GBP; axisBase -= 6.00_GBP) - { - auto axisValue = axisBase << yAxisScale; - ft = Formatter(); - ft.Add(axisValue); - DrawTextBasic( - dpi, coords + ScreenCoordsXY{ 70, 0 }, STR_FINANCES_FINANCIAL_GRAPH_CASH_VALUE, ft, - { FontStyle::Small, TextAlignment::RIGHT }); - GfxFillRectInset( - dpi, { coords + ScreenCoordsXY{ 70, 5 }, { graphTopLeft.x + 482, coords.y + 5 } }, colours[2], - INSET_RECT_FLAG_BORDER_INSET); - coords.y += 39; - } - - // X axis labels and values - coords = graphTopLeft + ScreenCoordsXY{ 98, 17 }; - Graph::Draw(dpi, gameState.Park.ValueHistory, 64, coords, yAxisScale, 0); - } - -#pragma endregion - -#pragma region Profit Graph Events - - void OnDrawProfitGraph(DrawPixelInfo& dpi) - { - auto& gameState = GetGameState(); - Widget* pageWidget = &_windowFinancesCashWidgets[WIDX_PAGE_BACKGROUND]; - auto graphTopLeft = windowPos + ScreenCoordsXY{ pageWidget->left + 4, pageWidget->top + 15 }; - auto graphBottomRight = windowPos + ScreenCoordsXY{ pageWidget->right - 4, pageWidget->bottom - 4 }; - - // Weekly profit - auto ft = Formatter(); - ft.Add(gameState.CurrentProfit); - DrawTextBasic( - dpi, graphTopLeft - ScreenCoordsXY{ 0, 11 }, - gameState.CurrentProfit >= 0 ? STR_FINANCES_WEEKLY_PROFIT_POSITIVE : STR_FINANCES_WEEKLY_PROFIT_LOSS, ft); - - // Graph - GfxFillRectInset(dpi, { graphTopLeft, graphBottomRight }, colours[1], INSET_RECT_F_30); - - // Calculate the Y axis scale (log2 of highest [+/-]balance) - int32_t yAxisScale = 0; - for (int32_t i = 0; i < 64; i++) - { - auto balance = gameState.WeeklyProfitHistory[i]; - if (balance == kMoney64Undefined) - continue; - - // Modifier balance then keep halving until less than 127 pixels - balance = std::abs(balance) >> yAxisScale; - while (balance > 127) - { - balance /= 2; - yAxisScale++; - } - } - - // Y axis labels - auto screenPos = graphTopLeft + ScreenCoordsXY{ 18, 14 }; - money64 axisBase; - for (axisBase = 12.00_GBP; axisBase >= -12.00_GBP; axisBase -= 6.00_GBP) - { - money64 axisValue = axisBase << yAxisScale; - ft = Formatter(); - ft.Add(axisValue); - DrawTextBasic( - dpi, screenPos + ScreenCoordsXY{ 70, 0 }, STR_FINANCES_FINANCIAL_GRAPH_CASH_VALUE, ft, - { FontStyle::Small, TextAlignment::RIGHT }); - GfxFillRectInset( - dpi, { screenPos + ScreenCoordsXY{ 70, 5 }, { graphTopLeft.x + 482, screenPos.y + 5 } }, colours[2], - INSET_RECT_FLAG_BORDER_INSET); - screenPos.y += 39; - } - - // X axis labels and values - screenPos = graphTopLeft + ScreenCoordsXY{ 98, 17 }; - Graph::Draw(dpi, gameState.WeeklyProfitHistory, 64, screenPos, yAxisScale, 128); - } - -#pragma endregion - #pragma region Marketing Events void OnMouseUpMarketing(WidgetIndex widgetIndex) @@ -951,6 +837,47 @@ static Widget _windowFinancesResearchWidgets[] = { ResizeFrameWithPage(); } + + void OnDrawGraph( + DrawPixelInfo& dpi, const money64 currentValue, money64 (&series)[kFinanceHistorySize], const StringId fmt, + const bool centred) + { + Formatter ft; + ft.Add(currentValue); + DrawTextBasic(dpi, _graphBounds.Point1 - ScreenCoordsXY{ 0, 11 }, fmt, ft); + + // Graph + GfxFillRectInset(dpi, _graphBounds, colours[1], INSET_RECT_F_30); + // hide resize widget on graph area + constexpr ScreenCoordsXY offset{ 1, 1 }; + constexpr ScreenCoordsXY bigOffset{ 5, 5 }; + GfxFillRectInset( + dpi, { _graphBounds.Point2 - bigOffset, _graphBounds.Point2 - offset }, colours[1], + INSET_RECT_FLAG_FILL_DONT_LIGHTEN | INSET_RECT_FLAG_BORDER_NONE); + + // Calculate Y axis max and min. + money64 maxVal = 0; + for (int32_t i = 0; i < kGraphNumPoints; i++) + { + auto val = series[i]; + if (val == kMoney64Undefined) + continue; + while (std::abs(val) > maxVal) + maxVal = val; + } + // This algorithm increments the leading digit of the max and sets all other digits to zero. + // e.g. 681 => 700. + money64 oom = 10; + while (maxVal / oom >= 10) + oom *= 10; + const money64 max = ((maxVal + oom - 1) / oom) * oom; + + _graphProps.min = centred ? -max : 0.00_GBP; + _graphProps.max = max; + _graphProps.series = series; + + Graph::DrawFinanceGraph(dpi, _graphProps); + } }; static FinancesWindow* FinancesWindowOpen(uint8_t page) diff --git a/src/openrct2-ui/windows/Park.cpp b/src/openrct2-ui/windows/Park.cpp index 79b2b5e60e..1068c49ada 100644 --- a/src/openrct2-ui/windows/Park.cpp +++ b/src/openrct2-ui/windows/Park.cpp @@ -10,7 +10,6 @@ #include "../interface/Theme.h" #include -#include #include #include #include @@ -27,13 +26,11 @@ #include #include #include -#include #include #include #include #include #include -#include #include namespace OpenRCT2::Ui::Windows @@ -41,6 +38,10 @@ namespace OpenRCT2::Ui::Windows static constexpr StringId WINDOW_TITLE = STR_STRINGID; static constexpr int32_t WH = 224; + static constexpr ScreenCoordsXY kGraphTopLeftPadding{ 45, 15 }; + static constexpr ScreenCoordsXY kGraphBottomRightPadding{ 5, 5 }; + static constexpr uint8_t kGraphNumYLabels = 6; + // clang-format off enum WindowParkPage { WINDOW_PARK_PAGE_ENTRANCE, @@ -197,6 +198,12 @@ static constexpr WindowParkAward _parkAwards[] = { int32_t _numberOfRides = -1; uint8_t _peepAnimationFrame = 0; + Graph::GraphProperties _ratingProps{}; + Graph::GraphProperties _guestProps{}; + + ScreenRect _ratingGraphBounds; + ScreenRect _guestGraphBounds; + public: void OnOpen() override { @@ -206,6 +213,11 @@ static constexpr WindowParkAward _parkAwards[] = { _numberOfStaff = -1; _peepAnimationFrame = 0; SetPage(0); + + _ratingProps.lineCol = colours[2]; + _guestProps.lineCol = colours[2]; + _ratingProps.hoverIdx = -1; + _guestProps.hoverIdx = -1; } void OnClose() override @@ -691,6 +703,13 @@ static constexpr WindowParkAward _parkAwards[] = { WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); AnchorBorderWidgets(); + + const Widget* background = &widgets[WIDX_PAGE_BACKGROUND]; + _ratingGraphBounds = { windowPos + ScreenCoordsXY{ background->left + 4, background->top + 15 }, + windowPos + ScreenCoordsXY{ background->right - 4, background->bottom - 4 } }; + _ratingProps.RecalculateLayout( + { _ratingGraphBounds.Point1 + kGraphTopLeftPadding, _ratingGraphBounds.Point2 - kGraphBottomRightPadding }, + kGraphNumYLabels, kParkRatingHistorySize); } void OnDrawRating(DrawPixelInfo& dpi) @@ -698,41 +717,21 @@ static constexpr WindowParkAward _parkAwards[] = { DrawWidgets(dpi); DrawTabImages(dpi); - auto screenPos = windowPos; Widget* widget = &widgets[WIDX_PAGE_BACKGROUND]; + const auto& gameState = OpenRCT2::GetGameState(); // Current value - auto ft = Formatter(); - ft.Add(GetGameState().Park.Rating); - DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ widget->left + 3, widget->top + 2 }, STR_PARK_RATING_LABEL, ft); + Formatter ft; + ft.Add(gameState.Park.Rating); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ widget->left + 3, widget->top + 2 }, STR_PARK_RATING_LABEL, ft); // Graph border - GfxFillRectInset( - dpi, - { screenPos + ScreenCoordsXY{ widget->left + 4, widget->top + 15 }, - screenPos + ScreenCoordsXY{ widget->right - 4, widget->bottom - 4 } }, - colours[1], INSET_RECT_F_30); + GfxFillRectInset(dpi, _ratingGraphBounds, colours[1], INSET_RECT_F_30); - // Y axis labels - screenPos = screenPos + ScreenCoordsXY{ widget->left + 27, widget->top + 23 }; - for (int i = 5; i >= 0; i--) - { - uint32_t axisValue = i * 200; - ft = Formatter(); - ft.Add(axisValue); - DrawTextBasic( - dpi, screenPos + ScreenCoordsXY{ 10, 0 }, STR_GRAPH_AXIS_LABEL, ft, - { FontStyle::Small, TextAlignment::RIGHT }); - GfxFillRectInset( - dpi, { screenPos + ScreenCoordsXY{ 15, 5 }, screenPos + ScreenCoordsXY{ width - 32, 5 } }, colours[2], - INSET_RECT_FLAG_BORDER_INSET); - screenPos.y += 20; - } - - // Graph - screenPos = windowPos + ScreenCoordsXY{ widget->left + 47, widget->top + 26 }; - - Graph::Draw(dpi, GetGameState().Park.RatingHistory, kParkRatingHistorySize, screenPos); + _ratingProps.min = 0; + _ratingProps.max = 250; + _ratingProps.series = gameState.Park.RatingHistory; + Graph::DrawRatingGraph(dpi, _ratingProps); } #pragma endregion @@ -764,6 +763,13 @@ static constexpr WindowParkAward _parkAwards[] = { WindowAlignTabs(this, WIDX_TAB_1, WIDX_TAB_7); AnchorBorderWidgets(); + + const Widget* background = &widgets[WIDX_PAGE_BACKGROUND]; + _guestGraphBounds = { windowPos + ScreenCoordsXY{ background->left + 4, background->top + 15 }, + windowPos + ScreenCoordsXY{ background->right - 4, background->bottom - 4 } }; + _guestProps.RecalculateLayout( + { _guestGraphBounds.Point1 + kGraphTopLeftPadding, _guestGraphBounds.Point2 - kGraphBottomRightPadding }, + kGraphNumYLabels, kGuestsInParkHistorySize); } void OnDrawGuests(DrawPixelInfo& dpi) @@ -771,56 +777,30 @@ static constexpr WindowParkAward _parkAwards[] = { DrawWidgets(dpi); DrawTabImages(dpi); - auto screenPos = windowPos; Widget* widget = &widgets[WIDX_PAGE_BACKGROUND]; - const auto& gameState = OpenRCT2::GetGameState(); // Current value - auto ft = Formatter(); + Formatter ft; ft.Add(gameState.NumGuestsInPark); - DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ widget->left + 3, widget->top + 2 }, STR_GUESTS_IN_PARK_LABEL, ft); + DrawTextBasic(dpi, windowPos + ScreenCoordsXY{ widget->left + 3, widget->top + 2 }, STR_GUESTS_IN_PARK_LABEL, ft); // Graph border - GfxFillRectInset( - dpi, - { screenPos + ScreenCoordsXY{ widget->left + 4, widget->top + 15 }, - screenPos + ScreenCoordsXY{ widget->right - 4, widget->bottom - 4 } }, - colours[1], INSET_RECT_F_30); + GfxFillRectInset(dpi, _guestGraphBounds, colours[1], INSET_RECT_F_30); - // Y axis labels - screenPos = screenPos + ScreenCoordsXY{ widget->left + 27, widget->top + 23 }; - for (int i = 5; i >= 0; i--) - { - uint32_t axisValue = i * 1000; - ft = Formatter(); - ft.Add(axisValue); - DrawTextBasic( - dpi, screenPos + ScreenCoordsXY{ 10, 0 }, STR_GRAPH_AXIS_LABEL, ft, - { FontStyle::Small, TextAlignment::RIGHT }); - GfxFillRectInset( - dpi, { screenPos + ScreenCoordsXY{ 15, 5 }, screenPos + ScreenCoordsXY{ width - 32, 5 } }, colours[2], - INSET_RECT_FLAG_BORDER_INSET); - screenPos.y += 20; - } - - // Graph - screenPos = windowPos + ScreenCoordsXY{ widget->left + 47, widget->top + 26 }; - - uint8_t cappedHistory[32]; - for (size_t i = 0; i < std::size(cappedHistory); i++) + // Calculate Y axis max and min + _guestProps.min = 0; + _guestProps.max = 5000; + for (size_t i = 0; i < std::size(gameState.GuestsInParkHistory); i++) { auto value = gameState.GuestsInParkHistory[i]; - if (value != std::numeric_limits::max()) - { - cappedHistory[i] = static_cast(std::min(value, 5000) / 20); - } - else - { - cappedHistory[i] = std::numeric_limits::max(); - } + if (value == GuestsInParkHistoryUndefined) + continue; + while (value > _guestProps.max) + _guestProps.max += 5000; } - Graph::Draw(dpi, cappedHistory, static_cast(std::size(cappedHistory)), screenPos); + _guestProps.series = gameState.GuestsInParkHistory; + Graph::DrawGuestGraph(dpi, _guestProps); } #pragma endregion @@ -1313,6 +1293,7 @@ static constexpr WindowParkAward _parkAwards[] = { windowPos + ScreenCoordsXY{ widgets[WIDX_TAB_7].left, widgets[WIDX_TAB_7].top }); } } +#pragma endregion }; static ParkWindow* ParkWindowOpen(uint8_t page) diff --git a/src/openrct2/GameState.h b/src/openrct2/GameState.h index bcf3db304e..6c03f6b610 100644 --- a/src/openrct2/GameState.h +++ b/src/openrct2/GameState.h @@ -47,13 +47,13 @@ namespace OpenRCT2 money64 ConstructionRightsPrice; money64 CurrentExpenditure; money64 CurrentProfit; - uint32_t GuestsInParkHistory[32]; + uint32_t GuestsInParkHistory[kGuestsInParkHistorySize]; ClimateType Climate; ClimateState ClimateCurrent; ClimateState ClimateNext; uint16_t ClimateUpdateTimer; money64 Cash; - money64 CashHistory[kFinanceGraphSize]; + money64 CashHistory[kFinanceHistorySize]; money64 InitialCash; money64 GuestInitialCash; uint8_t GuestInitialHappiness; @@ -69,7 +69,7 @@ namespace OpenRCT2 money64 TotalIncomeFromAdmissions; money64 TotalRideValueForMoney; uint16_t WeeklyProfitAverageDivisor; - money64 WeeklyProfitHistory[kFinanceGraphSize]; + money64 WeeklyProfitHistory[kFinanceHistorySize]; Objective ScenarioObjective; uint16_t ScenarioParkRatingWarningDays; money64 ScenarioCompletedCompanyValue; diff --git a/src/openrct2/management/Finance.cpp b/src/openrct2/management/Finance.cpp index 8dadd58ab1..887e23342a 100644 --- a/src/openrct2/management/Finance.cpp +++ b/src/openrct2/management/Finance.cpp @@ -185,7 +185,7 @@ void FinancePayRideUpkeep() void FinanceResetHistory() { auto& gameState = GetGameState(); - for (auto i = 0; i < kFinanceGraphSize; i++) + for (auto i = 0; i < kFinanceHistorySize; i++) { gameState.CashHistory[i] = kMoney64Undefined; gameState.WeeklyProfitHistory[i] = kMoney64Undefined; diff --git a/src/openrct2/management/Finance.h b/src/openrct2/management/Finance.h index afdd4cf544..956377057e 100644 --- a/src/openrct2/management/Finance.h +++ b/src/openrct2/management/Finance.h @@ -32,7 +32,7 @@ enum class ExpenditureType : int32_t }; constexpr uint8_t kExpenditureTableMonthCount = 16; -constexpr uint8_t kFinanceGraphSize = 128; +constexpr uint8_t kFinanceHistorySize = 128; constexpr uint8_t MaxBankLoanInterestRate = 255; diff --git a/src/openrct2/rct1/RCT1.h b/src/openrct2/rct1/RCT1.h index b1b6df3510..6ad1917aa6 100644 --- a/src/openrct2/rct1/RCT1.h +++ b/src/openrct2/rct1/RCT1.h @@ -769,7 +769,7 @@ namespace OpenRCT2::RCT1 uint8_t Unk1990AA[94]; uint16_t ParkRating; uint8_t ParkRatingHistory[kParkRatingHistorySize]; - uint8_t GuestsInParkHistory[32]; + uint8_t GuestsInParkHistory[kGuestsInParkHistorySize]; uint8_t ResearchPriority; uint8_t ResearchProgressStage; uint8_t LastResearchItem; diff --git a/src/openrct2/rct2/RCT2.h b/src/openrct2/rct2/RCT2.h index 220186ea87..9660bb0ef3 100644 --- a/src/openrct2/rct2/RCT2.h +++ b/src/openrct2/rct2/RCT2.h @@ -862,7 +862,7 @@ namespace OpenRCT2::RCT2 // Ignored in scenario uint8_t ParkRatingHistory[kParkRatingHistorySize]; - uint8_t GuestsInParkHistory[32]; + uint8_t GuestsInParkHistory[kGuestsInParkHistorySize]; // SC6[10] uint8_t ActiveResearchTypes; diff --git a/src/openrct2/world/Park.cpp b/src/openrct2/world/Park.cpp index 6483d3a961..0763d8e7a3 100644 --- a/src/openrct2/world/Park.cpp +++ b/src/openrct2/world/Park.cpp @@ -586,8 +586,10 @@ namespace OpenRCT2::Park gameState.NumGuestsInParkLastWeek = gameState.NumGuestsInPark; // Update park rating, guests in park and current cash history - HistoryPushRecord(gameState.Park.RatingHistory, gameState.Park.Rating / 4); - HistoryPushRecord(gameState.GuestsInParkHistory, gameState.NumGuestsInPark); + constexpr auto ratingHistorySize = std::extent_v; + HistoryPushRecord(gameState.Park.RatingHistory, gameState.Park.Rating / 4); + constexpr auto numGuestsHistorySize = std::extent_v; + HistoryPushRecord(gameState.GuestsInParkHistory, gameState.NumGuestsInPark); constexpr auto cashHistorySize = std::extent_v; HistoryPushRecord(gameState.CashHistory, FinanceGetCurrentCash() - gameState.BankLoan); diff --git a/src/openrct2/world/Park.h b/src/openrct2/world/Park.h index 737c87775a..7d305463a5 100644 --- a/src/openrct2/world/Park.h +++ b/src/openrct2/world/Park.h @@ -17,6 +17,7 @@ constexpr auto MAX_ENTRANCE_FEE = 999.00_GBP; constexpr uint8_t ParkRatingHistoryUndefined = std::numeric_limits::max(); constexpr uint32_t GuestsInParkHistoryUndefined = std::numeric_limits::max(); constexpr uint8_t kParkRatingHistorySize = 32; +constexpr uint8_t kGuestsInParkHistorySize = 32; constexpr uint8_t ParkNameMaxLength = 128; constexpr uint8_t ScenarioNameMaxLength = 128; constexpr uint16_t ScenarioDetailsNameMaxLength = 256; @@ -65,7 +66,7 @@ namespace OpenRCT2 std::vector Entrances; uint32_t Size; money64 Value; - money64 ValueHistory[kFinanceGraphSize]; + money64 ValueHistory[kFinanceHistorySize]; bool IsOpen() const; };