Files @ r14217:40beee5d2e82
Branch filter:

Location: cpp/openttd-patchpack/source/src/train_gui.cpp - annotation

michi_cc
(svn r18778) -Fix [FS#3483]: [YAPP] Remove a special check for two-sided signals when reserving a path as this causes trains to get stuck in front of them.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
r5584:545d748cc681
r5584:545d748cc681
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r9111:983de9c5a848
r6422:5983361e241a
r5584:545d748cc681
r8107:82461791b7a2
r8224:194097dc7288
r8116:df67d3c5e4fd
r5584:545d748cc681
r5584:545d748cc681
r8114:866ed489ed98
r8144:1432edd15267
r9070:e059c65164f3
r9297:6cd3e74fc38e
r5584:545d748cc681
r8264:d493cb51fe8a
r8264:d493cb51fe8a
r8264:d493cb51fe8a
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r6422:5983361e241a
r9344:f9f972b7b45d
r12030:bf346482c342
r12030:bf346482c342
r12290:442d5d533a57
r12030:bf346482c342
r5584:545d748cc681
r12030:bf346482c342
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r6422:5983361e241a
r5584:545d748cc681
r12450:fcc3ae606967
r6422:5983361e241a
r10499:570896340d7a
r9297:6cd3e74fc38e
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r11917:612c11f7ab47
r5584:545d748cc681
r5584:545d748cc681
r7653:8b8bc2df3a7d
r5584:545d748cc681
r7486:610eee847f11
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r12364:cbc66d4e5c75
r13599:f6ef46132fe6
r13599:f6ef46132fe6
r13599:f6ef46132fe6
r13599:f6ef46132fe6
r13060:e57594b0ca84
r13599:f6ef46132fe6
r12364:cbc66d4e5c75
r13599:f6ef46132fe6
r5584:545d748cc681
r13602:30f9b380fb13
r13602:30f9b380fb13
r13602:30f9b380fb13
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r13599:f6ef46132fe6
r5584:545d748cc681
r13599:f6ef46132fe6
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r13602:30f9b380fb13
r12378:e294ac78ad96
r13602:30f9b380fb13
r12423:199e8b46a11b
r12423:199e8b46a11b
r12377:643da76b9d87
r13602:30f9b380fb13
r12377:643da76b9d87
r13602:30f9b380fb13
r12377:643da76b9d87
r5584:545d748cc681
r12378:e294ac78ad96
r12378:e294ac78ad96
r12377:643da76b9d87
r12377:643da76b9d87
r13628:29fed4984dc7
r13628:29fed4984dc7
r12378:e294ac78ad96
r12378:e294ac78ad96
r13602:30f9b380fb13
r13628:29fed4984dc7
r13602:30f9b380fb13
r13628:29fed4984dc7
r13602:30f9b380fb13
r5584:545d748cc681
r5584:545d748cc681
r13602:30f9b380fb13
r12377:643da76b9d87
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r9770:55a5aeabf960
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r13808:32b982f51e30
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r13808:32b982f51e30
r5584:545d748cc681
r13808:32b982f51e30
r5584:545d748cc681
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r5584:545d748cc681
r13808:32b982f51e30
r13808:32b982f51e30
r5584:545d748cc681
r5584:545d748cc681
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r5584:545d748cc681
r5868:bc3e720ec5f7
r7059:0c2dc932a6ae
r7002:201ff6832d3a
r13610:8af771d39472
r5584:545d748cc681
r7059:0c2dc932a6ae
r5584:545d748cc681
r7002:201ff6832d3a
r13610:8af771d39472
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r13808:32b982f51e30
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r5584:545d748cc681
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r5584:545d748cc681
r5584:545d748cc681
r12069:84bc50fb033a
r12069:84bc50fb033a
r12069:84bc50fb033a
r12069:84bc50fb033a
r12069:84bc50fb033a
r12069:84bc50fb033a
r12069:84bc50fb033a
r5584:545d748cc681
r7530:8eaeedf8b40c
r5584:545d748cc681
r12069:84bc50fb033a
r12253:9cc92893e2d0
r12253:9cc92893e2d0
r13806:2bc44cc74256
r7530:8eaeedf8b40c
r7530:8eaeedf8b40c
r7530:8eaeedf8b40c
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r6350:7a0fb15b3676
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r7530:8eaeedf8b40c
r5584:545d748cc681
r5584:545d748cc681
r7530:8eaeedf8b40c
r7530:8eaeedf8b40c
r5584:545d748cc681
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r11443:81f75d8ddd0c
r12069:84bc50fb033a
r12069:84bc50fb033a
r12069:84bc50fb033a
r11443:81f75d8ddd0c
r12292:c3b6baef441a
r7530:8eaeedf8b40c
r6422:5983361e241a
r12069:84bc50fb033a
r13608:06bb7056281f
r13608:06bb7056281f
r13608:06bb7056281f
r13721:3499c8d2d992
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r5584:545d748cc681
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r5584:545d748cc681
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r12069:84bc50fb033a
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r12069:84bc50fb033a
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r5584:545d748cc681
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r13808:32b982f51e30
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r12251:58d0d73149cb
r12251:58d0d73149cb
r12253:9cc92893e2d0
r7530:8eaeedf8b40c
r13806:2bc44cc74256
r7530:8eaeedf8b40c
r7530:8eaeedf8b40c
r9943:38700f276db2
r7530:8eaeedf8b40c
r7530:8eaeedf8b40c
r6422:5983361e241a
r13595:3017638cdea0
r13595:3017638cdea0
r13595:3017638cdea0
r6350:7a0fb15b3676
r7530:8eaeedf8b40c
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r9413:fcf267325763
r13595:3017638cdea0
r13595:3017638cdea0
r5584:545d748cc681
r5584:545d748cc681
r9943:38700f276db2
r13595:3017638cdea0
r5584:545d748cc681
r5584:545d748cc681
/* $Id$ */

/*
 * This file is part of OpenTTD.
 * 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.
 * 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.
 * 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/>.
 */

/** @file train_gui.cpp GUI for trains. */

#include "stdafx.h"
#include "window_gui.h"
#include "gfx_func.h"
#include "command_func.h"
#include "vehicle_gui.h"
#include "train.h"
#include "strings_func.h"
#include "vehicle_func.h"
#include "engine_base.h"
#include "window_func.h"

#include "table/sprites.h"
#include "table/strings.h"

void CcBuildWagon(bool success, TileIndex tile, uint32 p1, uint32 p2)
{
	if (!success) return;

	/* find a locomotive in the depot. */
	const Vehicle *found = NULL;
	const Train *t;
	FOR_ALL_TRAINS(t) {
		if (t->IsFrontEngine() && t->tile == tile &&
				t->track == TRACK_BIT_DEPOT) {
			if (found != NULL) return; // must be exactly one.
			found = t;
		}
	}

	/* if we found a loco, */
	if (found != NULL) {
		found = found->Last();
		/* put the new wagon at the end of the loco. */
		DoCommandP(0, _new_vehicle_id | (found->index << 16), 0, CMD_MOVE_RAIL_VEHICLE);
		InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
	}
}

void CcBuildLoco(bool success, TileIndex tile, uint32 p1, uint32 p2)
{
	if (!success) return;

	const Vehicle *v = Vehicle::Get(_new_vehicle_id);
	if (tile == _backup_orders_tile) {
		_backup_orders_tile = 0;
		RestoreVehicleOrders(v);
	}
	ShowVehicleViewWindow(v);
}

/**
 * Draws an image of a whole train
 * @param v         Front vehicle
 * @param left      The minimum horizontal position
 * @param right     The maximum horizontal position
 * @param y         Vertical position to draw at
 * @param selection Selected vehicle to draw a frame around
 * @param skip      Number of pixels to skip at the front (for scrolling)
 */
void DrawTrainImage(const Train *v, int left, int right, int y, VehicleID selection, int skip)
{
	bool rtl = _dynlang.text_dir == TD_RTL;
	Direction dir = rtl ? DIR_E : DIR_W;

	DrawPixelInfo tmp_dpi, *old_dpi;
	/* Position of highlight box */
	int highlight_l = 0;
	int highlight_r = 0;
	int max_width = right - left + 1;

	if (!FillDrawPixelInfo(&tmp_dpi, left, y, max_width, 14)) return;

	old_dpi = _cur_dpi;
	_cur_dpi = &tmp_dpi;

	int px = rtl ? max_width + skip : -skip;
	bool sel_articulated = false;
	for (; v != NULL && (rtl ? px > 0 : px < max_width); v = v->Next()) {
		Point offset;
		int width = Train::From(v)->GetDisplayImageWidth(&offset);

		if (rtl ? px + width > 0 : px - width < max_width) {
			SpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
			DrawSprite(v->GetImage(dir), pal, px + (rtl ? -offset.x : offset.x), 7 + offset.y);
		}

		if (!v->IsArticulatedPart()) sel_articulated = false;

		if (v->index == selection) {
			/* Set the highlight position */
			highlight_l = rtl ? px - width : px;
			highlight_r = rtl ? px - 1 : px + width - 1;
			sel_articulated = true;
		} else if ((_cursor.vehchain && highlight_r != 0) || sel_articulated) {
			if (rtl) {
				highlight_l -= width;
			} else {
				highlight_r += width;
			}
		}

		px += rtl ? -width : width;
	}

	if (highlight_l != highlight_r) {
		/* Draw the highlight. Now done after drawing all the engines, as
		 * the next engine after the highlight could overlap it. */
		DrawFrameRect(highlight_l, 0, highlight_r, 13, COLOUR_WHITE, FR_BORDERONLY);
	}

	_cur_dpi = old_dpi;
}

/** Helper struct for the cargo details information */
struct CargoSummaryItem {
	CargoID cargo;    ///< The cargo that is carried
	StringID subtype; ///< STR_EMPTY if none
	uint capacity;    ///< Amount that can be carried
	uint amount;      ///< Amount that is carried
	StationID source; ///< One of the source stations

	/** Used by CargoSummary::Find() and similiar functions */
	FORCEINLINE bool operator != (const CargoSummaryItem &other) const
	{
		return this->cargo != other.cargo || this->subtype != other.subtype;
	}
};

enum {
	TRAIN_DETAILS_MIN_INDENT = 32, ///< Minimum indent level in the train details window
	TRAIN_DETAILS_MAX_INDENT = 72, ///< Maximum indent level in the train details window; wider than this and we start on a new line
};

/** Container for the cargo summary information. */
typedef SmallVector<CargoSummaryItem, 2> CargoSummary;
/** Reused container of cargo details */
static CargoSummary _cargo_summary;

/**
 * Draw the details cargo tab for the given vehicle at the given position
 *
 * @param item  Data to draw
 * @param left  The left most coordinate to draw
 * @param right The right most coordinate to draw
 * @param y     The y coordinate
 */
static void TrainDetailsCargoTab(const CargoSummaryItem *item, int left, int right, int y)
{
	StringID str = STR_VEHICLE_DETAILS_CARGO_EMPTY;

	if (item->amount > 0) {
		SetDParam(0, item->cargo);
		SetDParam(1, item->amount);
		SetDParam(2, item->source);
		SetDParam(3, _settings_game.vehicle.freight_trains);
		str = FreightWagonMult(item->cargo) > 1 ? STR_VEHICLE_DETAILS_CARGO_FROM_MULT : STR_VEHICLE_DETAILS_CARGO_FROM;
	}

	DrawString(left, right, y, str);
}

/**
 * Draw the details info tab for the given vehicle at the given position
 *
 * @param v     current vehicle
 * @param left  The left most coordinate to draw
 * @param right The right most coordinate to draw
 * @param y     The y coordinate
 */
static void TrainDetailsInfoTab(const Vehicle *v, int left, int right, int y)
{
	if (RailVehInfo(v->engine_type)->railveh_type == RAILVEH_WAGON) {
		SetDParam(0, v->engine_type);
		SetDParam(1, v->value);
		DrawString(left, right, y, STR_VEHICLE_DETAILS_TRAIN_WAGON_VALUE, TC_FROMSTRING, SA_LEFT | SA_STRIP);
	} else {
		SetDParam(0, v->engine_type);
		SetDParam(1, v->build_year);
		SetDParam(2, v->value);
		DrawString(left, right, y, STR_VEHICLE_DETAILS_TRAIN_ENGINE_BUILT_AND_VALUE, TC_FROMSTRING, SA_LEFT | SA_STRIP);
	}
}

/**
 * Draw the details capacity tab for the given vehicle at the given position
 *
 * @param item  Data to draw
 * @param left  The left most coordinate to draw
 * @param right The right most coordinate to draw
 * @param y     The y coordinate
 */
static void TrainDetailsCapacityTab(const CargoSummaryItem *item, int left, int right, int y)
{
	SetDParam(0, item->cargo);
	SetDParam(1, item->capacity);
	SetDParam(4, item->subtype);
	SetDParam(5, _settings_game.vehicle.freight_trains);
	DrawString(left, right, y, FreightWagonMult(item->cargo) > 1 ? STR_VEHICLE_INFO_CAPACITY_MULT : STR_VEHICLE_INFO_CAPACITY);
}

/**
 * Collects the cargo transportet
 * @param v Vehicle to process
 * @param summary Space for the result
 */
static void GetCargoSummaryOfArticulatedVehicle(const Train *v, CargoSummary *summary)
{
	summary->Clear();
	do {
		if (v->cargo_cap == 0) continue;

		CargoSummaryItem new_item;
		new_item.cargo = v->cargo_type;
		new_item.subtype = GetCargoSubtypeText(v);

		CargoSummaryItem *item = summary->Find(new_item);
		if (item == summary->End()) {
			item = summary->Append();
			item->cargo = new_item.cargo;
			item->subtype = new_item.subtype;
			item->capacity = 0;
			item->amount = 0;
			item->source = INVALID_STATION;
		}

		item->capacity += v->cargo_cap;
		item->amount += v->cargo.Count();
		if (item->source == INVALID_STATION) item->source = v->cargo.Source();
	} while ((v = v->Next()) != NULL && v->IsArticulatedPart());
}

/**
 * Get the length of an articulated vehicle.
 * @param v the vehicle to get the length of.
 * @return the length in pixels.
 */
static uint GetLengthOfArticulatedVehicle(const Train *v)
{
	uint length = 0;

	do {
		length += v->GetDisplayImageWidth();
	} while ((v = v->Next()) != NULL && v->IsArticulatedPart());

	return length;
}

/**
 * Determines the number of lines in the train details window
 * @param veh_id Train
 * @param det_tab Selected details tab
 * @return Number of line
 */
int GetTrainDetailsWndVScroll(VehicleID veh_id, TrainDetailsWindowTabs det_tab)
{
	int num = 0;

	if (det_tab == TDW_TAB_TOTALS) { // Total cargo tab
		CargoArray act_cargo;
		CargoArray max_cargo;
		for (const Vehicle *v = Vehicle::Get(veh_id); v != NULL; v = v->Next()) {
			act_cargo[v->cargo_type] += v->cargo.Count();
			max_cargo[v->cargo_type] += v->cargo_cap;
		}

		/* Set scroll-amount seperately from counting, as to not compute num double
		 * for more carriages of the same type
		 */
		for (CargoID i = 0; i < NUM_CARGO; i++) {
			if (max_cargo[i] > 0) num++; // only count carriages that the train has
		}
		num++; // needs one more because first line is description string
	} else {
		for (const Train *v = Train::Get(veh_id); v != NULL; v = v->GetNextVehicle()) {
			GetCargoSummaryOfArticulatedVehicle(v, &_cargo_summary);
			num += max(1u, _cargo_summary.Length());

			uint length = GetLengthOfArticulatedVehicle(v);
			if (length > TRAIN_DETAILS_MAX_INDENT) num++;
		}
	}

	return num;
}

/**
 * Draw the details for the given vehicle at the given position
 *
 * @param v     current vehicle
 * @param left  The left most coordinate to draw
 * @param right The right most coordinate to draw
 * @param y     The y coordinate
 * @param vscroll_pos Position of scrollbar
 * @param vscroll_cap Number of lines currently displayed
 * @param det_tab Selected details tab
 */
void DrawTrainDetails(const Train *v, int left, int right, int y, int vscroll_pos, uint16 vscroll_cap, TrainDetailsWindowTabs det_tab)
{
	/* draw the first 3 details tabs */
	if (det_tab != TDW_TAB_TOTALS) {
		bool rtl = _dynlang.text_dir == TD_RTL;
		Direction dir = rtl ? DIR_E : DIR_W;
		int x = rtl ? right : left;
		int sprite_y_offset = 4 + (FONT_HEIGHT_NORMAL - 10) / 2;
		int line_height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
		for (; v != NULL && vscroll_pos > -vscroll_cap; v = v->GetNextVehicle()) {
			GetCargoSummaryOfArticulatedVehicle(v, &_cargo_summary);

			/* Draw sprites */
			int dx = 0;
			int px = x;
			const Train *u = v;
			do {
				Point offset;
				int width = u->GetDisplayImageWidth(&offset);
				if (vscroll_pos <= 0 && vscroll_pos > -vscroll_cap) {
					SpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
					DrawSprite(u->GetImage(dir), pal, px + (rtl ? -offset.x : offset.x), y - line_height * vscroll_pos + sprite_y_offset + offset.y);
				}
				px += rtl ? -width : width;
				dx += width;
				u = u->Next();
			} while (u != NULL && u->IsArticulatedPart());

			bool separate_sprite_row = (dx > TRAIN_DETAILS_MAX_INDENT);
			if (separate_sprite_row) {
				vscroll_pos--;
				dx = 0;
			}

			uint num_lines = max(1u, _cargo_summary.Length());
			for (uint i = 0; i < num_lines; i++) {
				int sprite_width = max<int>(dx, TRAIN_DETAILS_MIN_INDENT) + 3;
				int data_left  = left + (rtl ? 0 : sprite_width);
				int data_right = right - (rtl ? sprite_width : 0);
				if (vscroll_pos <= 0 && vscroll_pos > -vscroll_cap) {
					int py = y - line_height * vscroll_pos;
					if (i > 0 || separate_sprite_row) {
						if (vscroll_pos != 0) GfxFillRect(left, py - WD_MATRIX_TOP - 1, right, py - WD_MATRIX_TOP, _colour_gradient[COLOUR_GREY][5]);
					}
					switch (det_tab) {
						case TDW_TAB_CARGO:
							if (i < _cargo_summary.Length()) {
								TrainDetailsCargoTab(&_cargo_summary[i], data_left, data_right, py);
							} else {
								DrawString(data_left, data_right, py, STR_QUANTITY_N_A, TC_LIGHT_BLUE);
							}
							break;

						case TDW_TAB_INFO:
							if (i == 0) TrainDetailsInfoTab(v, data_left, data_right, py);
							break;

						case TDW_TAB_CAPACITY:
							if (i < _cargo_summary.Length()) {
								TrainDetailsCapacityTab(&_cargo_summary[i], data_left, data_right, py);
							} else {
								DrawString(data_left, data_right, py, STR_VEHICLE_INFO_NO_CAPACITY);
							}
							break;

						default: NOT_REACHED();
					}
				}
				vscroll_pos--;
			}
		}
	} else {
		CargoArray act_cargo;
		CargoArray max_cargo;
		Money feeder_share = 0;

		for (const Vehicle *u = v; u != NULL; u = u->Next()) {
			act_cargo[u->cargo_type] += u->cargo.Count();
			max_cargo[u->cargo_type] += u->cargo_cap;
			feeder_share             += u->cargo.FeederShare();
		}

		/* draw total cargo tab */
		DrawString(left, right, y, STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY_TEXT);
		y += WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;

		for (CargoID i = 0; i < NUM_CARGO; i++) {
			if (max_cargo[i] > 0 && --vscroll_pos < 0 && vscroll_pos > -vscroll_cap) {
				SetDParam(0, i);            // {CARGO} #1
				SetDParam(1, act_cargo[i]); // {CARGO} #2
				SetDParam(2, i);            // {SHORTCARGO} #1
				SetDParam(3, max_cargo[i]); // {SHORTCARGO} #2
				SetDParam(4, _settings_game.vehicle.freight_trains);
				DrawString(left, right, y, FreightWagonMult(i) > 1 ? STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY_MULT : STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY);
				y += WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
			}
		}
		SetDParam(0, feeder_share);
		DrawString(left, right, y, STR_VEHICLE_INFO_FEEDER_CARGO_VALUE);
	}
}