1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-22 14:24:33 +01:00

Merge pull request #22414 from mrmbernardi/fix-graphs

Improve financial and park history graphs
This commit is contained in:
mrmbernardi
2024-08-06 01:10:05 +10:00
committed by GitHub
12 changed files with 384 additions and 515 deletions

View File

@@ -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 Coasters 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)
------------------------------------------------------------------------

View File

@@ -10,282 +10,189 @@
#include "../UiStringIds.h"
#include <openrct2-ui/interface/Graph.h>
#include <openrct2/Context.h>
#include <openrct2/Date.h>
#include <openrct2/localisation/Formatter.h>
#include <openrct2/localisation/Formatting.h>
#include <openrct2/localisation/Localisation.Date.h>
using namespace OpenRCT2;
#include <openrct2/world/Park.h>
namespace OpenRCT2::Graph
{
static void DrawMonths(DrawPixelInfo& dpi, const uint8_t* history, int32_t count, const ScreenCoordsXY& origCoords)
constexpr int32_t kDashLength = 2;
template<typename T>
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<typename T, T TkNoValue>
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<uint32_t>(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<int32_t>(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<StringId>(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<typename T>
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<money64>(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<money64>(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<typename T, T TkNoValue, bool TbackgroundLine>
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<money64>& p)
{
const FmtString fmt("{BLACK}{CURRENCY2DP} -");
DrawYLabels<money64>(dpi, p.internalBounds, p.min, p.max, p.numYLabels, p.yLabelStepPx, p.lineCol, fmt);
DrawMonths<money64, kMoney64Undefined>(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx);
DrawLine<money64, kMoney64Undefined, true>(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx, p.min, p.max);
DrawLine<money64, kMoney64Undefined, false>(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<money64>(
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<uint8_t>& 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<uint16_t>(dpi, p.internalBounds, p.min, 1000, p.numYLabels, p.yLabelStepPx, p.lineCol, fmt);
DrawMonths<uint8_t, noValue>(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx);
DrawLine<uint8_t, noValue, true>(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx, p.min, p.max);
DrawLine<uint8_t, noValue, false>(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx, p.min, p.max);
}
void DrawGuestGraph(DrawPixelInfo& dpi, const GraphProperties<uint32_t>& p)
{
constexpr uint32_t noValue = GuestsInParkHistoryUndefined;
const FmtString fmt("{BLACK}{COMMA32} -");
DrawYLabels<uint32_t>(dpi, p.internalBounds, p.min, p.max, p.numYLabels, p.yLabelStepPx, p.lineCol, fmt);
DrawMonths<uint32_t, noValue>(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx);
DrawLine<uint32_t, noValue, true>(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx, p.min, p.max);
DrawLine<uint32_t, noValue, false>(dpi, p.series, p.numPoints, p.internalBounds, p.xStepPx, p.min, p.max);
}
} // namespace OpenRCT2::Graph

View File

@@ -9,14 +9,62 @@
#pragma once
#include <openrct2/Context.h>
#include <openrct2/core/Money.hpp>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/world/Location.hpp>
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<typename T> 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<money64>& p);
void DrawRatingGraph(DrawPixelInfo& dpi, const GraphProperties<uint8_t>& p);
void DrawGuestGraph(DrawPixelInfo& dpi, const GraphProperties<uint32_t>& p);
} // namespace OpenRCT2::Graph

View File

@@ -12,7 +12,6 @@
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/GameState.h>
#include <openrct2/actions/ParkSetLoanAction.h>
#include <openrct2/actions/ParkSetResearchFundingAction.h>
@@ -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<uint32_t>::max();
ScreenRect _graphBounds;
Graph::GraphProperties<money64> _graphProps{};
void SetDisabledTabs()
{
@@ -236,12 +244,22 @@ static Widget _windowFinancesResearchWidgets[] =
SetPage(WINDOW_FINANCES_PAGE_SUMMARY);
_lastPaintedMonth = std::numeric_limits<uint32_t>::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<money64>(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<money64>(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<money64>(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<money64>(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<money64>(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<money64>(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<money64>(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)

View File

@@ -10,7 +10,6 @@
#include "../interface/Theme.h"
#include <array>
#include <limits>
#include <openrct2-ui/interface/Dropdown.h>
#include <openrct2-ui/interface/Graph.h>
#include <openrct2-ui/interface/LandTool.h>
@@ -27,13 +26,11 @@
#include <openrct2/config/Config.h>
#include <openrct2/localisation/Currency.h>
#include <openrct2/localisation/Formatting.h>
#include <openrct2/localisation/Localisation.Date.h>
#include <openrct2/management/Award.h>
#include <openrct2/peep/PeepAnimationData.h>
#include <openrct2/ride/RideData.h>
#include <openrct2/scenario/Scenario.h>
#include <openrct2/util/Util.h>
#include <openrct2/world/Entrance.h>
#include <openrct2/world/Park.h>
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<uint8_t> _ratingProps{};
Graph::GraphProperties<uint32_t> _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<uint16_t>(GetGameState().Park.Rating);
DrawTextBasic(dpi, screenPos + ScreenCoordsXY{ widget->left + 3, widget->top + 2 }, STR_PARK_RATING_LABEL, ft);
Formatter ft;
ft.Add<uint16_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>::max())
{
cappedHistory[i] = static_cast<uint8_t>(std::min<uint32_t>(value, 5000) / 20);
}
else
{
cappedHistory[i] = std::numeric_limits<uint8_t>::max();
}
if (value == GuestsInParkHistoryUndefined)
continue;
while (value > _guestProps.max)
_guestProps.max += 5000;
}
Graph::Draw(dpi, cappedHistory, static_cast<int32_t>(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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -586,8 +586,10 @@ namespace OpenRCT2::Park
gameState.NumGuestsInParkLastWeek = gameState.NumGuestsInPark;
// Update park rating, guests in park and current cash history
HistoryPushRecord<uint8_t, 32>(gameState.Park.RatingHistory, gameState.Park.Rating / 4);
HistoryPushRecord<uint32_t, 32>(gameState.GuestsInParkHistory, gameState.NumGuestsInPark);
constexpr auto ratingHistorySize = std::extent_v<decltype(ParkData::RatingHistory)>;
HistoryPushRecord<uint8_t, ratingHistorySize>(gameState.Park.RatingHistory, gameState.Park.Rating / 4);
constexpr auto numGuestsHistorySize = std::extent_v<decltype(GameState_t::GuestsInParkHistory)>;
HistoryPushRecord<uint32_t, numGuestsHistorySize>(gameState.GuestsInParkHistory, gameState.NumGuestsInPark);
constexpr auto cashHistorySize = std::extent_v<decltype(GameState_t::CashHistory)>;
HistoryPushRecord<money64, cashHistorySize>(gameState.CashHistory, FinanceGetCurrentCash() - gameState.BankLoan);

View File

@@ -17,6 +17,7 @@ constexpr auto MAX_ENTRANCE_FEE = 999.00_GBP;
constexpr uint8_t ParkRatingHistoryUndefined = std::numeric_limits<uint8_t>::max();
constexpr uint32_t GuestsInParkHistoryUndefined = std::numeric_limits<uint32_t>::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<CoordsXYZD> Entrances;
uint32_t Size;
money64 Value;
money64 ValueHistory[kFinanceGraphSize];
money64 ValueHistory[kFinanceHistorySize];
bool IsOpen() const;
};