# train_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: train_gui.cpp 18344 2009-11-29 19:31:23Z rubidium $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "window_gui.h"
    00014 #include "gfx_func.h"
    00015 #include "command_func.h"
    00016 #include "vehicle_gui.h"
    00017 #include "train.h"
    00018 #include "strings_func.h"
    00019 #include "vehicle_func.h"
    00020 #include "engine_base.h"
    00021 #include "window_func.h"
    00022
    00023 #include "table/sprites.h"
    00024 #include "table/strings.h"
    00025
    00026 void CcBuildWagon(bool success, TileIndex tile, uint32 p1, uint32 p2)
    00027 {
    00028   if (!success) return;
    00029
    00030   /* find a locomotive in the depot. */
    00031   const Vehicle *found = NULL;
    00032   const Train *t;
    00033   FOR_ALL_TRAINS(t) {
    00034     if (t->IsFrontEngine() && t->tile == tile &&
    00035         t->track == TRACK_BIT_DEPOT) {
    00036       if (found != NULL) return; // must be exactly one.
    00037       found = t;
    00038     }
    00039   }
    00040
    00041   /* if we found a loco, */
    00042   if (found != NULL) {
    00043     found = found->Last();
    00044     /* put the new wagon at the end of the loco. */
    00045     DoCommandP(0, _new_vehicle_id | (found->index << 16), 0, CMD_MOVE_RAIL_VEHICLE);
    00046     InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
    00047   }
    00048 }
    00049
    00050 void CcBuildLoco(bool success, TileIndex tile, uint32 p1, uint32 p2)
    00051 {
    00052   if (!success) return;
    00053
    00054   const Vehicle *v = Vehicle::Get(_new_vehicle_id);
    00055   if (tile == _backup_orders_tile) {
    00056     _backup_orders_tile = 0;
    00057     RestoreVehicleOrders(v);
    00058   }
    00059   ShowVehicleViewWindow(v);
    00060 }
    00061
    00071 void DrawTrainImage(const Train *v, int left, int right, int y, VehicleID selection, int skip)
    00072 {
    00073   bool rtl = _dynlang.text_dir == TD_RTL;
    00074   Direction dir = rtl ? DIR_E : DIR_W;
    00075
    00076   DrawPixelInfo tmp_dpi, *old_dpi;
    00077   /* Position of highlight box */
    00078   int highlight_l = 0;
    00079   int highlight_r = 0;
    00080   int max_width = right - left + 1;
    00081
    00082   if (!FillDrawPixelInfo(&tmp_dpi, left, y, max_width, 14)) return;
    00083
    00084   old_dpi = _cur_dpi;
    00085   _cur_dpi = &tmp_dpi;
    00086
    00087   int px = rtl ? max_width + skip : -skip;
    00088   bool sel_articulated = false;
    00089   for (; v != NULL && (rtl ? px > 0 : px < max_width); v = v->Next()) {
    00090     Point offset;
    00091     int width = Train::From(v)->GetDisplayImageWidth(&offset);
    00092
    00093     if (rtl ? px + width > 0 : px - width < max_width) {
    00094       SpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
    00095       DrawSprite(v->GetImage(dir), pal, px + (rtl ? -offset.x : offset.x), 7 + offset.y);
    00096     }
    00097
    00098     if (!v->IsArticulatedPart()) sel_articulated = false;
    00099
    00100     if (v->index == selection) {
    00101       /* Set the highlight position */
    00102       highlight_l = rtl ? px - width : px;
    00103       highlight_r = rtl ? px - 1 : px + width - 1;
    00104       sel_articulated = true;
    00105     } else if ((_cursor.vehchain && highlight_r != 0) || sel_articulated) {
    00106       if (rtl) {
    00107         highlight_l -= width;
    00108       } else {
    00109         highlight_r += width;
    00110       }
    00111     }
    00112
    00113     px += rtl ? -width : width;
    00114   }
    00115
    00116   if (highlight_l != highlight_r) {
    00117     /* Draw the highlight. Now done after drawing all the engines, as
    00118      * the next engine after the highlight could overlap it. */
    00119     DrawFrameRect(highlight_l, 0, highlight_r, 13, COLOUR_WHITE, FR_BORDERONLY);
    00120   }
    00121
    00122   _cur_dpi = old_dpi;
    00123 }
    00124
    00126 struct CargoSummaryItem {
    00127   CargoID cargo;
    00128   StringID subtype;
    00129   uint capacity;
    00130   uint amount;
    00131   StationID source;
    00132
    00134   FORCEINLINE bool operator != (const CargoSummaryItem &other) const
    00135   {
    00136     return this->cargo != other.cargo || this->subtype != other.subtype;
    00137   }
    00138 };
    00139
    00140 enum {
    00141   TRAIN_DETAILS_MIN_INDENT = 32,
    00142   TRAIN_DETAILS_MAX_INDENT = 72,
    00143 };
    00144
    00146 typedef SmallVector<CargoSummaryItem, 2> CargoSummary;
    00148 static CargoSummary _cargo_summary;
    00149
    00158 static void TrainDetailsCargoTab(const CargoSummaryItem *item, int left, int right, int y)
    00159 {
    00160   StringID str = STR_VEHICLE_DETAILS_CARGO_EMPTY;
    00161
    00162   if (item->amount > 0) {
    00163     SetDParam(0, item->cargo);
    00164     SetDParam(1, item->amount);
    00165     SetDParam(2, item->source);
    00166     SetDParam(3, _settings_game.vehicle.freight_trains);
    00167     str = FreightWagonMult(item->cargo) > 1 ? STR_VEHICLE_DETAILS_CARGO_FROM_MULT : STR_VEHICLE_DETAILS_CARGO_FROM;
    00168   }
    00169
    00170   DrawString(left, right, y, str);
    00171 }
    00172
    00181 static void TrainDetailsInfoTab(const Vehicle *v, int left, int right, int y)
    00182 {
    00183   if (RailVehInfo(v->engine_type)->railveh_type == RAILVEH_WAGON) {
    00184     SetDParam(0, v->engine_type);
    00185     SetDParam(1, v->value);
    00186     DrawString(left, right, y, STR_VEHICLE_DETAILS_TRAIN_WAGON_VALUE, TC_FROMSTRING, SA_LEFT | SA_STRIP);
    00187   } else {
    00188     SetDParam(0, v->engine_type);
    00189     SetDParam(1, v->build_year);
    00190     SetDParam(2, v->value);
    00191     DrawString(left, right, y, STR_VEHICLE_DETAILS_TRAIN_ENGINE_BUILT_AND_VALUE, TC_FROMSTRING, SA_LEFT | SA_STRIP);
    00192   }
    00193 }
    00194
    00203 static void TrainDetailsCapacityTab(const CargoSummaryItem *item, int left, int right, int y)
    00204 {
    00205   SetDParam(0, item->cargo);
    00206   SetDParam(1, item->capacity);
    00207   SetDParam(4, item->subtype);
    00208   SetDParam(5, _settings_game.vehicle.freight_trains);
    00209   DrawString(left, right, y, FreightWagonMult(item->cargo) > 1 ? STR_VEHICLE_INFO_CAPACITY_MULT : STR_VEHICLE_INFO_CAPACITY);
    00210 }
    00211
    00217 static void GetCargoSummaryOfArticulatedVehicle(const Train *v, CargoSummary *summary)
    00218 {
    00219   summary->Clear();
    00220   do {
    00221     if (v->cargo_cap == 0) continue;
    00222
    00223     CargoSummaryItem new_item;
    00224     new_item.cargo = v->cargo_type;
    00225     new_item.subtype = GetCargoSubtypeText(v);
    00226
    00227     CargoSummaryItem *item = summary->Find(new_item);
    00228     if (item == summary->End()) {
    00229       item = summary->Append();
    00230       item->cargo = new_item.cargo;
    00231       item->subtype = new_item.subtype;
    00232       item->capacity = 0;
    00233       item->amount = 0;
    00234       item->source = INVALID_STATION;
    00235     }
    00236
    00237     item->capacity += v->cargo_cap;
    00238     item->amount += v->cargo.Count();
    00239     if (item->source == INVALID_STATION) item->source = v->cargo.Source();
    00240   } while ((v = v->Next()) != NULL && v->IsArticulatedPart());
    00241 }
    00242
    00248 static uint GetLengthOfArticulatedVehicle(const Train *v)
    00249 {
    00250   uint length = 0;
    00251
    00252   do {
    00253     length += v->GetDisplayImageWidth();
    00254   } while ((v = v->Next()) != NULL && v->IsArticulatedPart());
    00255
    00256   return length;
    00257 }
    00258
    00265 int GetTrainDetailsWndVScroll(VehicleID veh_id, TrainDetailsWindowTabs det_tab)
    00266 {
    00267   int num = 0;
    00268
    00269   if (det_tab == TDW_TAB_TOTALS) { // Total cargo tab
    00270     CargoArray act_cargo;
    00271     CargoArray max_cargo;
    00272     for (const Vehicle *v = Vehicle::Get(veh_id); v != NULL; v = v->Next()) {
    00273       act_cargo[v->cargo_type] += v->cargo.Count();
    00274       max_cargo[v->cargo_type] += v->cargo_cap;
    00275     }
    00276
    00277     /* Set scroll-amount seperately from counting, as to not compute num double
    00278      * for more carriages of the same type
    00279      */
    00280     for (CargoID i = 0; i < NUM_CARGO; i++) {
    00281       if (max_cargo[i] > 0) num++; // only count carriages that the train has
    00282     }
    00283     num++; // needs one more because first line is description string
    00284   } else {
    00285     for (const Train *v = Train::Get(veh_id); v != NULL; v = v->GetNextVehicle()) {
    00286       GetCargoSummaryOfArticulatedVehicle(v, &_cargo_summary);
    00287       num += max(1u, _cargo_summary.Length());
    00288
    00289       uint length = GetLengthOfArticulatedVehicle(v);
    00290       if (length > TRAIN_DETAILS_MAX_INDENT) num++;
    00291     }
    00292   }
    00293
    00294   return num;
    00295 }
    00296
    00308 void DrawTrainDetails(const Train *v, int left, int right, int y, int vscroll_pos, uint16 vscroll_cap, TrainDetailsWindowTabs det_tab)
    00309 {
    00310   /* draw the first 3 details tabs */
    00311   if (det_tab != TDW_TAB_TOTALS) {
    00312     bool rtl = _dynlang.text_dir == TD_RTL;
    00313     Direction dir = rtl ? DIR_E : DIR_W;
    00314     int x = rtl ? right : left;
    00315     int sprite_y_offset = 4 + (FONT_HEIGHT_NORMAL - 10) / 2;
    00316     int line_height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
    00317     for (; v != NULL && vscroll_pos > -vscroll_cap; v = v->GetNextVehicle()) {
    00318       GetCargoSummaryOfArticulatedVehicle(v, &_cargo_summary);
    00319
    00320       /* Draw sprites */
    00321       int dx = 0;
    00322       int px = x;
    00323       const Train *u = v;
    00324       do {
    00325         Point offset;
    00326         int width = u->GetDisplayImageWidth(&offset);
    00327         if (vscroll_pos <= 0 && vscroll_pos > -vscroll_cap) {
    00328           SpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
    00329           DrawSprite(u->GetImage(dir), pal, px + (rtl ? -offset.x : offset.x), y - line_height * vscroll_pos + sprite_y_offset + offset.y);
    00330         }
    00331         px += rtl ? -width : width;
    00332         dx += width;
    00333         u = u->Next();
    00334       } while (u != NULL && u->IsArticulatedPart());
    00335
    00336       bool separate_sprite_row = (dx > TRAIN_DETAILS_MAX_INDENT);
    00337       if (separate_sprite_row) {
    00338         vscroll_pos--;
    00339         dx = 0;
    00340       }
    00341
    00342       uint num_lines = max(1u, _cargo_summary.Length());
    00343       for (uint i = 0; i < num_lines; i++) {
    00344         int sprite_width = max<int>(dx, TRAIN_DETAILS_MIN_INDENT) + 3;
    00345         int data_left  = left + (rtl ? 0 : sprite_width);
    00346         int data_right = right - (rtl ? sprite_width : 0);
    00347         if (vscroll_pos <= 0 && vscroll_pos > -vscroll_cap) {
    00348           int py = y - line_height * vscroll_pos;
    00349           if (i > 0 || separate_sprite_row) {
    00350             if (vscroll_pos != 0) GfxFillRect(left, py - WD_MATRIX_TOP - 1, right, py - WD_MATRIX_TOP, _colour_gradient[COLOUR_GREY][5]);
    00351           }
    00352           switch (det_tab) {
    00353             case TDW_TAB_CARGO:
    00354               if (i < _cargo_summary.Length()) {
    00355                 TrainDetailsCargoTab(&_cargo_summary[i], data_left, data_right, py);
    00356               } else {
    00357                 DrawString(data_left, data_right, py, STR_QUANTITY_N_A, TC_LIGHT_BLUE);
    00358               }
    00359               break;
    00360
    00361             case TDW_TAB_INFO:
    00362               if (i == 0) TrainDetailsInfoTab(v, data_left, data_right, py);
    00363               break;
    00364
    00365             case TDW_TAB_CAPACITY:
    00366               if (i < _cargo_summary.Length()) {
    00367                 TrainDetailsCapacityTab(&_cargo_summary[i], data_left, data_right, py);
    00368               } else {
    00369                 DrawString(data_left, data_right, py, STR_VEHICLE_INFO_NO_CAPACITY);
    00370               }
    00371               break;
    00372
    00373             default: NOT_REACHED();
    00374           }
    00375         }
    00376         vscroll_pos--;
    00377       }
    00378     }
    00379   } else {
    00380     CargoArray act_cargo;
    00381     CargoArray max_cargo;
    00382     Money feeder_share = 0;
    00383
    00384     for (const Vehicle *u = v; u != NULL; u = u->Next()) {
    00385       act_cargo[u->cargo_type] += u->cargo.Count();
    00386       max_cargo[u->cargo_type] += u->cargo_cap;
    00387       feeder_share             += u->cargo.FeederShare();
    00388     }
    00389
    00390     /* draw total cargo tab */
    00391     DrawString(left, right, y, STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY_TEXT);
    00392     y += WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
    00393
    00394     for (CargoID i = 0; i < NUM_CARGO; i++) {
    00395       if (max_cargo[i] > 0 && --vscroll_pos < 0 && vscroll_pos > -vscroll_cap) {
    00396         SetDParam(0, i);            // {CARGO} #1
    00397         SetDParam(1, act_cargo[i]); // {CARGO} #2
    00398         SetDParam(2, i);            // {SHORTCARGO} #1
    00399         SetDParam(3, max_cargo[i]); // {SHORTCARGO} #2
    00400         SetDParam(4, _settings_game.vehicle.freight_trains);
    00401         DrawString(left, right, y, FreightWagonMult(i) > 1 ? STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY_MULT : STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY);
    00402         y += WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
    00403       }
    00404     }
    00405     SetDParam(0, feeder_share);
    00406     DrawString(left, right, y, STR_VEHICLE_INFO_FEEDER_CARGO_VALUE);
    00407   }
    00408 }