/***************************************************************************** * Copyright (c) 2014-2023 OpenRCT2 developers * * For a complete list of all authors, please refer to contributors.md * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ #include #include #include #include #include namespace Graph { static void DrawMonths(DrawPixelInfo* dpi, const uint8_t* history, int32_t count, const ScreenCoordsXY& origCoords) { int32_t currentMonth = DateGetMonth(gDateMonthsElapsed); int32_t currentDay = gDateMonthTicks; int32_t yearOver32 = (currentMonth * 4) + (currentDay >> 14) - 31; auto screenCoords = origCoords; 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 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 { {}, MONEY64_UNDEFINED }; } 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 Graph { static void DrawMonths(DrawPixelInfo* dpi, const money64* history, int32_t count, const ScreenCoordsXY& origCoords) { int32_t i, yearOver32, currentMonth, currentDay; currentMonth = DateGetMonth(gDateMonthsElapsed); currentDay = gDateMonthTicks; yearOver32 = (currentMonth * 4) + (currentDay >> 14) - 31; auto screenCoords = origCoords; for (i = count - 1; i >= 0; i--) { if (history[i] != MONEY64_UNDEFINED && 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 money64* history, int32_t count, const ScreenCoordsXY& origCoords, int32_t modifier, int32_t offset) { auto lastCoords = ScreenCoordsXY{ -1, -1 }; auto coords = origCoords; for (int32_t i = count - 1; i >= 0; i--) { if (history[i] != MONEY64_UNDEFINED) { coords.y = origCoords.y + 170 - 6 - ((((history[i] >> modifier) + offset) * 170) / 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 money64* history, int32_t count, const ScreenCoordsXY& origCoords, int32_t modifier, int32_t offset) { auto lastCoords = ScreenCoordsXY{ -1, -1 }; auto coords = origCoords; for (int32_t i = count - 1; i >= 0; i--) { if (history[i] != MONEY64_UNDEFINED) { coords.y = origCoords.y + 170 - 6 - ((((history[i] >> modifier) + offset) * 170) / 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; } } static void DrawHoveredValue( DrawPixelInfo* dpi, const money64* history, const int32_t historyCount, const ScreenCoordsXY& screenCoords, const int32_t modifier, const int32_t offset) { const auto cursorPosition = ContextGetCursorPositionScaled(); const ScreenRect chartFrame{ screenCoords, screenCoords + ScreenCoordsXY{ ChartMaxWidth, ChartMaxHeight } }; if (!chartFrame.Contains(cursorPosition)) { return; } const auto info = FinanceTooltipInfoFromMoney(history, ChartMaxDataCount, modifier, offset, chartFrame, cursorPosition); if (info.money == MONEY64_UNDEFINED) { return; } GfxDrawDashedLine(dpi, { { info.coords.x, chartFrame.GetTop() }, info.coords }, DefaultDashedLength, 0); GfxDrawDashedLine(dpi, { { chartFrame.GetLeft() - 10, info.coords.y }, info.coords }, DefaultDashedLength, 0); 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); } void Draw( DrawPixelInfo* dpi, const money64* history, const int32_t count, const ScreenCoordsXY& screenCoords, const int32_t modifier, const int32_t offset) { 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); } } // namespace Graph