28 #include "table/strings.h"
35 static CompanyMask _legend_excluded_companies;
36 static CargoTypes _legend_excluded_cargo;
40 static const uint INVALID_DATAPOINT_POS = UINT_MAX;
97 if (!gui_scope)
return;
100 SetBit(_legend_excluded_companies, data);
121 panel->
SetDataTip(0x0, STR_GRAPH_KEY_COMPANY_SELECTION_TOOLTIP);
128 static const NWidgetPart _nested_graph_legend_widgets[] = {
150 _nested_graph_legend_widgets,
lengthof(_nested_graph_legend_widgets)
153 static void ShowGraphLegend()
155 AllocateWindowDescFront<GraphLegendWindow>(&_graph_legend_desc, 0);
170 static const int GRAPH_MAX_DATASETS = 64;
171 static const int GRAPH_BASE_COLOUR =
GREY_SCALE(2);
172 static const int GRAPH_GRID_COLOUR =
GREY_SCALE(3);
173 static const int GRAPH_AXIS_LINE_COLOUR =
GREY_SCALE(1);
174 static const int GRAPH_ZERO_LINE_COLOUR =
GREY_SCALE(8);
175 static const int GRAPH_YEAR_LINE_COLOUR =
GREY_SCALE(5);
195 uint16 x_values_start;
196 uint16 x_values_increment;
200 byte colours[GRAPH_MAX_DATASETS];
211 assert(num_hori_lines > 0);
214 current_interval.
highest = INT64_MIN;
215 current_interval.
lowest = INT64_MAX;
217 for (
int i = 0; i < this->num_dataset; i++) {
218 if (
HasBit(this->excluded_data, i))
continue;
219 for (
int j = 0; j < this->num_on_x_axis; j++) {
222 if (datapoint != INVALID_DATAPOINT) {
223 current_interval.
highest = std::max(current_interval.
highest, datapoint);
224 current_interval.
lowest = std::min(current_interval.
lowest, datapoint);
231 current_interval.
lowest = (11 * current_interval.
lowest) / 10;
234 double abs_lower = (current_interval.
lowest > 0) ? 0 : (
double)
abs(current_interval.
lowest);
235 double abs_higher = (current_interval.
highest < 0) ? 0 : (
double)current_interval.
highest;
240 if (abs_lower != 0 || abs_higher != 0) {
242 num_pos_grids = (int)floor(0.5 + num_hori_lines * abs_higher / (abs_higher + abs_lower));
245 if (num_pos_grids == 0 && abs_higher != 0) num_pos_grids++;
246 if (num_pos_grids == num_hori_lines && abs_lower != 0) num_pos_grids--;
249 int64 grid_size_higher = (abs_higher > 0) ? ((int64)abs_higher + num_pos_grids - 1) / num_pos_grids : 0;
250 int64 grid_size_lower = (abs_lower > 0) ? ((int64)abs_lower + num_hori_lines - num_pos_grids - 1) / (num_hori_lines - num_pos_grids) : 0;
251 grid_size = std::max(grid_size_higher, grid_size_lower);
254 num_pos_grids = num_hori_lines / 2;
258 current_interval.
highest = num_pos_grids * grid_size;
259 current_interval.
lowest = -(num_hori_lines - num_pos_grids) * grid_size;
260 return current_interval;
271 int64 y_label = current_interval.
highest;
272 int64 y_label_separation = (current_interval.
highest - current_interval.
lowest) / num_hori_lines;
276 for (
int i = 0; i < (num_hori_lines + 1); i++) {
280 if (d.width > max_width) max_width = d.width;
282 y_label -= y_label_separation;
301 assert(this->num_vert_lines > 0);
320 r.left += label_width;
322 int x_sep = (r.right - r.left) / this->num_vert_lines;
323 int y_sep = (r.bottom - r.top) / num_hori_lines;
327 r.right = r.left + x_sep * this->num_vert_lines;
328 r.bottom = r.top + y_sep * num_hori_lines;
332 x_axis_offset = (int)((r.bottom - r.top) * (double)interval.
highest / (
double)interval_size);
335 GfxFillRect(r.left, r.top, r.right, r.bottom, GRAPH_BASE_COLOUR);
342 for (
int i = 0; i < this->num_vert_lines; i++) {
343 GfxFillRect(x, r.top, x, r.bottom, GRAPH_GRID_COLOUR);
350 for (
int i = 0; i < (num_hori_lines + 1); i++) {
351 GfxFillRect(r.left - 3, y, r.left - 1, y, GRAPH_AXIS_LINE_COLOUR);
352 GfxFillRect(r.left, y, r.right, y, GRAPH_GRID_COLOUR);
357 GfxFillRect(r.left, r.top, r.left, r.bottom, GRAPH_AXIS_LINE_COLOUR);
360 y = x_axis_offset + r.top;
361 GfxFillRect(r.left, y, r.right, y, GRAPH_ZERO_LINE_COLOUR);
364 if (this->num_on_x_axis == 0)
return;
366 assert(this->num_on_x_axis > 0);
367 assert(this->num_dataset > 0);
370 int64 y_label = interval.
highest;
371 int64 y_label_separation =
abs(interval.
highest - interval.
lowest) / num_hori_lines;
375 for (
int i = 0; i < (num_hori_lines + 1); i++) {
380 y_label -= y_label_separation;
385 if (this->month != 0xFF) {
388 byte month = this->month;
389 Year year = this->year;
390 for (
int i = 0; i < this->num_on_x_axis; i++) {
391 SetDParam(0, month + STR_MONTH_ABBREV_JAN);
401 GfxFillRect(x + x_sep, r.top + 1, x + x_sep, r.bottom - 1, GRAPH_YEAR_LINE_COLOUR);
409 uint16 label = this->x_values_start;
411 for (
int i = 0; i < this->num_on_x_axis; i++) {
415 label += this->x_values_increment;
422 uint pointoffs1 = (linewidth + 1) / 2;
423 uint pointoffs2 = linewidth + 1 - pointoffs1;
424 for (
int i = 0; i < this->num_dataset; i++) {
425 if (!
HasBit(this->excluded_data, i)) {
427 x = r.left + (x_sep / 2);
429 byte colour = this->colours[i];
430 uint prev_x = INVALID_DATAPOINT_POS;
431 uint prev_y = INVALID_DATAPOINT_POS;
433 for (
int j = 0; j < this->num_on_x_axis; j++) {
436 if (datapoint != INVALID_DATAPOINT) {
449 int reduce_range = std::max(mult_range - 31, 0);
453 datapoint = -(
abs(datapoint) >> reduce_range);
455 datapoint >>= reduce_range;
457 y = r.top + x_axis_offset - ((r.bottom - r.top) * datapoint) / (interval_size >> reduce_range);
460 GfxFillRect(x - pointoffs1, y - pointoffs1, x + pointoffs2, y + pointoffs2, colour);
463 if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, colour, linewidth);
468 prev_x = INVALID_DATAPOINT_POS;
469 prev_y = INVALID_DATAPOINT_POS;
481 format_str_y_axis(format_str_y_axis)
484 this->num_vert_lines = 24;
485 this->graph_widget = widget;
499 if (widget != this->graph_widget)
return;
501 uint x_label_width = 0;
504 if (this->month != 0xFF) {
505 byte month = this->month;
506 Year year = this->year;
507 for (
int i = 0; i < this->num_on_x_axis; i++) {
508 SetDParam(0, month + STR_MONTH_ABBREV_JAN);
510 x_label_width = std::max(x_label_width,
GetStringBoundingBox(month == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH).
width);
528 size->width = std::max<uint>(size->width, 5 + y_label_width + this->num_on_x_axis * (x_label_width + 5) + 9);
530 size->height = std::max<uint>(size->height, size->width / 3);
535 if (widget != this->graph_widget)
return;
542 return INVALID_DATAPOINT;
563 if (!gui_scope)
return;
573 CompanyMask excluded_companies = _legend_excluded_companies;
582 nums = std::min(this->num_vert_lines, std::max(nums, c->num_valid_stat_ent));
592 if (!initialize && this->excluded_data == excluded_companies && this->num_on_x_axis == nums &&
593 this->year == yr && this->month == mo) {
598 this->excluded_data = excluded_companies;
599 this->num_on_x_axis = nums;
608 for (
int j = this->num_on_x_axis, i = 0; --j >= 0;) {
609 this->cost[numd][i] = (j >= c->
num_valid_stat_ent) ? INVALID_DATAPOINT : GetGraphData(c, j);
616 this->num_dataset = numd;
638 static const NWidgetPart _nested_operating_profit_widgets[] = {
659 WDP_AUTO,
"graph_operating_profit", 0, 0,
662 _nested_operating_profit_widgets,
lengthof(_nested_operating_profit_widgets)
666 void ShowOperatingProfitGraph()
668 AllocateWindowDescFront<OperatingProfitGraphWindow>(&_operating_profit_desc, 0);
689 static const NWidgetPart _nested_income_graph_widgets[] = {
713 _nested_income_graph_widgets,
lengthof(_nested_income_graph_widgets)
716 void ShowIncomeGraph()
718 AllocateWindowDescFront<IncomeGraphWindow>(&_income_graph_desc, 0);
738 static const NWidgetPart _nested_delivered_cargo_graph_widgets[] = {
758 static WindowDesc _delivered_cargo_graph_desc(
759 WDP_AUTO,
"graph_delivered_cargo", 0, 0,
762 _nested_delivered_cargo_graph_widgets,
lengthof(_nested_delivered_cargo_graph_widgets)
765 void ShowDeliveredCargoGraph()
767 AllocateWindowDescFront<DeliveredCargoGraphWindow>(&_delivered_cargo_graph_desc, 0);
793 static const NWidgetPart _nested_performance_history_widgets[] = {
815 WDP_AUTO,
"graph_performance", 0, 0,
818 _nested_performance_history_widgets,
lengthof(_nested_performance_history_widgets)
821 void ShowPerformanceHistoryGraph()
823 AllocateWindowDescFront<PerformanceHistoryGraphWindow>(&_performance_history_desc, 0);
843 static const NWidgetPart _nested_company_value_graph_widgets[] = {
864 WDP_AUTO,
"graph_company_value", 0, 0,
867 _nested_company_value_graph_widgets,
lengthof(_nested_company_value_graph_widgets)
870 void ShowCompanyValueGraph()
872 AllocateWindowDescFront<CompanyValueGraphWindow>(&_company_value_graph_desc, 0);
887 this->num_on_x_axis = 20;
888 this->num_vert_lines = 20;
890 this->x_values_start = 10;
891 this->x_values_increment = 10;
909 void UpdateExcludedData()
930 d.width += this->legend_width + 4;
936 this->line_height = size->height;
937 size->height = this->line_height * 11;
960 if (pos-- > 0)
continue;
961 if (--max < 0)
break;
963 bool lowered = !
HasBit(_legend_excluded_cargo, cs->Index());
968 byte clk_dif = lowered ? 1 : 0;
971 GfxFillRect(rect_x, y + padding + clk_dif, rect_x + this->legend_width, y + row_height - 1 + clk_dif,
PC_BLACK);
972 GfxFillRect(rect_x + 1, y + padding + 1 + clk_dif, rect_x + this->legend_width - 1, y + row_height - 2 + clk_dif, cs->legend_colour);
974 DrawString(rtl ? r.left : x + this->legend_width + 4 + clk_dif, (rtl ? r.right - this->legend_width - 4 + clk_dif : r.right), y + clk_dif, STR_GRAPH_CARGO_PAYMENT_CARGO);
985 _legend_excluded_cargo = 0;
994 SetBit(_legend_excluded_cargo, cs->Index());
1004 if (row >= this->vscroll->
GetCount())
return;
1007 if (row-- > 0)
continue;
1009 ToggleBit(_legend_excluded_cargo, cs->Index());
1010 this->UpdateExcludedData();
1036 if (!gui_scope)
return;
1042 this->UpdateExcludedData();
1046 this->colours[i] = cs->legend_colour;
1047 for (uint j = 0; j != 20; j++) {
1048 this->
cost[i][j] = GetTransportedGoodsIncome(10, 20, j * 4 + 4, cs->Index());
1052 this->num_dataset = i;
1056 static const NWidgetPart _nested_cargo_payment_rates_widgets[] = {
1067 NWidget(
WWT_TEXT, COLOUR_BROWN,
WID_CPR_HEADER),
SetMinimalSize(0, 6),
SetPadding(2, 0, 2, 0),
SetDataTip(STR_GRAPH_CARGO_PAYMENT_RATES_TITLE, STR_NULL),
1087 NWidget(
WWT_TEXT, COLOUR_BROWN,
WID_CPR_FOOTER),
SetMinimalSize(0, 6),
SetPadding(2, 0, 2, 0),
SetDataTip(STR_GRAPH_CARGO_PAYMENT_RATES_X_LABEL, STR_NULL),
1095 WDP_AUTO,
"graph_cargo_payment_rates", 0, 0,
1098 _nested_cargo_payment_rates_widgets,
lengthof(_nested_cargo_payment_rates_widgets)
1102 void ShowCargoPaymentRates()
1104 AllocateWindowDescFront<PaymentRatesGraphWindow>(&_cargo_payment_rates_desc, 0);
1111 static const StringID _performance_titles[] = {
1112 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_ENGINEER,
1113 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_ENGINEER,
1114 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TRAFFIC_MANAGER,
1115 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TRAFFIC_MANAGER,
1116 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TRANSPORT_COORDINATOR,
1117 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TRANSPORT_COORDINATOR,
1118 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_ROUTE_SUPERVISOR,
1119 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_ROUTE_SUPERVISOR,
1120 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_DIRECTOR,
1121 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_DIRECTOR,
1122 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_CHIEF_EXECUTIVE,
1123 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_CHIEF_EXECUTIVE,
1124 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_CHAIRMAN,
1125 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_CHAIRMAN,
1126 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_PRESIDENT,
1127 STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TYCOON,
1130 static inline StringID GetPerformanceTitleFromValue(uint value)
1132 return _performance_titles[std::min(value, 1000u) >> 6];
1150 this->companies.clear();
1153 this->companies.push_back(c);
1156 this->companies.shrink_to_fit();
1196 for (uint i = 0; i != this->companies.size(); i++) {
1197 const Company *c = this->companies[i];
1198 DrawString(ordinal_left, ordinal_right, y, i + STR_ORDINAL_NUMBER_1ST, i == 0 ? TC_WHITE : TC_YELLOW);
1205 DrawString(text_left, text_right, y, STR_COMPANY_LEAGUE_COMPANY_NAME);
1214 this->ordinal_width = 0;
1218 this->ordinal_width += 5;
1220 uint widest_width = 0;
1221 uint widest_title = 0;
1222 for (uint i = 0; i <
lengthof(_performance_titles); i++) {
1224 if (
width > widest_width) {
1226 widest_width =
width;
1231 this->icon_width = d.width + 2;
1237 SetDParam(2, _performance_titles[widest_title]);
1241 this->text_width = widest_width + 30;
1271 static const NWidgetPart _nested_company_league_widgets[] = {
1285 _nested_company_league_widgets,
lengthof(_nested_company_league_widgets)
1288 void ShowCompanyLeagueTable()
1290 AllocateWindowDescFront<CompanyLeagueWindow>(&_company_league_desc, 0);
1303 this->UpdateCompanyStats();
1309 void UpdateCompanyStats()
1320 uint score_info_left;
1321 uint score_info_right;
1326 uint score_detail_left;
1327 uint score_detail_right;
1336 uint score_info_width = 0;
1337 for (uint i = SCORE_BEGIN; i <
SCORE_END; i++) {
1352 int max = -(999999999 - 500);
1365 if (_currency->rate < 1000) max /= _currency->rate;
1370 size->width = 7 + score_info_width + 5 + this->bar_width + 5 + score_detail_width + 7;
1372 uint right = size->width - 7;
1375 this->score_info_left = rtl ? right - score_info_width :
left;
1376 this->score_info_right = rtl ? right :
left + score_info_width;
1378 this->score_detail_left = rtl ?
left : right - score_detail_width;
1379 this->score_detail_right = rtl ?
left + score_detail_width : right;
1381 this->bar_left =
left + (rtl ? score_detail_width : score_info_width) + 5;
1382 this->bar_right = this->bar_left + this->bar_width;
1395 int offset = (cid == this->company) ? 1 : 0;
1397 DrawCompanyIcon(cid, (r.left + r.right - sprite_size.width) / 2 + offset, (r.top + r.bottom - sprite_size.height) / 2 + offset);
1410 int64 val = _score_part[company][score_type];
1421 uint text_top = bar_top + 2;
1423 DrawString(this->score_info_left, this->score_info_right, text_top, STR_PERFORMANCE_DETAIL_VEHICLES + score_type);
1427 DrawString(this->score_info_left, this->score_info_right, text_top, STR_BLACK_COMMA, TC_FROMSTRING,
SA_RIGHT);
1430 uint x = Clamp<int64>(val, 0, needed) * this->bar_width / needed;
1433 x = this->bar_right - x;
1435 x = this->bar_left + x;
1439 if (x != this->bar_left)
GfxFillRect(this->bar_left, bar_top, x, bar_top + this->bar_height, rtl ? colour_notdone : colour_done);
1440 if (x != this->bar_right)
GfxFillRect(x, bar_top, this->bar_right, bar_top + this->bar_height, rtl ? colour_done : colour_notdone);
1443 SetDParam(0, Clamp<int64>(val, 0, needed) * 100 / needed);
1444 DrawString(this->bar_left, this->bar_right, text_top, STR_PERFORMANCE_DETAIL_PERCENT, TC_FROMSTRING,
SA_HOR_CENTER);
1447 if (score_type == SCORE_LOAN) val = needed - val;
1453 switch (score_type) {
1454 case SCORE_MIN_PROFIT:
1455 case SCORE_MIN_INCOME:
1456 case SCORE_MAX_INCOME:
1459 DrawString(this->score_detail_left, this->score_detail_right, text_top, STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY);
1462 DrawString(this->score_detail_left, this->score_detail_right, text_top, STR_PERFORMANCE_DETAIL_AMOUNT_INT);
1483 if (--this->timeout == 0) {
1484 this->UpdateCompanyStats();
1496 if (!gui_scope)
return;
1511 this->company = c->index;
1531 const StringID performance_tips[] = {
1532 STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP,
1533 STR_PERFORMANCE_DETAIL_STATIONS_TOOLTIP,
1534 STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP,
1535 STR_PERFORMANCE_DETAIL_MIN_INCOME_TOOLTIP,
1536 STR_PERFORMANCE_DETAIL_MAX_INCOME_TOOLTIP,
1537 STR_PERFORMANCE_DETAIL_DELIVERED_TOOLTIP,
1538 STR_PERFORMANCE_DETAIL_CARGO_TOOLTIP,
1539 STR_PERFORMANCE_DETAIL_MONEY_TOOLTIP,
1540 STR_PERFORMANCE_DETAIL_LOAN_TOOLTIP,
1541 STR_PERFORMANCE_DETAIL_TOTAL_TOOLTIP,
1563 static const NWidgetPart _nested_performance_rating_detail_widgets[] = {
1576 static WindowDesc _performance_rating_detail_desc(
1580 _nested_performance_rating_detail_widgets,
lengthof(_nested_performance_rating_detail_widgets)
1583 void ShowPerformanceRatingDetail()
1585 AllocateWindowDescFront<PerformanceRatingDetailWindow>(&_performance_rating_detail_desc, 0);
1588 void InitializeGraphGui()
1590 _legend_excluded_companies = 0;
1591 _legend_excluded_cargo = 0;