tron@2186: /* $Id$ */ tron@2186: belugas@6505: /** @file graph_gui.cpp */ belugas@6505: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@507: #include "table/strings.h" rubidium@7762: #include "strings.h" celestar@2187: #include "table/sprites.h" tron@2163: #include "functions.h" truelight@0: #include "window.h" truelight@0: #include "gui.h" truelight@0: #include "gfx.h" truelight@0: #include "player.h" dominik@116: #include "economy.h" tron@2159: #include "variables.h" rubidium@4261: #include "date.h" rubidium@5838: #include "helpers.hpp" peter1138@6417: #include "cargotype.h" Darkvater@5291: maedhros@6057: /* Bitmasks of player and cargo indices that shouldn't be drawn. */ maedhros@6057: static uint _legend_excluded_players; maedhros@6057: static uint _legend_excluded_cargo; truelight@0: truelight@0: /************************/ truelight@0: /* GENERIC GRAPH DRAWER */ truelight@0: /************************/ truelight@0: maedhros@6019: enum { peter1138@6959: GRAPH_MAX_DATASETS = 32, belugas@8320: GRAPH_AXIS_LABEL_COLOUR = TC_BLACK, maedhros@6019: GRAPH_AXIS_LINE_COLOUR = 215, maedhros@6027: belugas@6505: GRAPH_X_POSITION_BEGINNING = 44, ///< Start the graph 44 pixels from gw->left belugas@6505: GRAPH_X_POSITION_SEPARATION = 22, ///< There are 22 pixels between each X value maedhros@6027: belugas@6505: GRAPH_NUM_LINES_Y = 9, ///< How many horizontal lines to draw. belugas@6505: /* 9 is convenient as that means the distance between them is the height of the graph / 8, belugas@6505: * which is the same maedhros@6027: * as height >> 3. */ maedhros@6019: }; darkvater@236: maedhros@6056: /* Apparently these don't play well with enums. */ rubidium@8259: static const OverflowSafeInt64 INVALID_DATAPOINT = INT64_MAX; // Value used for a datapoint that shouldn't be drawn. rubidium@8259: static const uint INVALID_DATAPOINT_POS = UINT_MAX; // Used to determine if the previous point was drawn. maedhros@6056: rubidium@6574: struct GraphDrawer { belugas@6505: uint excluded_data; ///< bitmask of the datasets that shouldn't be displayed. truelight@0: byte num_dataset; truelight@0: byte num_on_x_axis; maedhros@6058: bool has_negative_values; maedhros@6022: byte num_vert_lines; maedhros@6022: maedhros@6022: /* The starting month and year that values are plotted against. If month is maedhros@6022: * 0xFF, use x_values_start and x_values_increment below instead. */ truelight@0: byte month; rubidium@4293: Year year; maedhros@6022: maedhros@6022: /* These values are used if the graph is being plotted against values maedhros@6022: * rather than the dates specified by month and year. */ maedhros@6022: uint16 x_values_start; maedhros@6022: uint16 x_values_increment; maedhros@6022: belugas@6505: int left, top; ///< Where to start drawing the graph, in pixels. belugas@6505: uint height; ///< The height of the graph in pixels. truelight@0: StringID format_str_y_axis; maedhros@6019: byte colors[GRAPH_MAX_DATASETS]; rubidium@7487: Money cost[GRAPH_MAX_DATASETS][24]; ///< last 2 years rubidium@6574: }; truelight@0: Darkvater@2436: static void DrawGraph(const GraphDrawer *gw) truelight@0: { rubidium@8259: uint x, y; ///< Reused whenever x and y coordinates are needed. rubidium@8259: OverflowSafeInt64 highest_value; ///< Highest value to be drawn. rubidium@8259: int x_axis_offset; ///< Distance from the top of the graph to the x axis. truelight@0: darkvater@240: /* the colors and cost array of GraphDrawer must accomodate darkvater@236: * both values for cargo and players. So if any are higher, quit */ maedhros@6019: assert(GRAPH_MAX_DATASETS >= (int)NUM_CARGO && GRAPH_MAX_DATASETS >= (int)MAX_PLAYERS); maedhros@6027: assert(gw->num_vert_lines > 0); maedhros@6027: maedhros@6017: byte grid_colour = _colour_gradient[14][4]; truelight@0: maedhros@6058: /* The coordinates of the opposite edges of the graph. */ maedhros@6027: int bottom = gw->top + gw->height - 1; maedhros@6058: int right = gw->left + GRAPH_X_POSITION_BEGINNING + gw->num_vert_lines * GRAPH_X_POSITION_SEPARATION - 1; maedhros@6027: maedhros@6058: /* Draw the vertical grid lines. */ maedhros@6027: maedhros@6027: /* Don't draw the first line, as that's where the axis will be. */ maedhros@6027: x = gw->left + GRAPH_X_POSITION_BEGINNING + GRAPH_X_POSITION_SEPARATION; maedhros@6027: maedhros@6027: for (int i = 0; i < gw->num_vert_lines; i++) { maedhros@6017: GfxFillRect(x, gw->top, x, bottom, grid_colour); maedhros@6027: x += GRAPH_X_POSITION_SEPARATION; maedhros@6027: } truelight@193: maedhros@6058: /* Draw the horizontal grid lines. */ maedhros@6027: x = gw->left + GRAPH_X_POSITION_BEGINNING; truelight@0: y = gw->height + gw->top; truelight@0: maedhros@6027: for (int i = 0; i < GRAPH_NUM_LINES_Y; i++) { maedhros@6017: GfxFillRect(x, y, right, y, grid_colour); maedhros@6027: y -= (gw->height / (GRAPH_NUM_LINES_Y - 1)); maedhros@6027: } truelight@0: maedhros@6058: /* Draw the y axis. */ maedhros@6019: GfxFillRect(x, gw->top, x, bottom, GRAPH_AXIS_LINE_COLOUR); truelight@0: maedhros@6058: /* Find the distance from the top of the graph to the x axis. */ maedhros@6058: x_axis_offset = gw->height; truelight@0: maedhros@6058: /* The graph is currently symmetrical about the x axis. */ maedhros@6058: if (gw->has_negative_values) x_axis_offset /= 2; maedhros@6058: maedhros@6058: /* Draw the x axis. */ maedhros@6058: y = x_axis_offset + gw->top; maedhros@6019: GfxFillRect(x, y, right, y, GRAPH_AXIS_LINE_COLOUR); truelight@0: maedhros@6058: /* Find the largest value that will be drawn. */ truelight@0: if (gw->num_on_x_axis == 0) truelight@0: return; truelight@0: maedhros@6027: assert(gw->num_on_x_axis > 0); maedhros@6029: assert(gw->num_dataset > 0); darkvater@236: maedhros@6055: /* Start of with a value of twice the height of the graph in pixels. It's a maedhros@6055: * bit arbitrary, but it makes the cargo payment graph look a little nicer, maedhros@6055: * and prevents division by zero when calculating where the datapoint maedhros@6055: * should be drawn. */ maedhros@6058: highest_value = x_axis_offset * 2; maedhros@6047: maedhros@6029: for (int i = 0; i < gw->num_dataset; i++) { skidd13@8424: if (!HasBit(gw->excluded_data, i)) { maedhros@6042: for (int j = 0; j < gw->num_on_x_axis; j++) { rubidium@7487: Money datapoint = gw->cost[i][j]; maedhros@6042: maedhros@6056: if (datapoint != INVALID_DATAPOINT) { maedhros@6047: /* For now, if the graph has negative values the scaling is maedhros@6047: * symmetrical about the x axis, so take the absolute value maedhros@6047: * of each data point. */ skidd13@8419: highest_value = max(highest_value, abs(datapoint)); darkvater@236: } maedhros@6029: } darkvater@236: } maedhros@6029: } truelight@193: maedhros@6055: /* Round up highest_value so that it will divide cleanly into the number of maedhros@6055: * axis labels used. */ maedhros@6055: int round_val = highest_value % (GRAPH_NUM_LINES_Y - 1); maedhros@6055: if (round_val != 0) highest_value += (GRAPH_NUM_LINES_Y - 1 - round_val); truelight@0: truelight@0: /* draw text strings on the y axis */ maedhros@6055: int64 y_label = highest_value; maedhros@6055: int64 y_label_separation = highest_value / (GRAPH_NUM_LINES_Y - 1); maedhros@6055: maedhros@6055: /* If there are negative values, the graph goes from highest_value to maedhros@6055: * -highest_value, not highest_value to 0. */ maedhros@6058: if (gw->has_negative_values) y_label_separation *= 2; maedhros@6055: maedhros@6027: x = gw->left + GRAPH_X_POSITION_BEGINNING + 1; truelight@0: y = gw->top - 3; maedhros@6027: maedhros@6027: for (int i = 0; i < GRAPH_NUM_LINES_Y; i++) { tron@534: SetDParam(0, gw->format_str_y_axis); rubidium@7498: SetDParam(1, y_label); maedhros@6019: DrawStringRightAligned(x, y, STR_0170, GRAPH_AXIS_LABEL_COLOUR); maedhros@6055: maedhros@6055: y_label -= y_label_separation; maedhros@6027: y += (gw->height / (GRAPH_NUM_LINES_Y - 1)); maedhros@6027: } truelight@0: truelight@0: /* draw strings on the x axis */ truelight@0: if (gw->month != 0xFF) { maedhros@6027: x = gw->left + GRAPH_X_POSITION_BEGINNING; truelight@0: y = gw->top + gw->height + 1; maedhros@6028: byte month = gw->month; maedhros@6028: Year year = gw->year; maedhros@6028: for (int i = 0; i < gw->num_on_x_axis; i++) { maedhros@6028: SetDParam(0, month + STR_0162_JAN); maedhros@6028: SetDParam(1, month + STR_0162_JAN + 2); maedhros@6028: SetDParam(2, year); maedhros@6028: DrawString(x, y, month == 0 ? STR_016F : STR_016E, GRAPH_AXIS_LABEL_COLOUR); truelight@0: maedhros@6028: month += 3; maedhros@6028: if (month >= 12) { maedhros@6028: month = 0; maedhros@6028: year++; truelight@0: } maedhros@6027: x += GRAPH_X_POSITION_SEPARATION; maedhros@6028: } truelight@0: } else { maedhros@6044: /* Draw the label under the data point rather than on the grid line. */ maedhros@6044: x = gw->left + GRAPH_X_POSITION_BEGINNING + (GRAPH_X_POSITION_SEPARATION / 2) + 1; truelight@0: y = gw->top + gw->height + 1; maedhros@6022: uint16 label = gw->x_values_start; maedhros@6022: maedhros@6022: for (int i = 0; i < gw->num_on_x_axis; i++) { maedhros@6022: SetDParam(0, label); maedhros@6044: DrawStringCentered(x, y, STR_01CB, GRAPH_AXIS_LABEL_COLOUR); maedhros@6044: maedhros@6022: label += gw->x_values_increment; maedhros@6027: x += GRAPH_X_POSITION_SEPARATION; maedhros@6022: } truelight@0: } truelight@0: truelight@0: /* draw lines and dots */ maedhros@6028: for (int i = 0; i < gw->num_dataset; i++) { skidd13@8424: if (!HasBit(gw->excluded_data, i)) { maedhros@6027: /* Centre the dot between the grid lines. */ maedhros@6027: x = gw->left + GRAPH_X_POSITION_BEGINNING + (GRAPH_X_POSITION_SEPARATION / 2); maedhros@6042: maedhros@6056: byte color = gw->colors[i]; maedhros@6056: uint prev_x = INVALID_DATAPOINT_POS; maedhros@6056: uint prev_y = INVALID_DATAPOINT_POS; maedhros@6027: maedhros@6042: for (int j = 0; j < gw->num_on_x_axis; j++) { rubidium@7487: Money datapoint = gw->cost[i][j]; maedhros@6042: maedhros@6056: if (datapoint != INVALID_DATAPOINT) { maedhros@6058: /* XXX: This can overflow if x_axis_offset * datapoint is maedhros@6058: * too big to fit in an int64. */ maedhros@6058: y = gw->top + x_axis_offset - (x_axis_offset * datapoint) / highest_value; truelight@0: maedhros@6056: /* Draw the point. */ rubidium@6987: GfxFillRect(x - 1, y - 1, x + 1, y + 1, color); truelight@0: maedhros@6056: /* Draw the line connected to the previous point. */ maedhros@6056: if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, color); maedhros@6056: maedhros@6056: prev_x = x; maedhros@6056: prev_y = y; truelight@0: } else { maedhros@6056: prev_x = INVALID_DATAPOINT_POS; maedhros@6056: prev_y = INVALID_DATAPOINT_POS; truelight@0: } maedhros@6056: maedhros@6027: x += GRAPH_X_POSITION_SEPARATION; maedhros@6027: } truelight@0: } maedhros@6028: } truelight@0: } truelight@0: truelight@0: /****************/ truelight@0: /* GRAPH LEGEND */ truelight@0: /****************/ truelight@0: truelight@0: static void GraphLegendWndProc(Window *w, WindowEvent *e) truelight@0: { tron@2952: switch (e->event) { peter1138@6529: case WE_CREATE: peter1138@6529: for (uint i = 3; i < w->widget_count; i++) { skidd13@8424: if (!HasBit(_legend_excluded_players, i - 3)) LowerWindowWidget(w, i); peter1138@6529: } peter1138@6529: break; belugas@4719: peter1138@6529: case WE_PAINT: { peter1138@6529: const Player *p; peter1138@6529: peter1138@6529: FOR_ALL_PLAYERS(p) { peter1138@6529: if (p->is_active) continue; peter1138@6529: skidd13@8427: SetBit(_legend_excluded_players, p->index); belugas@4719: RaiseWindowWidget(w, p->index + 3); belugas@4719: } truelight@0: peter1138@6529: DrawWindowWidgets(w); peter1138@6529: peter1138@6529: FOR_ALL_PLAYERS(p) { peter1138@6529: if (!p->is_active) continue; peter1138@6529: rubidium@6987: DrawPlayerIcon(p->index, 4, 18 + p->index * 12); peter1138@6529: peter1138@7554: SetDParam(0, p->index); peter1138@7554: SetDParam(1, p->index); skidd13@8424: DrawString(21, 17 + p->index * 12, STR_7021, HasBit(_legend_excluded_players, p->index) ? TC_BLACK : TC_WHITE); peter1138@6529: } peter1138@6529: break; truelight@0: } truelight@0: peter1138@6529: case WE_CLICK: skidd13@8450: if (!IsInsideMM(e->we.click.widget, 3, 11)) return; peter1138@6529: skidd13@8428: ToggleBit(_legend_excluded_players, e->we.click.widget - 3); belugas@4719: ToggleWidgetLoweredState(w, e->we.click.widget); truelight@0: SetWindowDirty(w); truelight@0: InvalidateWindow(WC_INCOME_GRAPH, 0); truelight@0: InvalidateWindow(WC_OPERATING_PROFIT, 0); truelight@0: InvalidateWindow(WC_DELIVERED_CARGO, 0); truelight@0: InvalidateWindow(WC_PERFORMANCE_HISTORY, 0); truelight@140: InvalidateWindow(WC_COMPANY_VALUE, 0); peter1138@6529: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static const Widget _graph_legend_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 14, 11, 249, 0, 13, STR_704E_KEY_TO_COMPANY_GRAPHS, STR_018C_WINDOW_TITLE_DRAG_THIS}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 249, 14, 113, 0x0, STR_NULL}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 2, 247, 16, 27, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 2, 247, 28, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 2, 247, 40, 51, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 2, 247, 52, 63, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 2, 247, 64, 75, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 2, 247, 76, 87, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 2, 247, 88, 99, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 2, 247, 100, 111, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _graph_legend_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 250, 114, 250, 114, rubidium@6144: WC_GRAPH_LEGEND, WC_NONE, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, truelight@0: _graph_legend_widgets, truelight@0: GraphLegendWndProc truelight@0: }; truelight@0: rubidium@6573: static void ShowGraphLegend() truelight@0: { truelight@0: AllocateWindowDescFront(&_graph_legend_desc, 0); truelight@0: } truelight@0: truelight@0: /********************/ truelight@0: /* OPERATING PROFIT */ truelight@0: /********************/ truelight@0: truelight@0: static void SetupGraphDrawerForPlayers(GraphDrawer *gd) truelight@0: { tron@2475: const Player* p; maedhros@6057: uint excluded_players = _legend_excluded_players; celestar@5852: byte nums; rubidium@6987: int mo, yr; truelight@0: belugas@6505: /* Exclude the players which aren't valid */ truelight@0: FOR_ALL_PLAYERS(p) { skidd13@8427: if (!p->is_active) SetBit(excluded_players, p->index); truelight@193: } maedhros@6057: gd->excluded_data = excluded_players; truelight@0: gd->num_vert_lines = 24; truelight@0: truelight@0: nums = 0; truelight@0: FOR_ALL_PLAYERS(p) { peter1138@6529: if (p->is_active) nums = max(nums, p->num_valid_stat_ent); truelight@0: } peter1138@6529: gd->num_on_x_axis = min(nums, 24); truelight@0: peter1138@6529: mo = (_cur_month / 3 - nums) * 3; truelight@0: yr = _cur_year; truelight@0: while (mo < 0) { truelight@0: yr--; truelight@0: mo += 12; truelight@0: } truelight@0: truelight@0: gd->year = yr; truelight@0: gd->month = mo; truelight@0: } truelight@0: truelight@0: static void OperatingProfitWndProc(Window *w, WindowEvent *e) truelight@0: { tron@2952: switch (e->event) { peter1138@6529: case WE_PAINT: { peter1138@6529: GraphDrawer gd; peter1138@6529: const Player* p; truelight@193: peter1138@6529: DrawWindowWidgets(w); truelight@0: peter1138@6529: gd.left = 2; peter1138@6529: gd.top = 18; peter1138@6529: gd.height = 136; peter1138@6529: gd.has_negative_values = true; peter1138@6529: gd.format_str_y_axis = STR_CURRCOMPACT; peter1138@6529: peter1138@6529: SetupGraphDrawerForPlayers(&gd); peter1138@6529: peter1138@6529: int numd = 0; peter1138@6529: FOR_ALL_PLAYERS(p) { peter1138@6529: if (p->is_active) { peter1138@6529: gd.colors[numd] = _colour_gradient[p->player_color][6]; peter1138@6529: for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { peter1138@6529: gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (p->old_economy[j].income + p->old_economy[j].expenses); peter1138@6529: i++; peter1138@6529: } darkvater@1086: } peter1138@6529: numd++; darkvater@1086: } peter1138@6529: peter1138@6529: gd.num_dataset = numd; peter1138@6529: peter1138@6529: DrawGraph(&gd); peter1138@6529: break; truelight@0: } dominik@1085: peter1138@6529: case WE_CLICK: peter1138@6529: /* Clicked on legend? */ peter1138@6529: if (e->we.click.widget == 2) ShowGraphLegend(); peter1138@6529: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static const Widget _operating_profit_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 14, 11, 525, 0, 13, STR_7025_OPERATING_PROFIT_GRAPH, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@4344: { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 526, 575, 0, 13, STR_704C_KEY, STR_704D_SHOW_KEY_TO_GRAPHS}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 575, 14, 173, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _operating_profit_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 576, 174, 576, 174, rubidium@6144: WC_OPERATING_PROFIT, WC_NONE, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, truelight@0: _operating_profit_widgets, truelight@0: OperatingProfitWndProc truelight@0: }; truelight@0: truelight@0: rubidium@6573: void ShowOperatingProfitGraph() truelight@0: { truelight@0: if (AllocateWindowDescFront(&_operating_profit_desc, 0)) { truelight@0: InvalidateWindow(WC_GRAPH_LEGEND, 0); truelight@0: } truelight@0: } truelight@0: truelight@0: truelight@0: /****************/ truelight@0: /* INCOME GRAPH */ truelight@0: /****************/ truelight@0: truelight@0: static void IncomeGraphWndProc(Window *w, WindowEvent *e) truelight@0: { tron@2952: switch (e->event) { peter1138@6529: case WE_PAINT: { peter1138@6529: GraphDrawer gd; peter1138@6529: const Player* p; truelight@0: peter1138@6529: DrawWindowWidgets(w); truelight@0: peter1138@6529: gd.left = 2; peter1138@6529: gd.top = 18; peter1138@6529: gd.height = 104; peter1138@6529: gd.has_negative_values = false; peter1138@6529: gd.format_str_y_axis = STR_CURRCOMPACT; peter1138@6529: SetupGraphDrawerForPlayers(&gd); peter1138@6529: peter1138@6529: int numd = 0; peter1138@6529: FOR_ALL_PLAYERS(p) { peter1138@6529: if (p->is_active) { peter1138@6529: gd.colors[numd] = _colour_gradient[p->player_color][6]; peter1138@6529: for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { peter1138@6529: gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].income; peter1138@6529: i++; peter1138@6529: } darkvater@1086: } peter1138@6529: numd++; darkvater@1086: } peter1138@6529: peter1138@6529: gd.num_dataset = numd; peter1138@6529: peter1138@6529: DrawGraph(&gd); peter1138@6529: break; truelight@0: } truelight@0: peter1138@6529: case WE_CLICK: peter1138@6529: if (e->we.click.widget == 2) ShowGraphLegend(); peter1138@6529: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static const Widget _income_graph_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 14, 11, 525, 0, 13, STR_7022_INCOME_GRAPH, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@4344: { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 526, 575, 0, 13, STR_704C_KEY, STR_704D_SHOW_KEY_TO_GRAPHS}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 575, 14, 141, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _income_graph_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 576, 142, 576, 142, rubidium@6144: WC_INCOME_GRAPH, WC_NONE, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, truelight@0: _income_graph_widgets, truelight@0: IncomeGraphWndProc truelight@0: }; truelight@0: rubidium@6573: void ShowIncomeGraph() truelight@0: { truelight@0: if (AllocateWindowDescFront(&_income_graph_desc, 0)) { truelight@0: InvalidateWindow(WC_GRAPH_LEGEND, 0); truelight@0: } truelight@0: } truelight@0: truelight@0: /*******************/ truelight@0: /* DELIVERED CARGO */ truelight@0: /*******************/ truelight@0: truelight@0: static void DeliveredCargoGraphWndProc(Window *w, WindowEvent *e) truelight@0: { tron@2952: switch (e->event) { peter1138@6529: case WE_PAINT: { peter1138@6529: GraphDrawer gd; peter1138@6529: const Player* p; truelight@0: peter1138@6529: DrawWindowWidgets(w); truelight@0: peter1138@6529: gd.left = 2; peter1138@6529: gd.top = 18; peter1138@6529: gd.height = 104; peter1138@6529: gd.has_negative_values = false; peter1138@6529: gd.format_str_y_axis = STR_7024; peter1138@6529: SetupGraphDrawerForPlayers(&gd); peter1138@6529: peter1138@6529: int numd = 0; peter1138@6529: FOR_ALL_PLAYERS(p) { peter1138@6529: if (p->is_active) { peter1138@6529: gd.colors[numd] = _colour_gradient[p->player_color][6]; peter1138@6529: for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { rubidium@8259: gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (OverflowSafeInt64)p->old_economy[j].delivered_cargo; peter1138@6529: i++; peter1138@6529: } darkvater@1086: } peter1138@6529: numd++; darkvater@1086: } peter1138@6529: peter1138@6529: gd.num_dataset = numd; peter1138@6529: peter1138@6529: DrawGraph(&gd); peter1138@6529: break; truelight@0: } truelight@0: peter1138@6529: case WE_CLICK: peter1138@6529: if (e->we.click.widget == 2) ShowGraphLegend(); peter1138@6529: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static const Widget _delivered_cargo_graph_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 14, 11, 525, 0, 13, STR_7050_UNITS_OF_CARGO_DELIVERED, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@4344: { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 526, 575, 0, 13, STR_704C_KEY, STR_704D_SHOW_KEY_TO_GRAPHS}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 575, 14, 141, 0x0, STR_NULL}, darkvater@176: { WIDGETS_END}, truelight@0: }; truelight@0: truelight@0: static const WindowDesc _delivered_cargo_graph_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 576, 142, 576, 142, rubidium@6144: WC_DELIVERED_CARGO, WC_NONE, truelight@0: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, truelight@0: _delivered_cargo_graph_widgets, truelight@0: DeliveredCargoGraphWndProc truelight@0: }; truelight@0: rubidium@6573: void ShowDeliveredCargoGraph() truelight@0: { truelight@0: if (AllocateWindowDescFront(&_delivered_cargo_graph_desc, 0)) { truelight@0: InvalidateWindow(WC_GRAPH_LEGEND, 0); truelight@0: } dominik@116: } dominik@116: darkvater@1086: /***********************/ darkvater@1086: /* PERFORMANCE HISTORY */ darkvater@1086: /***********************/ darkvater@1086: darkvater@1086: static void PerformanceHistoryWndProc(Window *w, WindowEvent *e) darkvater@1086: { tron@2952: switch (e->event) { peter1138@6529: case WE_PAINT: { peter1138@6529: GraphDrawer gd; peter1138@6529: const Player* p; darkvater@1086: peter1138@6529: DrawWindowWidgets(w); darkvater@1086: peter1138@6529: gd.left = 2; peter1138@6529: gd.top = 18; peter1138@6529: gd.height = 200; peter1138@6529: gd.has_negative_values = false; peter1138@6529: gd.format_str_y_axis = STR_7024; peter1138@6529: SetupGraphDrawerForPlayers(&gd); peter1138@6529: peter1138@6529: int numd = 0; peter1138@6529: FOR_ALL_PLAYERS(p) { peter1138@6529: if (p->is_active) { peter1138@6529: gd.colors[numd] = _colour_gradient[p->player_color][6]; peter1138@6529: for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { rubidium@8259: gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : (OverflowSafeInt64)p->old_economy[j].performance_history; peter1138@6529: i++; peter1138@6529: } darkvater@1086: } peter1138@6529: numd++; darkvater@1086: } peter1138@6529: peter1138@6529: gd.num_dataset = numd; peter1138@6529: peter1138@6529: DrawGraph(&gd); peter1138@6529: break; darkvater@1086: } darkvater@1086: peter1138@6529: case WE_CLICK: peter1138@6529: if (e->we.click.widget == 2) ShowGraphLegend(); peter1138@6529: if (e->we.click.widget == 3) ShowPerformanceRatingDetail(); peter1138@6529: break; darkvater@1086: } darkvater@1086: } darkvater@1086: darkvater@1086: static const Widget _performance_history_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_NONE, 14, 11, 475, 0, 13, STR_7051_COMPANY_PERFORMANCE_RATINGS, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@4344: { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 526, 575, 0, 13, STR_704C_KEY, STR_704D_SHOW_KEY_TO_GRAPHS}, rubidium@4344: { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 476, 525, 0, 13, STR_PERFORMANCE_DETAIL_KEY, STR_704D_SHOW_KEY_TO_GRAPHS}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 575, 14, 237, 0x0, STR_NULL}, darkvater@1086: { WIDGETS_END}, darkvater@1086: }; darkvater@1086: darkvater@1086: static const WindowDesc _performance_history_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 576, 238, 576, 238, rubidium@6144: WC_PERFORMANCE_HISTORY, WC_NONE, darkvater@1086: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, darkvater@1086: _performance_history_widgets, darkvater@1086: PerformanceHistoryWndProc darkvater@1086: }; darkvater@1086: rubidium@6573: void ShowPerformanceHistoryGraph() darkvater@1086: { darkvater@1086: if (AllocateWindowDescFront(&_performance_history_desc, 0)) { darkvater@1086: InvalidateWindow(WC_GRAPH_LEGEND, 0); darkvater@1086: } darkvater@1086: } darkvater@1086: darkvater@1086: /*****************/ darkvater@1086: /* COMPANY VALUE */ darkvater@1086: /*****************/ darkvater@1086: darkvater@1086: static void CompanyValueGraphWndProc(Window *w, WindowEvent *e) darkvater@1086: { tron@2952: switch (e->event) { peter1138@6529: case WE_PAINT: { peter1138@6529: GraphDrawer gd; peter1138@6529: const Player* p; darkvater@1086: peter1138@6529: DrawWindowWidgets(w); darkvater@1086: peter1138@6529: gd.left = 2; peter1138@6529: gd.top = 18; peter1138@6529: gd.height = 200; peter1138@6529: gd.has_negative_values = false; peter1138@6529: gd.format_str_y_axis = STR_CURRCOMPACT; peter1138@6529: SetupGraphDrawerForPlayers(&gd); peter1138@6529: peter1138@6529: int numd = 0; peter1138@6529: FOR_ALL_PLAYERS(p) { peter1138@6529: if (p->is_active) { peter1138@6529: gd.colors[numd] = _colour_gradient[p->player_color][6]; peter1138@6529: for (int j = gd.num_on_x_axis, i = 0; --j >= 0;) { peter1138@6529: gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_DATAPOINT : p->old_economy[j].company_value; peter1138@6529: i++; peter1138@6529: } darkvater@1086: } peter1138@6529: numd++; darkvater@1086: } peter1138@6529: peter1138@6529: gd.num_dataset = numd; peter1138@6529: peter1138@6529: DrawGraph(&gd); peter1138@6529: break; darkvater@1086: } darkvater@1086: peter1138@6529: case WE_CLICK: peter1138@6529: if (e->we.click.widget == 2) ShowGraphLegend(); peter1138@6529: break; darkvater@1086: } darkvater@1086: } darkvater@1086: darkvater@1086: static const Widget _company_value_graph_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_NONE, 14, 11, 525, 0, 13, STR_7052_COMPANY_VALUES, STR_018C_WINDOW_TITLE_DRAG_THIS}, rubidium@4344: { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 526, 575, 0, 13, STR_704C_KEY, STR_704D_SHOW_KEY_TO_GRAPHS}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 575, 14, 237, 0x0, STR_NULL}, darkvater@1086: { WIDGETS_END}, darkvater@1086: }; darkvater@1086: darkvater@1086: static const WindowDesc _company_value_graph_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 576, 238, 576, 238, rubidium@6144: WC_COMPANY_VALUE, WC_NONE, darkvater@1086: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, darkvater@1086: _company_value_graph_widgets, darkvater@1086: CompanyValueGraphWndProc darkvater@1086: }; darkvater@1086: rubidium@6573: void ShowCompanyValueGraph() darkvater@1086: { darkvater@1086: if (AllocateWindowDescFront(&_company_value_graph_desc, 0)) { darkvater@1086: InvalidateWindow(WC_GRAPH_LEGEND, 0); darkvater@1086: } darkvater@1086: } darkvater@1086: darkvater@1086: /*****************/ darkvater@1086: /* PAYMENT RATES */ darkvater@1086: /*****************/ darkvater@1086: darkvater@1086: static void CargoPaymentRatesWndProc(Window *w, WindowEvent *e) darkvater@1086: { tron@2952: switch (e->event) { peter1138@6529: case WE_PAINT: { peter1138@6529: GraphDrawer gd; belugas@4851: peter1138@6529: DrawWindowWidgets(w); peter1138@6518: peter1138@6529: int x = 495; peter1138@6529: int y = 24; darkvater@1086: peter1138@6529: gd.excluded_data = _legend_excluded_cargo; peter1138@6529: gd.left = 2; peter1138@6529: gd.top = 24; peter1138@6529: gd.height = w->height - 38; peter1138@6529: gd.has_negative_values = false; peter1138@6529: gd.format_str_y_axis = STR_CURRCOMPACT; peter1138@6529: gd.num_on_x_axis = 20; peter1138@6529: gd.num_vert_lines = 20; peter1138@6529: gd.month = 0xFF; peter1138@6529: gd.x_values_start = 10; peter1138@6529: gd.x_values_increment = 10; darkvater@1086: peter1138@6529: uint i = 0; peter1138@6676: for (CargoID c = 0; c < NUM_CARGO; c++) { peter1138@6529: const CargoSpec *cs = GetCargo(c); peter1138@6529: if (!cs->IsValid()) continue; peter1138@6529: peter1138@6529: /* Only draw labels for widgets that exist. If the widget doesn't peter1138@6529: * exist then the local player has used the climate cheat or peter1138@6529: * changed the NewGRF configuration with this window open. */ peter1138@6529: if (i + 3 < w->widget_count) { peter1138@6529: /* Since the buttons have no text, no images, peter1138@6529: * both the text and the colored box have to be manually painted. peter1138@6529: * clk_dif will move one pixel down and one pixel to the right peter1138@6529: * when the button is clicked */ peter1138@6529: byte clk_dif = IsWindowWidgetLowered(w, i + 3) ? 1 : 0; peter1138@6529: peter1138@6529: GfxFillRect(x + clk_dif, y + clk_dif, x + 8 + clk_dif, y + 5 + clk_dif, 0); peter1138@6529: GfxFillRect(x + 1 + clk_dif, y + 1 + clk_dif, x + 7 + clk_dif, y + 4 + clk_dif, cs->legend_colour); peter1138@6529: SetDParam(0, cs->name); belugas@8320: DrawString(x + 14 + clk_dif, y + clk_dif, STR_7065, TC_FROMSTRING); peter1138@6529: y += 8; peter1138@6529: } peter1138@6529: peter1138@6529: gd.colors[i] = cs->legend_colour; peter1138@6529: for (uint j = 0; j != 20; j++) { peter1138@6529: gd.cost[i][j] = GetTransportedGoodsIncome(10, 20, j * 6 + 6, c); peter1138@6529: } peter1138@6529: peter1138@6529: i++; peter1138@6529: } peter1138@6529: gd.num_dataset = i; peter1138@6529: peter1138@6529: DrawGraph(&gd); peter1138@6529: belugas@8320: DrawString(2 + 46, 24 + gd.height + 7, STR_7062_DAYS_IN_TRANSIT, TC_FROMSTRING); belugas@8320: DrawString(2 + 84, 24 - 9, STR_7063_PAYMENT_FOR_DELIVERING, TC_FROMSTRING); peter1138@6529: break; darkvater@1086: } peter1138@6529: peter1138@6529: case WE_CLICK: peter1138@6529: if (e->we.click.widget >= 3) { skidd13@8428: ToggleBit(_legend_excluded_cargo, e->we.click.widget - 3); peter1138@6529: ToggleWidgetLoweredState(w, e->we.click.widget); peter1138@6529: SetWindowDirty(w); peter1138@6529: } peter1138@6529: break; darkvater@1086: } darkvater@1086: } darkvater@1086: darkvater@1086: static const Widget _cargo_payment_rates_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_NONE, 14, 11, 567, 0, 13, STR_7061_CARGO_PAYMENT_RATES, STR_018C_WINDOW_TITLE_DRAG_THIS}, peter1138@6518: { WWT_PANEL, RESIZE_BOTTOM, 14, 0, 567, 14, 45, 0x0, STR_NULL}, darkvater@1086: { WIDGETS_END}, darkvater@1086: }; darkvater@1086: darkvater@1086: static const WindowDesc _cargo_payment_rates_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 568, 46, 568, 46, rubidium@6144: WC_PAYMENT_RATES, WC_NONE, darkvater@1086: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, darkvater@1086: _cargo_payment_rates_widgets, darkvater@1086: CargoPaymentRatesWndProc darkvater@1086: }; darkvater@1086: darkvater@1086: rubidium@6573: void ShowCargoPaymentRates() darkvater@1086: { peter1138@6518: Window *w = AllocateWindowDescFront(&_cargo_payment_rates_desc, 0); peter1138@6518: if (w == NULL) return; peter1138@6518: peter1138@6518: /* Count the number of active cargo types */ peter1138@6518: uint num_active = 0; peter1138@6676: for (CargoID c = 0; c < NUM_CARGO; c++) { peter1138@6518: if (GetCargo(c)->IsValid()) num_active++; peter1138@6518: } peter1138@6518: peter1138@6518: /* Resize the window to fit the cargo types */ peter1138@6698: ResizeWindow(w, 0, max(num_active, 12U) * 8); peter1138@6518: peter1138@6518: /* Add widgets for each cargo type */ peter1138@6596: w->widget_count += num_active; peter1138@6596: w->widget = ReallocT(w->widget, w->widget_count + 1); peter1138@6596: w->widget[w->widget_count].type = WWT_LAST; peter1138@6518: peter1138@6518: /* Set the properties of each widget */ peter1138@6518: for (uint i = 0; i != num_active; i++) { peter1138@6518: Widget *wi = &w->widget[3 + i]; peter1138@6518: wi->type = WWT_PANEL; peter1138@6518: wi->display_flags = RESIZE_NONE; peter1138@6518: wi->color = 12; peter1138@6518: wi->left = 493; peter1138@6518: wi->right = 562; peter1138@6518: wi->top = 24 + i * 8; peter1138@6518: wi->bottom = wi->top + 7; peter1138@6518: wi->data = 0; peter1138@6518: wi->tooltips = STR_7064_TOGGLE_GRAPH_FOR_CARGO; peter1138@6518: skidd13@8424: if (!HasBit(_legend_excluded_cargo, i)) LowerWindowWidget(w, i + 3); peter1138@6518: } peter1138@6518: peter1138@6518: SetWindowDirty(w); darkvater@1086: } darkvater@1086: darkvater@1086: /************************/ darkvater@1086: /* COMPANY LEAGUE TABLE */ darkvater@1086: /************************/ darkvater@1086: darkvater@1086: static const StringID _performance_titles[] = { darkvater@1086: STR_7066_ENGINEER, darkvater@1086: STR_7066_ENGINEER, darkvater@1086: STR_7067_TRAFFIC_MANAGER, darkvater@1086: STR_7067_TRAFFIC_MANAGER, darkvater@1086: STR_7068_TRANSPORT_COORDINATOR, darkvater@1086: STR_7068_TRANSPORT_COORDINATOR, darkvater@1086: STR_7069_ROUTE_SUPERVISOR, darkvater@1086: STR_7069_ROUTE_SUPERVISOR, darkvater@1086: STR_706A_DIRECTOR, darkvater@1086: STR_706A_DIRECTOR, darkvater@1086: STR_706B_CHIEF_EXECUTIVE, darkvater@1086: STR_706B_CHIEF_EXECUTIVE, darkvater@1086: STR_706C_CHAIRMAN, darkvater@1086: STR_706C_CHAIRMAN, darkvater@1086: STR_706D_PRESIDENT, darkvater@1086: STR_706E_TYCOON, darkvater@1086: }; darkvater@1086: darkvater@1086: static inline StringID GetPerformanceTitleFromValue(uint value) darkvater@1086: { darkvater@1086: return _performance_titles[minu(value, 1000) >> 6]; darkvater@1086: } darkvater@1086: tron@2656: static int CDECL PerfHistComp(const void* elem1, const void* elem2) tron@2656: { tron@2656: const Player* p1 = *(const Player* const*)elem1; tron@2656: const Player* p2 = *(const Player* const*)elem2; tron@2656: tron@2656: return p2->old_economy[1].performance_history - p1->old_economy[1].performance_history; darkvater@1086: } darkvater@1086: darkvater@1086: static void CompanyLeagueWndProc(Window *w, WindowEvent *e) darkvater@1086: { Darkvater@2482: switch (e->event) { tron@2656: case WE_PAINT: { tron@2656: const Player* plist[MAX_PLAYERS]; tron@2656: const Player* p; darkvater@1086: tron@2656: DrawWindowWidgets(w); darkvater@1086: peter1138@6529: uint pl_num = 0; tron@2656: FOR_ALL_PLAYERS(p) if (p->is_active) plist[pl_num++] = p; darkvater@1086: Darkvater@2673: qsort((void*)plist, pl_num, sizeof(*plist), PerfHistComp); darkvater@1086: peter1138@6529: for (uint i = 0; i != pl_num; i++) { tron@2656: p = plist[i]; tron@2656: SetDParam(0, i + STR_01AC_1ST); peter1138@7554: SetDParam(1, p->index); peter1138@7554: SetDParam(2, p->index); peter1138@7554: SetDParam(3, GetPerformanceTitleFromValue(p->old_economy[1].performance_history)); tron@2656: belugas@8320: DrawString(2, 15 + i * 10, i == 0 ? STR_7054 : STR_7055, TC_FROMSTRING); tron@2656: DrawPlayerIcon(p->index, 27, 16 + i * 10); tron@2656: } tron@2656: tron@2656: break; tron@2656: } darkvater@1086: } darkvater@1086: } darkvater@1086: darkvater@1086: darkvater@1086: static const Widget _company_league_widgets[] = { peter1138@2869: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, peter1138@2869: { WWT_CAPTION, RESIZE_NONE, 14, 11, 387, 0, 13, STR_7053_COMPANY_LEAGUE_TABLE, STR_018C_WINDOW_TITLE_DRAG_THIS}, peter1138@2869: { WWT_STICKYBOX, RESIZE_NONE, 14, 388, 399, 0, 13, STR_NULL, STR_STICKY_BUTTON}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 399, 14, 96, 0x0, STR_NULL}, darkvater@1086: { WIDGETS_END}, darkvater@1086: }; darkvater@1086: darkvater@1086: static const WindowDesc _company_league_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 400, 97, 400, 97, rubidium@6144: WC_COMPANY_LEAGUE, WC_NONE, peter1138@2869: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, darkvater@1086: _company_league_widgets, darkvater@1086: CompanyLeagueWndProc darkvater@1086: }; darkvater@1086: rubidium@6573: void ShowCompanyLeagueTable() darkvater@1086: { rubidium@6987: AllocateWindowDescFront(&_company_league_desc, 0); darkvater@1086: } darkvater@1086: dominik@116: /*****************************/ dominik@116: /* PERFORMANCE RATING DETAIL */ dominik@116: /*****************************/ dominik@116: dominik@116: static void PerformanceRatingDetailWndProc(Window *w, WindowEvent *e) dominik@116: { maedhros@6197: static PlayerID _performance_rating_detail_player = INVALID_PLAYER; belugas@4719: tron@2952: switch (e->event) { peter1138@3027: case WE_PAINT: { belugas@4719: byte x; tron@3033: uint16 y = 14; peter1138@3027: int total_score = 0; peter1138@3027: int color_done, color_notdone; truelight@193: belugas@6505: /* Draw standard stuff */ peter1138@3027: DrawWindowWidgets(w); truelight@193: maedhros@6197: /* Check if the currently selected player is still active. */ maedhros@6197: if (_performance_rating_detail_player == INVALID_PLAYER || !GetPlayer(_performance_rating_detail_player)->is_active) { maedhros@6197: if (_performance_rating_detail_player != INVALID_PLAYER) { maedhros@6197: /* Raise and disable the widget for the previous selection. */ maedhros@6197: RaiseWindowWidget(w, _performance_rating_detail_player + 13); maedhros@6197: DisableWindowWidget(w, _performance_rating_detail_player + 13); maedhros@6197: SetWindowDirty(w); maedhros@6197: maedhros@6197: _performance_rating_detail_player = INVALID_PLAYER; maedhros@6197: } maedhros@6197: maedhros@6197: for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { maedhros@6197: if (GetPlayer(i)->is_active) { maedhros@6197: /* Lower the widget corresponding to this player. */ maedhros@6197: LowerWindowWidget(w, i + 13); maedhros@6197: SetWindowDirty(w); maedhros@6197: maedhros@6197: _performance_rating_detail_player = i; maedhros@6197: break; maedhros@6197: } maedhros@6197: } maedhros@6197: } maedhros@6197: maedhros@6197: /* If there are no active players, don't display anything else. */ maedhros@6197: if (_performance_rating_detail_player == INVALID_PLAYER) break; maedhros@6197: belugas@6505: /* Paint the player icons */ rubidium@5838: for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { peter1138@3027: if (!GetPlayer(i)->is_active) { belugas@6505: /* Check if we have the player as an active player */ belugas@4709: if (!IsWindowWidgetDisabled(w, i + 13)) { belugas@6505: /* Bah, player gone :( */ belugas@4709: DisableWindowWidget(w, i + 13); maedhros@6197: belugas@6505: /* We need a repaint */ peter1138@3027: SetWindowDirty(w); peter1138@3027: } maedhros@6197: continue; peter1138@3027: } peter1138@3027: belugas@6505: /* Check if we have the player marked as inactive */ belugas@4709: if (IsWindowWidgetDisabled(w, i + 13)) { belugas@6505: /* New player! Yippie :p */ belugas@4709: EnableWindowWidget(w, i + 13); belugas@6505: /* We need a repaint */ peter1138@3027: SetWindowDirty(w); peter1138@3027: } peter1138@3027: belugas@4719: x = (i == _performance_rating_detail_player) ? 1 : 0; peter1138@3027: DrawPlayerIcon(i, i * 37 + 13 + x, 16 + x); peter1138@3027: } peter1138@3027: belugas@6505: /* The colors used to show how the progress is going */ tron@4444: color_done = _colour_gradient[COLOUR_GREEN][4]; tron@4444: color_notdone = _colour_gradient[COLOUR_RED][4]; peter1138@3027: belugas@6505: /* Draw all the score parts */ rubidium@5838: for (ScoreID i = SCORE_BEGIN; i < SCORE_END; i++) { belugas@4719: int val = _score_part[_performance_rating_detail_player][i]; tron@3033: int needed = _score_info[i].needed; tron@3033: int score = _score_info[i].score; tron@3033: peter1138@3027: y += 20; belugas@6505: /* SCORE_TOTAL has his own rulez ;) */ peter1138@3027: if (i == SCORE_TOTAL) { peter1138@3027: needed = total_score; peter1138@3027: score = SCORE_MAX; peter1138@3027: } else { peter1138@3027: total_score += score; peter1138@3027: } peter1138@3027: belugas@8320: DrawString(7, y, STR_PERFORMANCE_DETAIL_VEHICLES + i, TC_FROMSTRING); peter1138@3027: belugas@6505: /* Draw the score */ peter1138@3027: SetDParam(0, score); belugas@8320: DrawStringRightAligned(107, y, SET_PERFORMANCE_DETAIL_INT, TC_FROMSTRING); peter1138@3027: belugas@6505: /* Calculate the %-bar */ skidd13@8418: x = Clamp(val, 0, needed) * 50 / needed; peter1138@3027: belugas@6505: /* SCORE_LOAN is inversed */ tron@3033: if (val < 0 && i == SCORE_LOAN) x = 0; peter1138@3027: belugas@6505: /* Draw the bar */ tron@3033: if (x != 0) GfxFillRect(112, y - 2, 112 + x, y + 10, color_done); tron@3033: if (x != 50) GfxFillRect(112 + x, y - 2, 112 + 50, y + 10, color_notdone); peter1138@3027: belugas@6505: /* Calculate the % */ skidd13@8418: x = Clamp(val, 0, needed) * 100 / needed; peter1138@3027: belugas@6505: /* SCORE_LOAN is inversed */ tron@3033: if (val < 0 && i == SCORE_LOAN) x = 0; peter1138@3027: belugas@6505: /* Draw it */ peter1138@3027: SetDParam(0, x); belugas@8320: DrawStringCentered(137, y, STR_PERFORMANCE_DETAIL_PERCENT, TC_FROMSTRING); peter1138@3027: belugas@6505: /* SCORE_LOAN is inversed */ tron@3033: if (i == SCORE_LOAN) val = needed - val; peter1138@3027: belugas@6505: /* Draw the amount we have against what is needed belugas@6505: * For some of them it is in currency format */ peter1138@3027: SetDParam(0, val); peter1138@3027: SetDParam(1, needed); peter1138@3027: switch (i) { peter1138@3027: case SCORE_MIN_PROFIT: peter1138@3027: case SCORE_MIN_INCOME: peter1138@3027: case SCORE_MAX_INCOME: peter1138@3027: case SCORE_MONEY: peter1138@3027: case SCORE_LOAN: belugas@8320: DrawString(167, y, STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY, TC_FROMSTRING); peter1138@3027: break; peter1138@3027: default: belugas@8320: DrawString(167, y, STR_PERFORMANCE_DETAIL_AMOUNT_INT, TC_FROMSTRING); peter1138@3027: } peter1138@3027: } peter1138@3027: peter1138@3027: break; dominik@116: } truelight@193: peter1138@3027: case WE_CLICK: belugas@6505: /* Check which button is clicked */ skidd13@8450: if (IsInsideMM(e->we.click.widget, 13, 21)) { belugas@6505: /* Is it no on disable? */ belugas@4709: if (!IsWindowWidgetDisabled(w, e->we.click.widget)) { belugas@4719: RaiseWindowWidget(w, _performance_rating_detail_player + 13); rubidium@5838: _performance_rating_detail_player = (PlayerID)(e->we.click.widget - 13); belugas@4719: LowerWindowWidget(w, _performance_rating_detail_player + 13); peter1138@3027: SetWindowDirty(w); peter1138@3027: } peter1138@3027: } peter1138@3027: break; truelight@193: peter1138@3027: case WE_CREATE: { peter1138@3027: Player *p2; belugas@4702: belugas@4709: /* Disable the players who are not active */ peter1138@6529: for (PlayerID i = PLAYER_FIRST; i < MAX_PLAYERS; i++) { belugas@4709: SetWindowWidgetDisabledState(w, i + 13, !GetPlayer(i)->is_active); dominik@116: } belugas@4719: /* Update all player stats with the current data belugas@4719: * (this is because _score_info is not saved to a savegame) */ tron@3033: FOR_ALL_PLAYERS(p2) { tron@3033: if (p2->is_active) UpdateCompanyRatingAndValue(p2, false); tron@3033: } dominik@116: peter1138@3027: w->custom[0] = DAY_TICKS; peter1138@3027: w->custom[1] = 5; dominik@116: maedhros@6197: if (_performance_rating_detail_player != INVALID_PLAYER) LowerWindowWidget(w, _performance_rating_detail_player + 13); dominik@116: SetWindowDirty(w); peter1138@3027: peter1138@3027: break; peter1138@3027: } peter1138@3027: peter1138@6529: case WE_TICK: belugas@6505: /* Update the player score every 5 days */ peter1138@3027: if (--w->custom[0] == 0) { peter1138@3027: w->custom[0] = DAY_TICKS; peter1138@3027: if (--w->custom[1] == 0) { peter1138@3027: Player *p2; tron@3033: peter1138@3027: w->custom[1] = 5; tron@3033: FOR_ALL_PLAYERS(p2) { belugas@6505: /* Skip if player is not active */ tron@3033: if (p2->is_active) UpdateCompanyRatingAndValue(p2, false); tron@3033: } peter1138@3027: SetWindowDirty(w); peter1138@3027: } peter1138@3027: } peter1138@3027: peter1138@3027: break; dominik@116: } dominik@116: } dominik@116: dominik@116: static const Widget _performance_rating_detail_widgets[] = { rubidium@4344: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, rubidium@4344: { WWT_CAPTION, RESIZE_NONE, 14, 11, 298, 0, 13, STR_PERFORMANCE_DETAIL, STR_018C_WINDOW_TITLE_DRAG_THIS}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 298, 14, 27, 0x0, STR_NULL}, dominik@116: Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 298, 28, 47, 0x0, STR_PERFORMANCE_DETAIL_VEHICLES_TIP}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 298, 48, 67, 0x0, STR_PERFORMANCE_DETAIL_STATIONS_TIP}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 298, 68, 87, 0x0, STR_PERFORMANCE_DETAIL_MIN_PROFIT_TIP}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 298, 88, 107, 0x0, STR_PERFORMANCE_DETAIL_MIN_INCOME_TIP}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 298, 108, 127, 0x0, STR_PERFORMANCE_DETAIL_MAX_INCOME_TIP}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 298, 128, 147, 0x0, STR_PERFORMANCE_DETAIL_DELIVERED_TIP}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 298, 148, 167, 0x0, STR_PERFORMANCE_DETAIL_CARGO_TIP}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 298, 168, 187, 0x0, STR_PERFORMANCE_DETAIL_MONEY_TIP}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 298, 188, 207, 0x0, STR_PERFORMANCE_DETAIL_LOAN_TIP}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 0, 298, 208, 227, 0x0, STR_PERFORMANCE_DETAIL_TOTAL_TIP}, dominik@116: Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 2, 38, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 39, 75, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 76, 112, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 113, 149, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 150, 186, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 187, 223, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 224, 260, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, Darkvater@4938: { WWT_PANEL, RESIZE_NONE, 14, 261, 297, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, darkvater@176: { WIDGETS_END}, dominik@116: }; dominik@116: dominik@116: static const WindowDesc _performance_rating_detail_desc = { rubidium@7837: WDP_AUTO, WDP_AUTO, 299, 228, 299, 228, rubidium@6144: WC_PERFORMANCE_DETAIL, WC_NONE, dominik@116: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, dominik@116: _performance_rating_detail_widgets, dominik@116: PerformanceRatingDetailWndProc dominik@116: }; dominik@116: rubidium@6573: void ShowPerformanceRatingDetail() dominik@116: { dominik@116: AllocateWindowDescFront(&_performance_rating_detail_desc, 0); dominik@116: }