31 #include "table/strings.h"
37 static CompanyMask _legend_excluded_companies;
38 static CargoTypes _legend_excluded_cargo;
42 static const uint INVALID_DATAPOINT_POS = UINT_MAX;
63 void DrawWidget(
const Rect &r,
WidgetID widget)
const override
83 void OnClick([[maybe_unused]]
Point pt,
WidgetID widget, [[maybe_unused]]
int click_count)
override
102 void OnInvalidateData([[maybe_unused]]
int data = 0, [[maybe_unused]]
bool gui_scope =
true)
override
104 if (!gui_scope)
return;
107 SetBit(_legend_excluded_companies, data);
118 auto vert = std::make_unique<NWidgetVertical>(
NC_EQUALSIZE);
119 vert->SetPadding(2, 2, 2, 2);
123 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, COLOUR_BROWN, widnum);
126 panel->SetFill(1, 1);
127 panel->SetDataTip(0x0, STR_GRAPH_KEY_COMPANY_SELECTION_TOOLTIP);
128 vert->Add(std::move(panel));
133 static constexpr
NWidgetPart _nested_graph_legend_widgets[] = {
145 static WindowDesc _graph_legend_desc(__FILE__, __LINE__,
149 std::begin(_nested_graph_legend_widgets), std::end(_nested_graph_legend_widgets)
152 static void ShowGraphLegend()
154 AllocateWindowDescFront<GraphLegendWindow>(&_graph_legend_desc, 0);
169 static const int GRAPH_MAX_DATASETS = 64;
170 static const int GRAPH_BASE_COLOUR =
GREY_SCALE(2);
171 static const int GRAPH_GRID_COLOUR =
GREY_SCALE(3);
172 static const int GRAPH_AXIS_LINE_COLOUR =
GREY_SCALE(1);
173 static const int GRAPH_ZERO_LINE_COLOUR =
GREY_SCALE(8);
174 static const int GRAPH_YEAR_LINE_COLOUR =
GREY_SCALE(5);
192 TimerGameEconomy::Year year;
198 uint16_t x_values_start;
199 uint16_t x_values_increment;
202 byte colours[GRAPH_MAX_DATASETS];
213 assert(num_hori_lines > 0);
216 current_interval.
highest = INT64_MIN;
217 current_interval.
lowest = INT64_MAX;
219 for (
int i = 0; i < this->num_dataset; i++) {
220 if (
HasBit(this->excluded_data, i))
continue;
221 for (
int j = 0; j < this->num_on_x_axis; j++) {
224 if (datapoint != INVALID_DATAPOINT) {
225 current_interval.
highest = std::max(current_interval.
highest, datapoint);
226 current_interval.
lowest = std::min(current_interval.
lowest, datapoint);
232 double abs_lower = (current_interval.
lowest > 0) ? 0 : (
double)
abs(current_interval.
lowest);
233 double abs_higher = (current_interval.
highest < 0) ? 0 : (
double)current_interval.
highest;
236 abs_higher = (11.0 * abs_higher) / 10.0;
237 abs_lower = (11.0 * abs_lower) / 10.0;
242 if (abs_lower != 0 || abs_higher != 0) {
244 num_pos_grids = (int)floor(0.5 + num_hori_lines * abs_higher / (abs_higher + abs_lower));
247 if (num_pos_grids == 0 && abs_higher != 0) num_pos_grids++;
248 if (num_pos_grids == num_hori_lines && abs_lower != 0) num_pos_grids--;
253 if (abs_higher > 0) {
254 grid_size_higher = abs_higher >
INT64_MAX_IN_DOUBLE ? INT64_MAX :
static_cast<int64_t
>(abs_higher);
255 grid_size_higher = (grid_size_higher + num_pos_grids - 1) / num_pos_grids;
260 grid_size_lower = abs_lower >
INT64_MAX_IN_DOUBLE ? INT64_MAX :
static_cast<int64_t
>(abs_lower);
261 grid_size_lower = (grid_size_lower + num_hori_lines - num_pos_grids - 1) / (num_hori_lines - num_pos_grids);
264 grid_size = std::max(grid_size_higher, grid_size_lower);
267 num_pos_grids = num_hori_lines / 2;
271 current_interval.
highest = num_pos_grids * grid_size;
272 current_interval.
lowest = -(num_hori_lines - num_pos_grids) * grid_size;
273 return current_interval;
284 int64_t y_label = current_interval.
highest;
285 int64_t y_label_separation = (current_interval.
highest - current_interval.
lowest) / num_hori_lines;
289 for (
int i = 0; i < (num_hori_lines + 1); i++) {
293 if (d.width > max_width) max_width = d.width;
295 y_label -= y_label_separation;
314 assert(this->num_vert_lines > 0);
333 r.left += label_width;
335 int x_sep = (r.right - r.left) / this->num_vert_lines;
336 int y_sep = (r.bottom - r.top) / num_hori_lines;
340 r.right = r.left + x_sep * this->num_vert_lines;
341 r.bottom = r.top + y_sep * num_hori_lines;
345 x_axis_offset = (int)((r.bottom - r.top) * (double)interval.
highest / (
double)interval_size);
348 GfxFillRect(r.left, r.top, r.right, r.bottom, GRAPH_BASE_COLOUR);
355 int grid_colour = GRAPH_GRID_COLOUR;
356 for (
int i = 1; i < this->num_vert_lines + 1; i++) {
359 grid_colour = (i % 4 == 0) ? GRAPH_YEAR_LINE_COLOUR : GRAPH_GRID_COLOUR;
368 for (
int i = 0; i < (num_hori_lines + 1); i++) {
370 GfxFillRect(r.left, y, r.right, y, GRAPH_GRID_COLOUR);
375 GfxFillRect(r.left, r.top, r.left, r.bottom, GRAPH_AXIS_LINE_COLOUR);
378 y = x_axis_offset + r.top;
379 GfxFillRect(r.left, y, r.right, y, GRAPH_ZERO_LINE_COLOUR);
382 if (this->num_on_x_axis == 0)
return;
384 assert(this->num_on_x_axis > 0);
387 int64_t y_label = interval.
highest;
388 int64_t y_label_separation =
abs(interval.
highest - interval.
lowest) / num_hori_lines;
392 for (
int i = 0; i < (num_hori_lines + 1); i++) {
397 y_label -= y_label_separation;
402 if (this->draw_dates) {
406 TimerGameEconomy::Year year = this->year;
407 for (
int i = 0; i < this->num_on_x_axis; i++) {
408 SetDParam(0, month + STR_MONTH_ABBREV_JAN);
418 GfxFillRect(x + x_sep, r.top + 1, x + x_sep, r.bottom - 1, GRAPH_YEAR_LINE_COLOUR);
426 uint16_t label = this->x_values_start;
428 for (
int i = 0; i < this->num_on_x_axis; i++) {
432 label += this->x_values_increment;
439 uint pointoffs1 = (linewidth + 1) / 2;
440 uint pointoffs2 = linewidth + 1 - pointoffs1;
441 for (
int i = 0; i < this->num_dataset; i++) {
442 if (!
HasBit(this->excluded_data, i)) {
444 x = r.left + (x_sep / 2);
446 byte colour = this->colours[i];
447 uint prev_x = INVALID_DATAPOINT_POS;
448 uint prev_y = INVALID_DATAPOINT_POS;
450 for (
int j = 0; j < this->num_on_x_axis; j++) {
453 if (datapoint != INVALID_DATAPOINT) {
465 int mult_range = FindLastBit<uint32_t>(x_axis_offset) + FindLastBit<uint64_t>(
abs(datapoint));
466 int reduce_range = std::max(mult_range - 31, 0);
470 datapoint = -(
abs(datapoint) >> reduce_range);
472 datapoint >>= reduce_range;
474 y = r.top + x_axis_offset - ((r.bottom - r.top) * datapoint) / (interval_size >> reduce_range);
477 GfxFillRect(x - pointoffs1, y - pointoffs1, x + pointoffs2, y + pointoffs2, colour);
480 if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, colour, linewidth);
485 prev_x = INVALID_DATAPOINT_POS;
486 prev_y = INVALID_DATAPOINT_POS;
498 format_str_y_axis(format_str_y_axis)
501 this->num_vert_lines = 24;
513 wid->SetDataTip(STR_GRAPH_LAST_72_MINUTES_TIME_LABEL, STR_NULL);
524 uint x_label_width = 0;
527 if (this->draw_dates) {
529 TimerGameEconomy::Year year = this->year;
530 for (
int i = 0; i < this->num_on_x_axis; i++) {
531 SetDParam(0, month + STR_MONTH_ABBREV_JAN);
533 x_label_width = std::max(x_label_width,
GetStringBoundingBox(month == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH).
width);
553 size->height = std::max<uint>(size->height, size->width / 3);
556 void DrawWidget(
const Rect &r,
WidgetID widget)
const override
565 return INVALID_DATAPOINT;
568 void OnClick([[maybe_unused]]
Point pt,
WidgetID widget, [[maybe_unused]]
int click_count)
override
584 void OnInvalidateData([[maybe_unused]]
int data = 0, [[maybe_unused]]
bool gui_scope =
true)
override
586 if (!gui_scope)
return;
596 CompanyMask excluded_companies = _legend_excluded_companies;
605 nums = std::min(this->num_vert_lines, std::max(nums, c->num_valid_stat_ent));
615 if (!initialize && this->excluded_data == excluded_companies && this->num_on_x_axis == nums &&
616 this->year == yr && this->month == mo) {
621 this->excluded_data = excluded_companies;
622 this->num_on_x_axis = nums;
631 for (
int j = this->num_on_x_axis, i = 0; --j >= 0;) {
633 this->cost[numd][i] = INVALID_DATAPOINT;
637 this->cost[numd][i] = std::min(GetGraphData(c, j), INVALID_DATAPOINT - 1);
645 this->num_dataset = numd;
673 static constexpr
NWidgetPart _nested_operating_profit_widgets[] = {
687 NWidget(
WWT_TEXT, COLOUR_BROWN,
WID_GRAPH_FOOTER),
SetMinimalSize(0, 6),
SetPadding(2, 0, 2, 0),
SetDataTip(STR_EMPTY, STR_NULL),
695 static WindowDesc _operating_profit_desc(__FILE__, __LINE__,
696 WDP_AUTO,
"graph_operating_profit", 0, 0,
699 std::begin(_nested_operating_profit_widgets), std::end(_nested_operating_profit_widgets)
703 void ShowOperatingProfitGraph()
705 AllocateWindowDescFront<OperatingProfitGraphWindow>(&_operating_profit_desc, 0);
732 static constexpr
NWidgetPart _nested_income_graph_widgets[] = {
746 NWidget(
WWT_TEXT, COLOUR_BROWN,
WID_GRAPH_FOOTER),
SetMinimalSize(0, 6),
SetPadding(2, 0, 2, 0),
SetDataTip(STR_EMPTY, STR_NULL),
754 static WindowDesc _income_graph_desc(__FILE__, __LINE__,
758 std::begin(_nested_income_graph_widgets), std::end(_nested_income_graph_widgets)
761 void ShowIncomeGraph()
763 AllocateWindowDescFront<IncomeGraphWindow>(&_income_graph_desc, 0);
789 static constexpr
NWidgetPart _nested_delivered_cargo_graph_widgets[] = {
803 NWidget(
WWT_TEXT, COLOUR_BROWN,
WID_GRAPH_FOOTER),
SetMinimalSize(0, 6),
SetPadding(2, 0, 2, 0),
SetDataTip(STR_EMPTY, STR_NULL),
811 static WindowDesc _delivered_cargo_graph_desc(__FILE__, __LINE__,
812 WDP_AUTO,
"graph_delivered_cargo", 0, 0,
815 std::begin(_nested_delivered_cargo_graph_widgets), std::end(_nested_delivered_cargo_graph_widgets)
818 void ShowDeliveredCargoGraph()
820 AllocateWindowDescFront<DeliveredCargoGraphWindow>(&_delivered_cargo_graph_desc, 0);
845 void OnClick([[maybe_unused]]
Point pt,
WidgetID widget, [[maybe_unused]]
int click_count)
override
848 this->BaseGraphWindow::OnClick(pt, widget, click_count);
852 static constexpr
NWidgetPart _nested_performance_history_widgets[] = {
867 NWidget(
WWT_TEXT, COLOUR_BROWN,
WID_GRAPH_FOOTER),
SetMinimalSize(0, 6),
SetPadding(2, 0, 2, 0),
SetDataTip(STR_EMPTY, STR_NULL),
875 static WindowDesc _performance_history_desc(__FILE__, __LINE__,
876 WDP_AUTO,
"graph_performance", 0, 0,
879 std::begin(_nested_performance_history_widgets), std::end(_nested_performance_history_widgets)
882 void ShowPerformanceHistoryGraph()
884 AllocateWindowDescFront<PerformanceHistoryGraphWindow>(&_performance_history_desc, 0);
910 static constexpr
NWidgetPart _nested_company_value_graph_widgets[] = {
924 NWidget(
WWT_TEXT, COLOUR_BROWN,
WID_GRAPH_FOOTER),
SetMinimalSize(0, 6),
SetPadding(2, 0, 2, 0),
SetDataTip(STR_EMPTY, STR_NULL),
932 static WindowDesc _company_value_graph_desc(__FILE__, __LINE__,
933 WDP_AUTO,
"graph_company_value", 0, 0,
936 std::begin(_nested_company_value_graph_widgets), std::end(_nested_company_value_graph_widgets)
939 void ShowCompanyValueGraph()
941 AllocateWindowDescFront<CompanyValueGraphWindow>(&_company_value_graph_desc, 0);
956 this->num_on_x_axis = 20;
957 this->num_vert_lines = 20;
958 this->draw_dates =
false;
982 void UpdateExcludedData()
984 this->excluded_data = 0;
988 if (
HasBit(_legend_excluded_cargo, cs->Index()))
SetBit(this->excluded_data, i);
996 BaseGraphWindow::UpdateWidgetSize(widget, size, padding, fill,
resize);
1008 *size =
maxdim(d, *size);
1011 this->line_height = size->height;
1012 size->height = this->line_height * 11;
1017 void DrawWidget(
const Rect &r,
WidgetID widget)
const override
1020 BaseGraphWindow::DrawWidget(r, widget);
1031 if (pos-- > 0)
continue;
1032 if (--max < 0)
break;
1034 bool lowered = !
HasBit(_legend_excluded_cargo, cs->Index());
1050 line = line.
Translate(0, this->line_height);
1054 void OnClick([[maybe_unused]]
Point pt,
WidgetID widget, [[maybe_unused]]
int click_count)
override
1059 _legend_excluded_cargo = 0;
1060 this->excluded_data = 0;
1068 SetBit(_legend_excluded_cargo, cs->Index());
1069 SetBit(this->excluded_data, i);
1079 ToggleBit(_legend_excluded_cargo, (*it)->Index());
1080 this->UpdateExcludedData();
1104 void OnInvalidateData([[maybe_unused]]
int data = 0, [[maybe_unused]]
bool gui_scope =
true)
override
1106 if (!gui_scope)
return;
1120 this->UpdateExcludedData();
1124 this->colours[i] = cs->legend_colour;
1125 for (uint j = 0; j != 20; j++) {
1126 this->cost[i][j] = GetTransportedGoodsIncome(10, 20, j * 4 + 4, cs->Index());
1130 this->num_dataset = i;
1134 static constexpr
NWidgetPart _nested_cargo_payment_rates_widgets[] = {
1145 NWidget(
WWT_TEXT, COLOUR_BROWN,
WID_GRAPH_HEADER),
SetMinimalSize(0, 6),
SetPadding(2, 0, 2, 0),
SetDataTip(STR_GRAPH_CARGO_PAYMENT_RATES_TITLE, STR_NULL),
1156 NWidget(
WWT_MATRIX, COLOUR_BROWN,
WID_CPR_MATRIX),
SetFill(1, 0),
SetResize(0, 2),
SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO),
SetScrollbar(
WID_CPR_MATRIX_SCROLLBAR),
1165 NWidget(
WWT_TEXT, COLOUR_BROWN,
WID_GRAPH_FOOTER),
SetMinimalSize(0, 6),
SetPadding(2, 0, 2, 0),
SetDataTip(STR_NULL, STR_NULL),
1172 static WindowDesc _cargo_payment_rates_desc(__FILE__, __LINE__,
1173 WDP_AUTO,
"graph_cargo_payment_rates", 0, 0,
1176 std::begin(_nested_cargo_payment_rates_widgets), std::end(_nested_cargo_payment_rates_widgets)
1180 void ShowCargoPaymentRates()
1182 AllocateWindowDescFront<PaymentRatesGraphWindow>(&_cargo_payment_rates_desc, 0);
1195 this->UpdateCompanyStats();
1201 void UpdateCompanyStats()
1212 uint score_info_left;
1213 uint score_info_right;
1218 uint score_detail_left;
1219 uint score_detail_right;
1228 uint score_info_width = 0;
1229 for (uint i = SCORE_BEGIN; i <
SCORE_END; i++) {
1244 int max = -(999999999 - 500);
1257 if (_currency->rate < 1000) max /= _currency->rate;
1267 this->score_info_left = rtl ? right - score_info_width :
left;
1268 this->score_info_right = rtl ? right :
left + score_info_width;
1270 this->score_detail_left = rtl ?
left : right - score_detail_width;
1271 this->score_detail_right = rtl ?
left + score_detail_width : right;
1274 this->bar_right = this->bar_left + this->bar_width - 1;
1279 void DrawWidget(
const Rect &r,
WidgetID widget)
const override
1301 int64_t val = _score_part[company][score_type];
1311 uint bar_top =
CenterBounds(r.top, r.bottom, this->bar_height);
1314 DrawString(this->score_info_left, this->score_info_right, text_top, STR_PERFORMANCE_DETAIL_VEHICLES + score_type);
1318 DrawString(this->score_info_left, this->score_info_right, text_top, STR_JUST_COMMA, TC_BLACK,
SA_RIGHT);
1321 uint x = Clamp<int64_t>(val, 0, needed) * this->bar_width / needed;
1324 x = this->bar_right - x;
1326 x = this->bar_left + x;
1330 if (x != this->bar_left)
GfxFillRect(this->bar_left, bar_top, x, bar_top + this->bar_height - 1, rtl ? colour_notdone : colour_done);
1331 if (x != this->bar_right)
GfxFillRect(x, bar_top, this->bar_right, bar_top + this->bar_height - 1, rtl ? colour_done : colour_notdone);
1334 SetDParam(0, Clamp<int64_t>(val, 0, needed) * 100 / needed);
1335 DrawString(this->bar_left, this->bar_right, text_top, STR_PERFORMANCE_DETAIL_PERCENT, TC_FROMSTRING,
SA_HOR_CENTER);
1338 if (score_type == SCORE_LOAN) val = needed - val;
1344 switch (score_type) {
1345 case SCORE_MIN_PROFIT:
1346 case SCORE_MIN_INCOME:
1347 case SCORE_MAX_INCOME:
1350 DrawString(this->score_detail_left, this->score_detail_right, text_top, STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY);
1353 DrawString(this->score_detail_left, this->score_detail_right, text_top, STR_PERFORMANCE_DETAIL_AMOUNT_INT);
1357 void OnClick([[maybe_unused]]
Point pt,
WidgetID widget, [[maybe_unused]]
int click_count)
override
1374 if (--this->timeout == 0) {
1375 this->UpdateCompanyStats();
1385 void OnInvalidateData([[maybe_unused]]
int data = 0, [[maybe_unused]]
bool gui_scope =
true)
override
1387 if (!gui_scope)
return;
1402 this->company = c->index;
1421 const StringID performance_tips[] = {
1422 realtime ? STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP_PERIODS : STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP_YEARS,
1423 STR_PERFORMANCE_DETAIL_STATIONS_TOOLTIP,
1424 realtime ? STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP_PERIODS : STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP_YEARS,
1425 STR_PERFORMANCE_DETAIL_MIN_INCOME_TOOLTIP,
1426 STR_PERFORMANCE_DETAIL_MAX_INCOME_TOOLTIP,
1427 STR_PERFORMANCE_DETAIL_DELIVERED_TOOLTIP,
1428 STR_PERFORMANCE_DETAIL_CARGO_TOOLTIP,
1429 STR_PERFORMANCE_DETAIL_MONEY_TOOLTIP,
1430 STR_PERFORMANCE_DETAIL_LOAN_TOOLTIP,
1431 STR_PERFORMANCE_DETAIL_TOTAL_TOOLTIP,
1436 auto vert = std::make_unique<NWidgetVertical>(
NC_EQUALSIZE);
1438 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, COLOUR_BROWN, widnum);
1439 panel->SetFill(1, 1);
1441 vert->Add(std::move(panel));
1452 static constexpr
NWidgetPart _nested_performance_rating_detail_widgets[] = {
1465 static WindowDesc _performance_rating_detail_desc(__FILE__, __LINE__,
1469 std::begin(_nested_performance_rating_detail_widgets), std::end(_nested_performance_rating_detail_widgets)
1472 void ShowPerformanceRatingDetail()
1474 AllocateWindowDescFront<PerformanceRatingDetailWindow>(&_performance_rating_detail_desc, 0);
1477 void InitializeGraphGui()
1479 _legend_excluded_companies = 0;
1480 _legend_excluded_cargo = 0;