|
1 /* $Id$ */ |
|
2 |
|
3 #include "stdafx.h" |
|
4 #include "openttd.h" |
|
5 #include "bridge_map.h" |
|
6 #include "debug.h" |
|
7 #include "functions.h" |
|
8 #include "gui.h" |
|
9 #include "station_map.h" |
|
10 #include "table/strings.h" |
|
11 #include "map.h" |
|
12 #include "tile.h" |
|
13 #include "tunnel_map.h" |
|
14 #include "vehicle.h" |
|
15 #include "command.h" |
|
16 #include "pathfind.h" |
|
17 #include "npf.h" |
|
18 #include "station.h" |
|
19 #include "table/train_cmd.h" |
|
20 #include "news.h" |
|
21 #include "engine.h" |
|
22 #include "player.h" |
|
23 #include "sound.h" |
|
24 #include "depot.h" |
|
25 #include "waypoint.h" |
|
26 #include "vehicle_gui.h" |
|
27 #include "train.h" |
|
28 #include "bridge.h" |
|
29 #include "newgrf_callbacks.h" |
|
30 #include "newgrf_engine.h" |
|
31 #include "newgrf_sound.h" |
|
32 #include "newgrf_text.h" |
|
33 #include "direction.h" |
|
34 #include "yapf/yapf.h" |
|
35 #include "date.h" |
|
36 |
|
37 static bool TrainCheckIfLineEnds(Vehicle *v); |
|
38 static void TrainController(Vehicle *v, bool update_image); |
|
39 |
|
40 static const byte _vehicle_initial_x_fract[4] = {10, 8, 4, 8}; |
|
41 static const byte _vehicle_initial_y_fract[4] = { 8, 4, 8, 10}; |
|
42 static const byte _state_dir_table[4] = { 0x20, 8, 0x10, 4 }; |
|
43 |
|
44 |
|
45 /** Return the cargo weight multiplier to use for a rail vehicle |
|
46 * @param cargo Cargo type to get multiplier for |
|
47 * @return Cargo weight multiplier |
|
48 */ |
|
49 byte FreightWagonMult(CargoID cargo) |
|
50 { |
|
51 // XXX NewCargos introduces a specific "is freight" flag for this test. |
|
52 if (cargo == CT_PASSENGERS || cargo == CT_MAIL) return 1; |
|
53 return _patches.freight_trains; |
|
54 } |
|
55 |
|
56 |
|
57 /** |
|
58 * Recalculates the cached total power of a train. Should be called when the consist is changed |
|
59 * @param v First vehicle of the consist. |
|
60 */ |
|
61 void TrainPowerChanged(Vehicle* v) |
|
62 { |
|
63 Vehicle* u; |
|
64 uint32 power = 0; |
|
65 uint32 max_te = 0; |
|
66 |
|
67 for (u = v; u != NULL; u = u->next) { |
|
68 const RailVehicleInfo *rvi_u; |
|
69 bool engine_has_power = true; |
|
70 bool wagon_has_power = true; |
|
71 |
|
72 /* Power is not added for articulated parts */ |
|
73 if (IsArticulatedPart(u)) continue; |
|
74 |
|
75 if (IsLevelCrossingTile(u->tile)) { |
|
76 if (!HasPowerOnRail(u->u.rail.railtype, GetRailTypeCrossing(u->tile))) engine_has_power = false; |
|
77 if (!HasPowerOnRail(v->u.rail.railtype, GetRailTypeCrossing(u->tile))) wagon_has_power = false; |
|
78 } else { |
|
79 if (!HasPowerOnRail(u->u.rail.railtype, GetRailType(u->tile))) engine_has_power = false; |
|
80 if (!HasPowerOnRail(v->u.rail.railtype, GetRailType(u->tile))) wagon_has_power = false; |
|
81 } |
|
82 |
|
83 rvi_u = RailVehInfo(u->engine_type); |
|
84 |
|
85 if (engine_has_power && rvi_u->power != 0) { |
|
86 power += rvi_u->power; |
|
87 /* Tractive effort in (tonnes * 1000 * 10 =) N */ |
|
88 max_te += (u->u.rail.cached_veh_weight * 10000 * rvi_u->tractive_effort) / 256; |
|
89 } |
|
90 |
|
91 if (HASBIT(u->u.rail.flags, VRF_POWEREDWAGON) && (wagon_has_power)) { |
|
92 power += RailVehInfo(u->u.rail.first_engine)->pow_wag_power; |
|
93 } |
|
94 } |
|
95 |
|
96 if (v->u.rail.cached_power != power || v->u.rail.cached_max_te != max_te) { |
|
97 v->u.rail.cached_power = power; |
|
98 v->u.rail.cached_max_te = max_te; |
|
99 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
100 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
|
101 } |
|
102 } |
|
103 |
|
104 |
|
105 /** |
|
106 * Recalculates the cached weight of a train and its vehicles. Should be called each time the cargo on |
|
107 * the consist changes. |
|
108 * @param v First vehicle of the consist. |
|
109 */ |
|
110 static void TrainCargoChanged(Vehicle* v) |
|
111 { |
|
112 Vehicle *u; |
|
113 uint32 weight = 0; |
|
114 |
|
115 for (u = v; u != NULL; u = u->next) { |
|
116 const RailVehicleInfo *rvi = RailVehInfo(u->engine_type); |
|
117 uint32 vweight = (_cargoc.weights[u->cargo_type] * u->cargo_count * FreightWagonMult(u->cargo_type)) / 16; |
|
118 |
|
119 // Vehicle weight is not added for articulated parts. |
|
120 if (!IsArticulatedPart(u)) { |
|
121 // vehicle weight is the sum of the weight of the vehicle and the weight of its cargo |
|
122 vweight += rvi->weight; |
|
123 |
|
124 // powered wagons have extra weight added |
|
125 if (HASBIT(u->u.rail.flags, VRF_POWEREDWAGON)) |
|
126 vweight += RailVehInfo(u->u.rail.first_engine)->pow_wag_weight; |
|
127 } |
|
128 |
|
129 // consist weight is the sum of the weight of all vehicles in the consist |
|
130 weight += vweight; |
|
131 |
|
132 // store vehicle weight in cache |
|
133 u->u.rail.cached_veh_weight = vweight; |
|
134 }; |
|
135 |
|
136 // store consist weight in cache |
|
137 v->u.rail.cached_weight = weight; |
|
138 |
|
139 /* Now update train power (tractive effort is dependent on weight) */ |
|
140 TrainPowerChanged(v); |
|
141 } |
|
142 |
|
143 |
|
144 /** |
|
145 * Recalculates the cached stuff of a train. Should be called each time a vehicle is added |
|
146 * to/removed from the chain, and when the game is loaded. |
|
147 * Note: this needs to be called too for 'wagon chains' (in the depot, without an engine) |
|
148 * @param v First vehicle of the chain. |
|
149 */ |
|
150 void TrainConsistChanged(Vehicle* v) |
|
151 { |
|
152 const RailVehicleInfo *rvi_v; |
|
153 Vehicle *u; |
|
154 uint16 max_speed = 0xFFFF; |
|
155 EngineID first_engine; |
|
156 |
|
157 assert(v->type == VEH_Train); |
|
158 |
|
159 assert(IsFrontEngine(v) || IsFreeWagon(v)); |
|
160 |
|
161 rvi_v = RailVehInfo(v->engine_type); |
|
162 first_engine = IsFrontEngine(v) ? v->engine_type : INVALID_ENGINE; |
|
163 v->u.rail.cached_total_length = 0; |
|
164 v->u.rail.compatible_railtypes = 0; |
|
165 |
|
166 for (u = v; u != NULL; u = u->next) { |
|
167 const RailVehicleInfo *rvi_u = RailVehInfo(u->engine_type); |
|
168 uint16 veh_len; |
|
169 |
|
170 // Update the v->first cache. This is faster than having to brute force it later. |
|
171 if (u->first == NULL) u->first = v; |
|
172 |
|
173 // update the 'first engine' |
|
174 u->u.rail.first_engine = (v == u) ? INVALID_ENGINE : first_engine; |
|
175 u->u.rail.railtype = GetEngine(u->engine_type)->railtype; |
|
176 |
|
177 if (IsTrainEngine(u)) first_engine = u->engine_type; |
|
178 |
|
179 if (rvi_u->visual_effect != 0) { |
|
180 u->u.rail.cached_vis_effect = rvi_u->visual_effect; |
|
181 } else { |
|
182 if (IsTrainWagon(u) || IsArticulatedPart(u)) { |
|
183 // Wagons and articulated parts have no effect by default |
|
184 u->u.rail.cached_vis_effect = 0x40; |
|
185 } else if (rvi_u->engclass == 0) { |
|
186 // Steam is offset by -4 units |
|
187 u->u.rail.cached_vis_effect = 4; |
|
188 } else { |
|
189 // Diesel fumes and sparks come from the centre |
|
190 u->u.rail.cached_vis_effect = 8; |
|
191 } |
|
192 } |
|
193 |
|
194 if (!IsArticulatedPart(u)) { |
|
195 // check if its a powered wagon |
|
196 CLRBIT(u->u.rail.flags, VRF_POWEREDWAGON); |
|
197 |
|
198 /* Check powered wagon / visual effect callback */ |
|
199 if (HASBIT(EngInfo(u->engine_type)->callbackmask, CBM_WAGON_POWER)) { |
|
200 uint16 callback = GetVehicleCallback(CBID_TRAIN_WAGON_POWER, 0, 0, u->engine_type, u); |
|
201 |
|
202 if (callback != CALLBACK_FAILED) u->u.rail.cached_vis_effect = callback; |
|
203 } |
|
204 |
|
205 if ((rvi_v->pow_wag_power != 0) && (rvi_u->flags & RVI_WAGON) && UsesWagonOverride(u)) { |
|
206 if (u->u.rail.cached_vis_effect < 0x40) { |
|
207 /* wagon is powered */ |
|
208 SETBIT(u->u.rail.flags, VRF_POWEREDWAGON); // cache 'powered' status |
|
209 } |
|
210 } |
|
211 |
|
212 /* Do not count powered wagons for the compatible railtypes, as wagons always |
|
213 have railtype normal */ |
|
214 if (rvi_u->power > 0) { |
|
215 v->u.rail.compatible_railtypes |= GetRailTypeInfo(u->u.rail.railtype)->powered_railtypes; |
|
216 } |
|
217 |
|
218 /* Some electric engines can be allowed to run on normal rail. It happens to all |
|
219 * existing electric engines when elrails are disabled and then re-enabled */ |
|
220 if (HASBIT(u->u.rail.flags, VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL)) { |
|
221 u->u.rail.railtype = RAILTYPE_RAIL; |
|
222 u->u.rail.compatible_railtypes |= (1 << RAILTYPE_RAIL); |
|
223 } |
|
224 |
|
225 // max speed is the minimum of the speed limits of all vehicles in the consist |
|
226 if (!(rvi_u->flags & RVI_WAGON) || _patches.wagon_speed_limits) |
|
227 if (rvi_u->max_speed != 0 && !UsesWagonOverride(u)) |
|
228 max_speed = min(rvi_u->max_speed, max_speed); |
|
229 } |
|
230 |
|
231 // check the vehicle length (callback) |
|
232 veh_len = CALLBACK_FAILED; |
|
233 if (HASBIT(EngInfo(u->engine_type)->callbackmask, CBM_VEHICLE_LENGTH)) { |
|
234 veh_len = GetVehicleCallback(CBID_TRAIN_VEHICLE_LENGTH, 0, 0, u->engine_type, u); |
|
235 } |
|
236 if (veh_len == CALLBACK_FAILED) veh_len = rvi_u->shorten_factor; |
|
237 veh_len = clamp(veh_len, 0, u->next == NULL ? 7 : 5); // the clamp on vehicles not the last in chain is stricter, as too short wagons can break the 'follow next vehicle' code |
|
238 u->u.rail.cached_veh_length = 8 - veh_len; |
|
239 v->u.rail.cached_total_length += u->u.rail.cached_veh_length; |
|
240 |
|
241 }; |
|
242 |
|
243 // store consist weight/max speed in cache |
|
244 v->u.rail.cached_max_speed = max_speed; |
|
245 |
|
246 // recalculate cached weights and power too (we do this *after* the rest, so it is known which wagons are powered and need extra weight added) |
|
247 TrainCargoChanged(v); |
|
248 } |
|
249 |
|
250 /* These two arrays are used for realistic acceleration. XXX: How should they |
|
251 * be interpreted? */ |
|
252 static const byte _curve_neighbours45[8][2] = { |
|
253 {7, 1}, |
|
254 {0, 2}, |
|
255 {1, 3}, |
|
256 {2, 4}, |
|
257 {3, 5}, |
|
258 {4, 6}, |
|
259 {5, 7}, |
|
260 {6, 0}, |
|
261 }; |
|
262 |
|
263 static const byte _curve_neighbours90[8][2] = { |
|
264 {6, 2}, |
|
265 {7, 3}, |
|
266 {0, 4}, |
|
267 {1, 5}, |
|
268 {2, 6}, |
|
269 {3, 7}, |
|
270 {4, 0}, |
|
271 {5, 1}, |
|
272 }; |
|
273 |
|
274 enum AccelType { |
|
275 AM_ACCEL, |
|
276 AM_BRAKE |
|
277 }; |
|
278 |
|
279 static bool TrainShouldStop(const Vehicle* v, TileIndex tile) |
|
280 { |
|
281 const Order* o = &v->current_order; |
|
282 StationID sid = GetStationIndex(tile); |
|
283 |
|
284 assert(v->type == VEH_Train); |
|
285 //When does a train drive through a station |
|
286 //first we deal with the "new nonstop handling" |
|
287 if (_patches.new_nonstop && o->flags & OF_NON_STOP && sid == o->dest) { |
|
288 return false; |
|
289 } |
|
290 |
|
291 if (v->last_station_visited == sid) return false; |
|
292 |
|
293 if (sid != o->dest && (o->flags & OF_NON_STOP || _patches.new_nonstop)) { |
|
294 return false; |
|
295 } |
|
296 |
|
297 return true; |
|
298 } |
|
299 |
|
300 //new acceleration |
|
301 static int GetTrainAcceleration(Vehicle *v, bool mode) |
|
302 { |
|
303 const Vehicle *u; |
|
304 int num = 0; //number of vehicles, change this into the number of axles later |
|
305 int power = 0; |
|
306 int mass = 0; |
|
307 int max_speed = 2000; |
|
308 int area = 120; |
|
309 int friction = 35; //[1e-3] |
|
310 int drag_coeff = 20; //[1e-4] |
|
311 int incl = 0; |
|
312 int resistance; |
|
313 int speed = v->cur_speed; //[mph] |
|
314 int force = 0x3FFFFFFF; |
|
315 int pos = 0; |
|
316 int lastpos = -1; |
|
317 int curvecount[2] = {0, 0}; |
|
318 int sum = 0; |
|
319 int numcurve = 0; |
|
320 int max_te = v->u.rail.cached_max_te; // [N] |
|
321 |
|
322 speed *= 10; |
|
323 speed /= 16; |
|
324 |
|
325 //first find the curve speed limit |
|
326 for (u = v; u->next != NULL; u = u->next, pos++) { |
|
327 Direction dir = u->direction; |
|
328 Direction ndir = u->next->direction; |
|
329 int i; |
|
330 |
|
331 for (i = 0; i < 2; i++) { |
|
332 if ( _curve_neighbours45[dir][i] == ndir) { |
|
333 curvecount[i]++; |
|
334 if (lastpos != -1) { |
|
335 numcurve++; |
|
336 sum += pos - lastpos; |
|
337 if (pos - lastpos == 1) { |
|
338 max_speed = 88; |
|
339 } |
|
340 } |
|
341 lastpos = pos; |
|
342 } |
|
343 } |
|
344 |
|
345 //if we have a 90 degree turn, fix the speed limit to 60 |
|
346 if (_curve_neighbours90[dir][0] == ndir || |
|
347 _curve_neighbours90[dir][1] == ndir) { |
|
348 max_speed = 61; |
|
349 } |
|
350 } |
|
351 |
|
352 if (numcurve > 0) sum /= numcurve; |
|
353 |
|
354 if ((curvecount[0] != 0 || curvecount[1] != 0) && max_speed > 88) { |
|
355 int total = curvecount[0] + curvecount[1]; |
|
356 |
|
357 if (curvecount[0] == 1 && curvecount[1] == 1) { |
|
358 max_speed = 0xFFFF; |
|
359 } else if (total > 1) { |
|
360 max_speed = 232 - (13 - clamp(sum, 1, 12)) * (13 - clamp(sum, 1, 12)); |
|
361 } |
|
362 } |
|
363 |
|
364 max_speed += (max_speed / 2) * v->u.rail.railtype; |
|
365 |
|
366 if (IsTileType(v->tile, MP_STATION) && IsFrontEngine(v)) { |
|
367 if (TrainShouldStop(v, v->tile)) { |
|
368 uint station_length = GetPlatformLength(v->tile, DirToDiagDir(v->direction)); |
|
369 int delta_v; |
|
370 |
|
371 max_speed = 120; |
|
372 |
|
373 delta_v = v->cur_speed / (station_length + 1); |
|
374 if (v->max_speed > (v->cur_speed - delta_v)) |
|
375 max_speed = v->cur_speed - (delta_v / 10); |
|
376 |
|
377 max_speed = max(max_speed, 25 * station_length); |
|
378 } |
|
379 } |
|
380 |
|
381 mass = v->u.rail.cached_weight; |
|
382 power = v->u.rail.cached_power * 746; |
|
383 max_speed = min(max_speed, v->u.rail.cached_max_speed); |
|
384 |
|
385 for (u = v; u != NULL; u = u->next) { |
|
386 num++; |
|
387 drag_coeff += 3; |
|
388 |
|
389 if (u->u.rail.track == 0x80) max_speed = min(max_speed, 61); |
|
390 |
|
391 if (HASBIT(u->u.rail.flags, VRF_GOINGUP)) { |
|
392 incl += u->u.rail.cached_veh_weight * 60; //3% slope, quite a bit actually |
|
393 } else if (HASBIT(u->u.rail.flags, VRF_GOINGDOWN)) { |
|
394 incl -= u->u.rail.cached_veh_weight * 60; |
|
395 } |
|
396 } |
|
397 |
|
398 v->max_speed = max_speed; |
|
399 |
|
400 if (v->u.rail.railtype != RAILTYPE_MAGLEV) { |
|
401 resistance = 13 * mass / 10; |
|
402 resistance += 60 * num; |
|
403 resistance += friction * mass * speed / 1000; |
|
404 resistance += (area * drag_coeff * speed * speed) / 10000; |
|
405 } else { |
|
406 resistance = (area * (drag_coeff / 2) * speed * speed) / 10000; |
|
407 } |
|
408 resistance += incl; |
|
409 resistance *= 4; //[N] |
|
410 |
|
411 /* Due to the mph to m/s conversion below, at speeds below 3 mph the force is |
|
412 * actually double the train's power */ |
|
413 if (speed > 2) { |
|
414 switch (v->u.rail.railtype) { |
|
415 case RAILTYPE_RAIL: |
|
416 case RAILTYPE_ELECTRIC: |
|
417 case RAILTYPE_MONO: |
|
418 force = power / speed; //[N] |
|
419 force *= 22; |
|
420 force /= 10; |
|
421 if (mode == AM_ACCEL && force > max_te) force = max_te; |
|
422 break; |
|
423 |
|
424 case RAILTYPE_MAGLEV: |
|
425 force = power / 25; |
|
426 break; |
|
427 } |
|
428 } else { |
|
429 //"kickoff" acceleration |
|
430 force = (mode == AM_ACCEL && v->u.rail.railtype != RAILTYPE_MAGLEV) ? min(max_te, power) : power; |
|
431 force = max(force, (mass * 8) + resistance); |
|
432 } |
|
433 |
|
434 if (force <= 0) force = 10000; |
|
435 |
|
436 if (v->u.rail.railtype != RAILTYPE_MAGLEV) force = min(force, mass * 10 * 200); |
|
437 |
|
438 if (mode == AM_ACCEL) { |
|
439 return (force - resistance) / (mass * 4); |
|
440 } else { |
|
441 return min((-force - resistance) / (mass * 4), -10000 / (mass * 4)); |
|
442 } |
|
443 } |
|
444 |
|
445 static void UpdateTrainAcceleration(Vehicle* v) |
|
446 { |
|
447 uint power = 0; |
|
448 uint weight = 0; |
|
449 |
|
450 assert(IsFrontEngine(v)); |
|
451 |
|
452 weight = v->u.rail.cached_weight; |
|
453 power = v->u.rail.cached_power; |
|
454 v->max_speed = v->u.rail.cached_max_speed; |
|
455 |
|
456 assert(weight != 0); |
|
457 |
|
458 v->acceleration = clamp(power / weight * 4, 1, 255); |
|
459 } |
|
460 |
|
461 int GetTrainImage(const Vehicle* v, Direction direction) |
|
462 { |
|
463 int img = v->spritenum; |
|
464 int base; |
|
465 |
|
466 if (HASBIT(v->u.rail.flags, VRF_REVERSE_DIRECTION)) direction = ReverseDir(direction); |
|
467 |
|
468 if (is_custom_sprite(img)) { |
|
469 base = GetCustomVehicleSprite(v, direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(img)); |
|
470 if (base != 0) return base; |
|
471 img = orig_rail_vehicle_info[v->engine_type].image_index; |
|
472 } |
|
473 |
|
474 base = _engine_sprite_base[img] + ((direction + _engine_sprite_add[img]) & _engine_sprite_and[img]); |
|
475 |
|
476 if (v->cargo_count >= v->cargo_cap / 2) base += _wagon_full_adder[img]; |
|
477 return base; |
|
478 } |
|
479 |
|
480 void DrawTrainEngine(int x, int y, EngineID engine, uint32 image_ormod) |
|
481 { |
|
482 const RailVehicleInfo *rvi = RailVehInfo(engine); |
|
483 |
|
484 int img = rvi->image_index; |
|
485 uint32 image = 0; |
|
486 |
|
487 if (is_custom_sprite(img)) { |
|
488 image = GetCustomVehicleIcon(engine, DIR_W); |
|
489 if (image == 0) { |
|
490 img = orig_rail_vehicle_info[engine].image_index; |
|
491 } else { |
|
492 y += _traininfo_vehicle_pitch; |
|
493 } |
|
494 } |
|
495 if (image == 0) { |
|
496 image = (6 & _engine_sprite_and[img]) + _engine_sprite_base[img]; |
|
497 } |
|
498 |
|
499 if (rvi->flags & RVI_MULTIHEAD) { |
|
500 DrawSprite(image | image_ormod, x - 14, y); |
|
501 x += 15; |
|
502 image = 0; |
|
503 if (is_custom_sprite(img)) { |
|
504 image = GetCustomVehicleIcon(engine, 2); |
|
505 if (image == 0) img = orig_rail_vehicle_info[engine].image_index; |
|
506 } |
|
507 if (image == 0) { |
|
508 image = |
|
509 ((6 + _engine_sprite_add[img + 1]) & _engine_sprite_and[img + 1]) + |
|
510 _engine_sprite_base[img + 1]; |
|
511 } |
|
512 } |
|
513 DrawSprite(image | image_ormod, x, y); |
|
514 } |
|
515 |
|
516 uint CountArticulatedParts(EngineID engine_type) |
|
517 { |
|
518 uint16 callback; |
|
519 uint i; |
|
520 |
|
521 if (!HASBIT(EngInfo(engine_type)->callbackmask, CBM_ARTIC_ENGINE)) return 0; |
|
522 |
|
523 for (i = 1; i < 10; i++) { |
|
524 callback = GetVehicleCallback(CBID_TRAIN_ARTIC_ENGINE, i, 0, engine_type, NULL); |
|
525 if (callback == CALLBACK_FAILED || callback == 0xFF) break; |
|
526 } |
|
527 |
|
528 return i - 1; |
|
529 } |
|
530 |
|
531 static void AddArticulatedParts(Vehicle **vl) |
|
532 { |
|
533 const RailVehicleInfo *rvi_artic; |
|
534 EngineID engine_type; |
|
535 Vehicle *v = vl[0]; |
|
536 Vehicle *u = v; |
|
537 uint16 callback; |
|
538 bool flip_image; |
|
539 uint i; |
|
540 |
|
541 if (!HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_ARTIC_ENGINE)) return; |
|
542 |
|
543 for (i = 1; i < 10; i++) { |
|
544 callback = GetVehicleCallback(CBID_TRAIN_ARTIC_ENGINE, i, 0, v->engine_type, v); |
|
545 if (callback == CALLBACK_FAILED || callback == 0xFF) return; |
|
546 |
|
547 /* Attempt to use pre-allocated vehicles until they run out. This can happen |
|
548 * if the callback returns different values depending on the cargo type. */ |
|
549 u->next = vl[i]; |
|
550 if (u->next == NULL) u->next = AllocateVehicle(); |
|
551 if (u->next == NULL) return; |
|
552 |
|
553 u = u->next; |
|
554 |
|
555 engine_type = GB(callback, 0, 7); |
|
556 flip_image = HASBIT(callback, 7); |
|
557 rvi_artic = RailVehInfo(engine_type); |
|
558 |
|
559 // get common values from first engine |
|
560 u->direction = v->direction; |
|
561 u->owner = v->owner; |
|
562 u->tile = v->tile; |
|
563 u->x_pos = v->x_pos; |
|
564 u->y_pos = v->y_pos; |
|
565 u->z_pos = v->z_pos; |
|
566 u->z_height = v->z_height; |
|
567 u->u.rail.track = v->u.rail.track; |
|
568 u->u.rail.railtype = v->u.rail.railtype; |
|
569 u->build_year = v->build_year; |
|
570 u->vehstatus = v->vehstatus & ~VS_STOPPED; |
|
571 u->u.rail.first_engine = v->engine_type; |
|
572 |
|
573 // get more settings from rail vehicle info |
|
574 u->spritenum = rvi_artic->image_index; |
|
575 if (flip_image) u->spritenum++; |
|
576 u->cargo_type = rvi_artic->cargo_type; |
|
577 u->cargo_subtype = 0; |
|
578 u->cargo_cap = rvi_artic->capacity; |
|
579 u->max_speed = 0; |
|
580 u->max_age = 0; |
|
581 u->engine_type = engine_type; |
|
582 u->value = 0; |
|
583 u->type = VEH_Train; |
|
584 u->subtype = 0; |
|
585 SetArticulatedPart(u); |
|
586 u->cur_image = 0xAC2; |
|
587 u->random_bits = VehicleRandomBits(); |
|
588 |
|
589 VehiclePositionChanged(u); |
|
590 } |
|
591 } |
|
592 |
|
593 static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags) |
|
594 { |
|
595 int32 value; |
|
596 const RailVehicleInfo *rvi; |
|
597 uint num_vehicles; |
|
598 |
|
599 SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
600 |
|
601 rvi = RailVehInfo(engine); |
|
602 value = (rvi->base_cost * _price.build_railwagon) >> 8; |
|
603 |
|
604 num_vehicles = 1 + CountArticulatedParts(engine); |
|
605 |
|
606 if (!(flags & DC_QUERY_COST)) { |
|
607 Vehicle *vl[11]; // Allow for wagon and upto 10 artic parts. |
|
608 Vehicle* v; |
|
609 int x; |
|
610 int y; |
|
611 |
|
612 memset(&vl, 0, sizeof(vl)); |
|
613 |
|
614 if (!AllocateVehicles(vl, num_vehicles)) |
|
615 return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); |
|
616 |
|
617 if (flags & DC_EXEC) { |
|
618 Vehicle *u, *w; |
|
619 DiagDirection dir; |
|
620 |
|
621 v = vl[0]; |
|
622 v->spritenum = rvi->image_index; |
|
623 |
|
624 u = NULL; |
|
625 |
|
626 FOR_ALL_VEHICLES(w) { |
|
627 if (w->type == VEH_Train && w->tile == tile && |
|
628 IsFreeWagon(w) && w->engine_type == engine) { |
|
629 u = GetLastVehicleInChain(w); |
|
630 break; |
|
631 } |
|
632 } |
|
633 |
|
634 v->engine_type = engine; |
|
635 |
|
636 dir = GetRailDepotDirection(tile); |
|
637 |
|
638 v->direction = DiagDirToDir(dir); |
|
639 v->tile = tile; |
|
640 |
|
641 x = TileX(tile) * TILE_SIZE | _vehicle_initial_x_fract[dir]; |
|
642 y = TileY(tile) * TILE_SIZE | _vehicle_initial_y_fract[dir]; |
|
643 |
|
644 v->x_pos = x; |
|
645 v->y_pos = y; |
|
646 v->z_pos = GetSlopeZ(x,y); |
|
647 v->owner = _current_player; |
|
648 v->z_height = 6; |
|
649 v->u.rail.track = 0x80; |
|
650 v->vehstatus = VS_HIDDEN | VS_DEFPAL; |
|
651 |
|
652 v->subtype = 0; |
|
653 SetTrainWagon(v); |
|
654 if (u != NULL) { |
|
655 u->next = v; |
|
656 } else { |
|
657 SetFreeWagon(v); |
|
658 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); |
|
659 } |
|
660 |
|
661 v->cargo_type = rvi->cargo_type; |
|
662 v->cargo_subtype = 0; |
|
663 v->cargo_cap = rvi->capacity; |
|
664 v->value = value; |
|
665 // v->day_counter = 0; |
|
666 |
|
667 v->u.rail.railtype = GetEngine(engine)->railtype; |
|
668 |
|
669 v->build_year = _cur_year; |
|
670 v->type = VEH_Train; |
|
671 v->cur_image = 0xAC2; |
|
672 v->random_bits = VehicleRandomBits(); |
|
673 |
|
674 AddArticulatedParts(vl); |
|
675 |
|
676 _new_vehicle_id = v->index; |
|
677 |
|
678 VehiclePositionChanged(v); |
|
679 TrainConsistChanged(GetFirstVehicleInChain(v)); |
|
680 GetPlayer(_current_player)->num_engines[engine]++; |
|
681 |
|
682 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
|
683 if (IsLocalPlayer()) { |
|
684 InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train); // updates the replace Train window |
|
685 } |
|
686 } |
|
687 } |
|
688 |
|
689 return value; |
|
690 } |
|
691 |
|
692 // Move all free vehicles in the depot to the train |
|
693 static void NormalizeTrainVehInDepot(const Vehicle* u) |
|
694 { |
|
695 const Vehicle* v; |
|
696 |
|
697 FOR_ALL_VEHICLES(v) { |
|
698 if (v->type == VEH_Train && IsFreeWagon(v) && |
|
699 v->tile == u->tile && |
|
700 v->u.rail.track == 0x80) { |
|
701 if (CmdFailed(DoCommand(0, v->index | (u->index << 16), 1, DC_EXEC, |
|
702 CMD_MOVE_RAIL_VEHICLE))) |
|
703 break; |
|
704 } |
|
705 } |
|
706 } |
|
707 |
|
708 static int32 EstimateTrainCost(const RailVehicleInfo* rvi) |
|
709 { |
|
710 return rvi->base_cost * (_price.build_railvehicle >> 3) >> 5; |
|
711 } |
|
712 |
|
713 static void AddRearEngineToMultiheadedTrain(Vehicle* v, Vehicle* u, bool building) |
|
714 { |
|
715 u->direction = v->direction; |
|
716 u->owner = v->owner; |
|
717 u->tile = v->tile; |
|
718 u->x_pos = v->x_pos; |
|
719 u->y_pos = v->y_pos; |
|
720 u->z_pos = v->z_pos; |
|
721 u->z_height = 6; |
|
722 u->u.rail.track = 0x80; |
|
723 u->vehstatus = v->vehstatus & ~VS_STOPPED; |
|
724 u->subtype = 0; |
|
725 SetMultiheaded(u); |
|
726 u->spritenum = v->spritenum + 1; |
|
727 u->cargo_type = v->cargo_type; |
|
728 u->cargo_subtype = v->cargo_subtype; |
|
729 u->cargo_cap = v->cargo_cap; |
|
730 u->u.rail.railtype = v->u.rail.railtype; |
|
731 if (building) v->next = u; |
|
732 u->engine_type = v->engine_type; |
|
733 u->build_year = v->build_year; |
|
734 if (building) v->value >>= 1; |
|
735 u->value = v->value; |
|
736 u->type = VEH_Train; |
|
737 u->cur_image = 0xAC2; |
|
738 u->random_bits = VehicleRandomBits(); |
|
739 VehiclePositionChanged(u); |
|
740 } |
|
741 |
|
742 /** Build a railroad vehicle. |
|
743 * @param tile tile of the depot where rail-vehicle is built |
|
744 * @param p1 engine type id |
|
745 * @param p2 bit 0 when set, the train will get number 0, otherwise it will get a free number |
|
746 * bit 1 prevents any free cars from being added to the train |
|
747 */ |
|
748 int32 CmdBuildRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
749 { |
|
750 const RailVehicleInfo *rvi; |
|
751 int value; |
|
752 Vehicle *v; |
|
753 UnitID unit_num; |
|
754 Engine *e; |
|
755 uint num_vehicles; |
|
756 |
|
757 /* Check if the engine-type is valid (for the player) */ |
|
758 if (!IsEngineBuildable(p1, VEH_Train, _current_player)) return_cmd_error(STR_ENGINE_NOT_BUILDABLE); |
|
759 |
|
760 /* Check if the train is actually being built in a depot belonging |
|
761 * to the player. Doesn't matter if only the cost is queried */ |
|
762 if (!(flags & DC_QUERY_COST)) { |
|
763 if (!IsTileDepotType(tile, TRANSPORT_RAIL)) return CMD_ERROR; |
|
764 if (!IsTileOwner(tile, _current_player)) return CMD_ERROR; |
|
765 } |
|
766 |
|
767 SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
768 |
|
769 rvi = RailVehInfo(p1); |
|
770 e = GetEngine(p1); |
|
771 |
|
772 /* Check if depot and new engine uses the same kind of tracks */ |
|
773 /* We need to see if the engine got power on the tile to avoid eletric engines in non-electric depots */ |
|
774 if (!HasPowerOnRail(e->railtype, GetRailType(tile))) return CMD_ERROR; |
|
775 |
|
776 if (rvi->flags & RVI_WAGON) return CmdBuildRailWagon(p1, tile, flags); |
|
777 |
|
778 value = EstimateTrainCost(rvi); |
|
779 |
|
780 num_vehicles = (rvi->flags & RVI_MULTIHEAD) ? 2 : 1; |
|
781 num_vehicles += CountArticulatedParts(p1); |
|
782 |
|
783 if (!(flags & DC_QUERY_COST)) { |
|
784 Vehicle *vl[12]; // Allow for upto 10 artic parts and dual-heads |
|
785 |
|
786 memset(&vl, 0, sizeof(vl)); |
|
787 |
|
788 if (!AllocateVehicles(vl, num_vehicles) || IsOrderPoolFull()) |
|
789 return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); |
|
790 |
|
791 v = vl[0]; |
|
792 |
|
793 unit_num = HASBIT(p2, 0) ? 0 : GetFreeUnitNumber(VEH_Train); |
|
794 if (unit_num > _patches.max_trains) |
|
795 return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); |
|
796 |
|
797 if (flags & DC_EXEC) { |
|
798 DiagDirection dir = GetRailDepotDirection(tile); |
|
799 int x = TileX(tile) * TILE_SIZE + _vehicle_initial_x_fract[dir]; |
|
800 int y = TileY(tile) * TILE_SIZE + _vehicle_initial_y_fract[dir]; |
|
801 |
|
802 v->unitnumber = unit_num; |
|
803 v->direction = DiagDirToDir(dir); |
|
804 v->tile = tile; |
|
805 v->owner = _current_player; |
|
806 v->x_pos = x; |
|
807 v->y_pos = y; |
|
808 v->z_pos = GetSlopeZ(x,y); |
|
809 v->z_height = 6; |
|
810 v->u.rail.track = 0x80; |
|
811 v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; |
|
812 v->spritenum = rvi->image_index; |
|
813 v->cargo_type = rvi->cargo_type; |
|
814 v->cargo_subtype = 0; |
|
815 v->cargo_cap = rvi->capacity; |
|
816 v->max_speed = rvi->max_speed; |
|
817 v->value = value; |
|
818 v->last_station_visited = INVALID_STATION; |
|
819 v->dest_tile = 0; |
|
820 |
|
821 v->engine_type = p1; |
|
822 |
|
823 v->reliability = e->reliability; |
|
824 v->reliability_spd_dec = e->reliability_spd_dec; |
|
825 v->max_age = e->lifelength * 366; |
|
826 |
|
827 v->string_id = STR_SV_TRAIN_NAME; |
|
828 v->u.rail.railtype = e->railtype; |
|
829 _new_vehicle_id = v->index; |
|
830 |
|
831 v->service_interval = _patches.servint_trains; |
|
832 v->date_of_last_service = _date; |
|
833 v->build_year = _cur_year; |
|
834 v->type = VEH_Train; |
|
835 v->cur_image = 0xAC2; |
|
836 v->random_bits = VehicleRandomBits(); |
|
837 |
|
838 v->subtype = 0; |
|
839 SetFrontEngine(v); |
|
840 SetTrainEngine(v); |
|
841 |
|
842 VehiclePositionChanged(v); |
|
843 |
|
844 if (rvi->flags & RVI_MULTIHEAD) { |
|
845 SetMultiheaded(v); |
|
846 AddRearEngineToMultiheadedTrain(vl[0], vl[1], true); |
|
847 /* Now we need to link the front and rear engines together |
|
848 * other_multiheaded_part is the pointer that links to the other half of the engine |
|
849 * vl[0] is the front and vl[1] is the rear |
|
850 */ |
|
851 vl[0]->u.rail.other_multiheaded_part = vl[1]; |
|
852 vl[1]->u.rail.other_multiheaded_part = vl[0]; |
|
853 } else { |
|
854 AddArticulatedParts(vl); |
|
855 } |
|
856 |
|
857 TrainConsistChanged(v); |
|
858 UpdateTrainAcceleration(v); |
|
859 |
|
860 if (!HASBIT(p2, 1)) { // check if the cars should be added to the new vehicle |
|
861 NormalizeTrainVehInDepot(v); |
|
862 } |
|
863 |
|
864 GetPlayer(_current_player)->num_engines[p1]++; |
|
865 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); |
|
866 RebuildVehicleLists(); |
|
867 InvalidateWindow(WC_COMPANY, v->owner); |
|
868 if (IsLocalPlayer()) { |
|
869 InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train); // updates the replace Train window |
|
870 } |
|
871 } |
|
872 } |
|
873 |
|
874 return value; |
|
875 } |
|
876 |
|
877 |
|
878 /* Check if all the wagons of the given train are in a depot, returns the |
|
879 * number of cars (including loco) then. If not it returns -1 */ |
|
880 int CheckTrainInDepot(const Vehicle *v, bool needs_to_be_stopped) |
|
881 { |
|
882 int count; |
|
883 TileIndex tile = v->tile; |
|
884 |
|
885 /* check if stopped in a depot */ |
|
886 if (!IsTileDepotType(tile, TRANSPORT_RAIL) || v->cur_speed != 0) return -1; |
|
887 |
|
888 count = 0; |
|
889 for (; v != NULL; v = v->next) { |
|
890 /* This count is used by the depot code to determine the number of engines |
|
891 * in the consist. Exclude articulated parts so that autoreplacing to |
|
892 * engines with more articulated parts than before works correctly. |
|
893 * |
|
894 * Also skip counting rear ends of multiheaded engines */ |
|
895 if (!IsArticulatedPart(v) && !(!IsTrainEngine(v) && IsMultiheaded(v))) count++; |
|
896 if (v->u.rail.track != 0x80 || v->tile != tile || |
|
897 (IsFrontEngine(v) && needs_to_be_stopped && !(v->vehstatus & VS_STOPPED))) { |
|
898 return -1; |
|
899 } |
|
900 } |
|
901 |
|
902 return count; |
|
903 } |
|
904 |
|
905 /* Used to check if the train is inside the depot and verifying that the VS_STOPPED flag is set */ |
|
906 inline int CheckTrainStoppedInDepot(const Vehicle *v) |
|
907 { |
|
908 return CheckTrainInDepot(v, true); |
|
909 } |
|
910 |
|
911 /* Used to check if the train is inside the depot, but not checking the VS_STOPPED flag */ |
|
912 inline bool CheckTrainIsInsideDepot(const Vehicle *v) |
|
913 { |
|
914 return (CheckTrainInDepot(v, false) > 0); |
|
915 } |
|
916 |
|
917 /** |
|
918 * Unlink a rail wagon from the consist. |
|
919 * @param v Vehicle to remove. |
|
920 * @param first The first vehicle of the consist. |
|
921 * @return The first vehicle of the consist. |
|
922 */ |
|
923 static Vehicle *UnlinkWagon(Vehicle *v, Vehicle *first) |
|
924 { |
|
925 Vehicle *u; |
|
926 |
|
927 // unlinking the first vehicle of the chain? |
|
928 if (v == first) { |
|
929 v = GetNextVehicle(v); |
|
930 if (v == NULL) return NULL; |
|
931 |
|
932 if (IsTrainWagon(v)) SetFreeWagon(v); |
|
933 |
|
934 return v; |
|
935 } |
|
936 |
|
937 for (u = first; GetNextVehicle(u) != v; u = GetNextVehicle(u)) {} |
|
938 GetLastEnginePart(u)->next = GetNextVehicle(v); |
|
939 return first; |
|
940 } |
|
941 |
|
942 static Vehicle *FindGoodVehiclePos(const Vehicle *src) |
|
943 { |
|
944 Vehicle *dst; |
|
945 EngineID eng = src->engine_type; |
|
946 TileIndex tile = src->tile; |
|
947 |
|
948 FOR_ALL_VEHICLES(dst) { |
|
949 if (dst->type == VEH_Train && IsFreeWagon(dst) && dst->tile == tile) { |
|
950 // check so all vehicles in the line have the same engine. |
|
951 Vehicle *v = dst; |
|
952 |
|
953 while (v->engine_type == eng) { |
|
954 v = v->next; |
|
955 if (v == NULL) return dst; |
|
956 } |
|
957 } |
|
958 } |
|
959 |
|
960 return NULL; |
|
961 } |
|
962 |
|
963 /* |
|
964 * add a vehicle v behind vehicle dest |
|
965 * use this function since it sets flags as needed |
|
966 */ |
|
967 static void AddWagonToConsist(Vehicle *v, Vehicle *dest) |
|
968 { |
|
969 UnlinkWagon(v, GetFirstVehicleInChain(v)); |
|
970 if (dest == NULL) return; |
|
971 |
|
972 v->next = dest->next; |
|
973 dest->next = v; |
|
974 ClearFreeWagon(v); |
|
975 ClearFrontEngine(v); |
|
976 } |
|
977 |
|
978 /* |
|
979 * move around on the train so rear engines are placed correctly according to the other engines |
|
980 * always call with the front engine |
|
981 */ |
|
982 static void NormaliseTrainConsist(Vehicle *v) |
|
983 { |
|
984 Vehicle *u; |
|
985 |
|
986 if (IsFreeWagon(v)) return; |
|
987 |
|
988 assert(IsFrontEngine(v)); |
|
989 |
|
990 for (; v != NULL; v = GetNextVehicle(v)) { |
|
991 if (!IsMultiheaded(v) || !IsTrainEngine(v)) continue; |
|
992 |
|
993 /* make sure that there are no free cars before next engine */ |
|
994 for (u = v; u->next != NULL && !IsTrainEngine(u->next); u = u->next); |
|
995 |
|
996 if (u == v->u.rail.other_multiheaded_part) continue; |
|
997 AddWagonToConsist(v->u.rail.other_multiheaded_part, u); |
|
998 } |
|
999 } |
|
1000 |
|
1001 /** Move a rail vehicle around inside the depot. |
|
1002 * @param tile unused |
|
1003 * @param p1 various bitstuffed elements |
|
1004 * - p1 (bit 0 - 15) source vehicle index |
|
1005 * - p1 (bit 16 - 31) what wagon to put the source wagon AFTER, XXX - INVALID_VEHICLE to make a new line |
|
1006 * @param p2 (bit 0) move all vehicles following the source vehicle |
|
1007 */ |
|
1008 int32 CmdMoveRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
1009 { |
|
1010 VehicleID s = GB(p1, 0, 16); |
|
1011 VehicleID d = GB(p1, 16, 16); |
|
1012 Vehicle *src, *dst, *src_head, *dst_head; |
|
1013 |
|
1014 if (!IsValidVehicleID(s)) return CMD_ERROR; |
|
1015 |
|
1016 src = GetVehicle(s); |
|
1017 |
|
1018 if (src->type != VEH_Train) return CMD_ERROR; |
|
1019 |
|
1020 // if nothing is selected as destination, try and find a matching vehicle to drag to. |
|
1021 if (d == INVALID_VEHICLE) { |
|
1022 dst = IsTrainEngine(src) ? NULL : FindGoodVehiclePos(src); |
|
1023 } else { |
|
1024 dst = GetVehicle(d); |
|
1025 } |
|
1026 |
|
1027 // if an articulated part is being handled, deal with its parent vehicle |
|
1028 while (IsArticulatedPart(src)) src = GetPrevVehicleInChain(src); |
|
1029 if (dst != NULL) { |
|
1030 while (IsArticulatedPart(dst)) dst = GetPrevVehicleInChain(dst); |
|
1031 } |
|
1032 |
|
1033 // don't move the same vehicle.. |
|
1034 if (src == dst) return 0; |
|
1035 |
|
1036 /* the player must be the owner */ |
|
1037 if (!CheckOwnership(src->owner) || (dst != NULL && !CheckOwnership(dst->owner))) |
|
1038 return CMD_ERROR; |
|
1039 |
|
1040 /* locate the head of the two chains */ |
|
1041 src_head = GetFirstVehicleInChain(src); |
|
1042 dst_head = NULL; |
|
1043 if (dst != NULL) { |
|
1044 dst_head = GetFirstVehicleInChain(dst); |
|
1045 // Now deal with articulated part of destination wagon |
|
1046 dst = GetLastEnginePart(dst); |
|
1047 } |
|
1048 |
|
1049 if (dst != NULL && IsMultiheaded(dst) && !IsTrainEngine(dst) && IsTrainWagon(src)) { |
|
1050 /* We are moving a wagon to the rear part of a multiheaded engine */ |
|
1051 if (dst->next == NULL) { |
|
1052 /* It's the last one, so we will add the wagon just before the rear engine */ |
|
1053 dst = GetPrevVehicleInChain(dst); |
|
1054 /* Now if the vehicle we want to link to is the vehicle itself, drop out */ |
|
1055 if (dst == src) return CMD_ERROR; |
|
1056 // if dst is NULL, it means that dst got a rear multiheaded engine as first engine. We can't use that |
|
1057 if (dst == NULL) return CMD_ERROR; |
|
1058 } else { |
|
1059 /* there are more units on this train, so we will add the wagon after the next one*/ |
|
1060 dst = dst->next; |
|
1061 } |
|
1062 } |
|
1063 |
|
1064 if (IsTrainEngine(src) && dst_head != NULL) { |
|
1065 /* we need to make sure that we didn't place it between a pair of multiheaded engines */ |
|
1066 Vehicle *u, *engine = NULL; |
|
1067 |
|
1068 for (u = dst_head; u != NULL; u = u->next) { |
|
1069 if (IsTrainEngine(u) && IsMultiheaded(u) && u->u.rail.other_multiheaded_part != NULL) { |
|
1070 engine = u; |
|
1071 } |
|
1072 if (engine != NULL && engine->u.rail.other_multiheaded_part == u) { |
|
1073 engine = NULL; |
|
1074 } |
|
1075 if (u == dst) { |
|
1076 if (engine != NULL) dst = engine->u.rail.other_multiheaded_part; |
|
1077 break; |
|
1078 } |
|
1079 } |
|
1080 } |
|
1081 |
|
1082 if (IsMultiheaded(src) && !IsTrainEngine(src)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR); |
|
1083 |
|
1084 // when moving all wagons, we can't have the same src_head and dst_head |
|
1085 if (HASBIT(p2, 0) && src_head == dst_head) return 0; |
|
1086 |
|
1087 { |
|
1088 int src_len = 0; |
|
1089 int max_len = _patches.mammoth_trains ? 100 : 9; |
|
1090 |
|
1091 // check if all vehicles in the source train are stopped inside a depot. |
|
1092 src_len = CheckTrainStoppedInDepot(src_head); |
|
1093 if (src_len < 0) return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED); |
|
1094 |
|
1095 // check the destination row if the source and destination aren't the same. |
|
1096 if (src_head != dst_head) { |
|
1097 int dst_len = 0; |
|
1098 |
|
1099 if (dst_head != NULL) { |
|
1100 // check if all vehicles in the dest train are stopped. |
|
1101 dst_len = CheckTrainStoppedInDepot(dst_head); |
|
1102 if (dst_len < 0) return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED); |
|
1103 |
|
1104 assert(dst_head->tile == src_head->tile); |
|
1105 } |
|
1106 |
|
1107 // We are moving between rows, so only count the wagons from the source |
|
1108 // row that are being moved. |
|
1109 if (HASBIT(p2, 0)) { |
|
1110 const Vehicle *u; |
|
1111 for (u = src_head; u != src && u != NULL; u = GetNextVehicle(u)) |
|
1112 src_len--; |
|
1113 } else { |
|
1114 // If moving only one vehicle, just count that. |
|
1115 src_len = 1; |
|
1116 } |
|
1117 |
|
1118 if (src_len + dst_len > max_len) { |
|
1119 // Abort if we're adding too many wagons to a train. |
|
1120 if (dst_head != NULL && IsFrontEngine(dst_head)) return_cmd_error(STR_8819_TRAIN_TOO_LONG); |
|
1121 // Abort if we're making a train on a new row. |
|
1122 if (dst_head == NULL && IsTrainEngine(src)) return_cmd_error(STR_8819_TRAIN_TOO_LONG); |
|
1123 } |
|
1124 } else { |
|
1125 // Abort if we're creating a new train on an existing row. |
|
1126 if (src_len > max_len && src == src_head && IsTrainEngine(GetNextVehicle(src_head))) |
|
1127 return_cmd_error(STR_8819_TRAIN_TOO_LONG); |
|
1128 } |
|
1129 } |
|
1130 |
|
1131 // moving a loco to a new line?, then we need to assign a unitnumber. |
|
1132 if (dst == NULL && !IsFrontEngine(src) && IsTrainEngine(src)) { |
|
1133 UnitID unit_num = GetFreeUnitNumber(VEH_Train); |
|
1134 if (unit_num > _patches.max_trains) |
|
1135 return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); |
|
1136 |
|
1137 if (flags & DC_EXEC) src->unitnumber = unit_num; |
|
1138 } |
|
1139 |
|
1140 if (dst_head != NULL) { |
|
1141 /* Check NewGRF Callback 0x1D */ |
|
1142 uint16 callback = GetVehicleCallbackParent(CBID_TRAIN_ALLOW_WAGON_ATTACH, 0, 0, dst_head->engine_type, src, dst_head); |
|
1143 if (callback != CALLBACK_FAILED) { |
|
1144 if (callback == 0xFD) return_cmd_error(STR_INCOMPATIBLE_RAIL_TYPES); |
|
1145 if (callback < 0xFD) { |
|
1146 StringID error = GetGRFStringID(GetEngineGRFID(dst_head->engine_type), 0xD000 + callback); |
|
1147 return_cmd_error(error); |
|
1148 } |
|
1149 } |
|
1150 } |
|
1151 |
|
1152 /* do it? */ |
|
1153 if (flags & DC_EXEC) { |
|
1154 /* clear the ->first cache */ |
|
1155 { |
|
1156 Vehicle *u; |
|
1157 |
|
1158 for (u = src_head; u != NULL; u = u->next) u->first = NULL; |
|
1159 for (u = dst_head; u != NULL; u = u->next) u->first = NULL; |
|
1160 } |
|
1161 |
|
1162 if (HASBIT(p2, 0)) { |
|
1163 // unlink ALL wagons |
|
1164 if (src != src_head) { |
|
1165 Vehicle *v = src_head; |
|
1166 while (GetNextVehicle(v) != src) v = GetNextVehicle(v); |
|
1167 GetLastEnginePart(v)->next = NULL; |
|
1168 } else { |
|
1169 InvalidateWindowData(WC_VEHICLE_DEPOT, src_head->tile); // We removed a line |
|
1170 src_head = NULL; |
|
1171 } |
|
1172 } else { |
|
1173 // if moving within the same chain, dont use dst_head as it may get invalidated |
|
1174 if (src_head == dst_head) dst_head = NULL; |
|
1175 // unlink single wagon from linked list |
|
1176 src_head = UnlinkWagon(src, src_head); |
|
1177 GetLastEnginePart(src)->next = NULL; |
|
1178 } |
|
1179 |
|
1180 if (dst == NULL) { |
|
1181 /* We make a new line in the depot, so we know already that we invalidate the window data */ |
|
1182 InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile); |
|
1183 |
|
1184 // move the train to an empty line. for locomotives, we set the type to TS_Front. for wagons, 4. |
|
1185 if (IsTrainEngine(src)) { |
|
1186 if (!IsFrontEngine(src)) { |
|
1187 // setting the type to 0 also involves setting up the orders field. |
|
1188 SetFrontEngine(src); |
|
1189 assert(src->orders == NULL); |
|
1190 src->num_orders = 0; |
|
1191 } |
|
1192 } else { |
|
1193 SetFreeWagon(src); |
|
1194 } |
|
1195 dst_head = src; |
|
1196 } else { |
|
1197 if (IsFrontEngine(src)) { |
|
1198 // the vehicle was previously a loco. need to free the order list and delete vehicle windows etc. |
|
1199 DeleteWindowById(WC_VEHICLE_VIEW, src->index); |
|
1200 DeleteVehicleOrders(src); |
|
1201 } |
|
1202 |
|
1203 if (IsFrontEngine(src) || IsFreeWagon(src)) { |
|
1204 InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile); |
|
1205 ClearFrontEngine(src); |
|
1206 ClearFreeWagon(src); |
|
1207 src->unitnumber = 0; // doesn't occupy a unitnumber anymore. |
|
1208 } |
|
1209 |
|
1210 // link in the wagon(s) in the chain. |
|
1211 { |
|
1212 Vehicle *v; |
|
1213 |
|
1214 for (v = src; GetNextVehicle(v) != NULL; v = GetNextVehicle(v)); |
|
1215 GetLastEnginePart(v)->next = dst->next; |
|
1216 } |
|
1217 dst->next = src; |
|
1218 } |
|
1219 if (src->u.rail.other_multiheaded_part != NULL) { |
|
1220 if (src->u.rail.other_multiheaded_part == src_head) { |
|
1221 src_head = src_head->next; |
|
1222 } |
|
1223 AddWagonToConsist(src->u.rail.other_multiheaded_part, src); |
|
1224 // previous line set the front engine to the old front. We need to clear that |
|
1225 src->u.rail.other_multiheaded_part->first = NULL; |
|
1226 } |
|
1227 |
|
1228 if (HASBIT(p2, 0) && src_head != NULL && src_head != src) { |
|
1229 /* if we stole a rear multiheaded engine, we better give it back to the front end */ |
|
1230 Vehicle *engine = NULL, *u; |
|
1231 for (u = src_head; u != NULL; u = u->next) { |
|
1232 if (IsMultiheaded(u)) { |
|
1233 if (IsTrainEngine(u)) { |
|
1234 engine = u; |
|
1235 continue; |
|
1236 } |
|
1237 /* we got the rear engine to match with the front one */ |
|
1238 engine = NULL; |
|
1239 } |
|
1240 } |
|
1241 if (engine != NULL && engine->u.rail.other_multiheaded_part != NULL) { |
|
1242 AddWagonToConsist(engine->u.rail.other_multiheaded_part, engine); |
|
1243 // previous line set the front engine to the old front. We need to clear that |
|
1244 engine->u.rail.other_multiheaded_part->first = NULL; |
|
1245 } |
|
1246 } |
|
1247 |
|
1248 /* If there is an engine behind first_engine we moved away, it should become new first_engine |
|
1249 * To do this, CmdMoveRailVehicle must be called once more |
|
1250 * we can't loop forever here because next time we reach this line we will have a front engine */ |
|
1251 if (src_head != NULL && !IsFrontEngine(src_head) && IsTrainEngine(src_head)) { |
|
1252 CmdMoveRailVehicle(0, flags, src_head->index | (INVALID_VEHICLE << 16), 1); |
|
1253 src_head = NULL; // don't do anything more to this train since the new call will do it |
|
1254 } |
|
1255 |
|
1256 if (src_head != NULL) { |
|
1257 NormaliseTrainConsist(src_head); |
|
1258 TrainConsistChanged(src_head); |
|
1259 if (IsFrontEngine(src_head)) { |
|
1260 UpdateTrainAcceleration(src_head); |
|
1261 InvalidateWindow(WC_VEHICLE_DETAILS, src_head->index); |
|
1262 /* Update the refit button and window */ |
|
1263 InvalidateWindow(WC_VEHICLE_REFIT, src_head->index); |
|
1264 InvalidateWindowWidget(WC_VEHICLE_VIEW, src_head->index, 12); |
|
1265 } |
|
1266 /* Update the depot window */ |
|
1267 InvalidateWindow(WC_VEHICLE_DEPOT, src_head->tile); |
|
1268 }; |
|
1269 |
|
1270 if (dst_head != NULL) { |
|
1271 NormaliseTrainConsist(dst_head); |
|
1272 TrainConsistChanged(dst_head); |
|
1273 if (IsFrontEngine(dst_head)) { |
|
1274 UpdateTrainAcceleration(dst_head); |
|
1275 InvalidateWindow(WC_VEHICLE_DETAILS, dst_head->index); |
|
1276 /* Update the refit button and window */ |
|
1277 InvalidateWindowWidget(WC_VEHICLE_VIEW, dst_head->index, 12); |
|
1278 InvalidateWindow(WC_VEHICLE_REFIT, dst_head->index); |
|
1279 } |
|
1280 /* Update the depot window */ |
|
1281 InvalidateWindow(WC_VEHICLE_DEPOT, dst_head->tile); |
|
1282 } |
|
1283 |
|
1284 RebuildVehicleLists(); |
|
1285 } |
|
1286 |
|
1287 return 0; |
|
1288 } |
|
1289 |
|
1290 /** Start/Stop a train. |
|
1291 * @param tile unused |
|
1292 * @param p1 train to start/stop |
|
1293 * @param p2 unused |
|
1294 */ |
|
1295 int32 CmdStartStopTrain(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
1296 { |
|
1297 Vehicle *v; |
|
1298 uint16 callback; |
|
1299 |
|
1300 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
|
1301 |
|
1302 v = GetVehicle(p1); |
|
1303 |
|
1304 if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR; |
|
1305 |
|
1306 /* Check if this train can be started/stopped. The callback will fail or |
|
1307 * return 0xFF if it can. */ |
|
1308 callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v); |
|
1309 if (callback != CALLBACK_FAILED && callback != 0xFF) { |
|
1310 StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback); |
|
1311 return_cmd_error(error); |
|
1312 } |
|
1313 |
|
1314 if (v->vehstatus & VS_STOPPED && v->u.rail.cached_power == 0) return_cmd_error(STR_TRAIN_START_NO_CATENARY); |
|
1315 |
|
1316 if (flags & DC_EXEC) { |
|
1317 if (v->vehstatus & VS_STOPPED && v->u.rail.track == 0x80) { |
|
1318 DeleteVehicleNews(p1, STR_8814_TRAIN_IS_WAITING_IN_DEPOT); |
|
1319 } |
|
1320 |
|
1321 v->u.rail.days_since_order_progr = 0; |
|
1322 v->vehstatus ^= VS_STOPPED; |
|
1323 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
|
1324 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
|
1325 } |
|
1326 return 0; |
|
1327 } |
|
1328 |
|
1329 /** Sell a (single) train wagon/engine. |
|
1330 * @param tile unused |
|
1331 * @param p1 the wagon/engine index |
|
1332 * @param p2 the selling mode |
|
1333 * - p2 = 0: only sell the single dragged wagon/engine (and any belonging rear-engines) |
|
1334 * - p2 = 1: sell the vehicle and all vehicles following it in the chain |
|
1335 if the wagon is dragged, don't delete the possibly belonging rear-engine to some front |
|
1336 * - p2 = 2: when selling attached locos, rearrange all vehicles after it to separate lines; |
|
1337 * all wagons of the same type will go on the same line. Used by the AI currently |
|
1338 */ |
|
1339 int32 CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
1340 { |
|
1341 Vehicle *v, *tmp, *first; |
|
1342 Vehicle *new_f = NULL; |
|
1343 int32 cost = 0; |
|
1344 |
|
1345 if (!IsValidVehicleID(p1) || p2 > 2) return CMD_ERROR; |
|
1346 |
|
1347 v = GetVehicle(p1); |
|
1348 |
|
1349 if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR; |
|
1350 |
|
1351 SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
1352 |
|
1353 while (IsArticulatedPart(v)) v = GetPrevVehicleInChain(v); |
|
1354 first = GetFirstVehicleInChain(v); |
|
1355 |
|
1356 // make sure the vehicle is stopped in the depot |
|
1357 if (CheckTrainStoppedInDepot(first) < 0) { |
|
1358 return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED); |
|
1359 } |
|
1360 |
|
1361 if (IsMultiheaded(v) && !IsTrainEngine(v)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR); |
|
1362 |
|
1363 if (flags & DC_EXEC) { |
|
1364 if (v == first && IsFrontEngine(first)) { |
|
1365 DeleteWindowById(WC_VEHICLE_VIEW, first->index); |
|
1366 } |
|
1367 if (IsLocalPlayer() && (p1 == 1 || !(RailVehInfo(v->engine_type)->flags & RVI_WAGON))) { |
|
1368 InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train); |
|
1369 } |
|
1370 InvalidateWindow(WC_VEHICLE_DEPOT, first->tile); |
|
1371 RebuildVehicleLists(); |
|
1372 } |
|
1373 |
|
1374 switch (p2) { |
|
1375 case 0: case 2: { /* Delete given wagon */ |
|
1376 bool switch_engine = false; // update second wagon to engine? |
|
1377 byte ori_subtype = v->subtype; // backup subtype of deleted wagon in case DeleteVehicle() changes |
|
1378 |
|
1379 /* 1. Delete the engine, if it is dualheaded also delete the matching |
|
1380 * rear engine of the loco (from the point of deletion onwards) */ |
|
1381 Vehicle *rear = (IsMultiheaded(v) && |
|
1382 IsTrainEngine(v)) ? v->u.rail.other_multiheaded_part : NULL; |
|
1383 |
|
1384 if (rear != NULL) { |
|
1385 cost -= rear->value; |
|
1386 if (flags & DC_EXEC) { |
|
1387 UnlinkWagon(rear, first); |
|
1388 DeleteDepotHighlightOfVehicle(rear); |
|
1389 DeleteVehicle(rear); |
|
1390 } |
|
1391 } |
|
1392 |
|
1393 /* 2. We are selling the first engine, some special action might be required |
|
1394 * here, so take attention */ |
|
1395 if ((flags & DC_EXEC) && v == first) { |
|
1396 new_f = GetNextVehicle(first); |
|
1397 |
|
1398 /* 2.1 If the first wagon is sold, update the first-> pointers to NULL */ |
|
1399 for (tmp = first; tmp != NULL; tmp = tmp->next) tmp->first = NULL; |
|
1400 |
|
1401 /* 2.2 If there are wagons present after the deleted front engine, check |
|
1402 * if the second wagon (which will be first) is an engine. If it is one, |
|
1403 * promote it as a new train, retaining the unitnumber, orders */ |
|
1404 if (new_f != NULL) { |
|
1405 if (IsTrainEngine(new_f)) { |
|
1406 switch_engine = true; |
|
1407 /* Copy important data from the front engine */ |
|
1408 new_f->unitnumber = first->unitnumber; |
|
1409 new_f->current_order = first->current_order; |
|
1410 new_f->cur_order_index = first->cur_order_index; |
|
1411 new_f->orders = first->orders; |
|
1412 if (first->prev_shared != NULL) { |
|
1413 first->prev_shared->next_shared = new_f; |
|
1414 new_f->prev_shared = first->prev_shared; |
|
1415 } |
|
1416 |
|
1417 if (first->next_shared != NULL) { |
|
1418 first->next_shared->prev_shared = new_f; |
|
1419 new_f->next_shared = first->next_shared; |
|
1420 } |
|
1421 |
|
1422 new_f->num_orders = first->num_orders; |
|
1423 first->orders = NULL; // XXX - to not to delete the orders */ |
|
1424 if (IsLocalPlayer()) ShowTrainViewWindow(new_f); |
|
1425 } |
|
1426 } |
|
1427 } |
|
1428 |
|
1429 /* 3. Delete the requested wagon */ |
|
1430 cost -= v->value; |
|
1431 if (flags & DC_EXEC) { |
|
1432 first = UnlinkWagon(v, first); |
|
1433 DeleteDepotHighlightOfVehicle(v); |
|
1434 DeleteVehicle(v); |
|
1435 |
|
1436 /* 4 If the second wagon was an engine, update it to front_engine |
|
1437 * which UnlinkWagon() has changed to TS_Free_Car */ |
|
1438 if (switch_engine) SetFrontEngine(first); |
|
1439 |
|
1440 /* 5. If the train still exists, update its acceleration, window, etc. */ |
|
1441 if (first != NULL) { |
|
1442 NormaliseTrainConsist(first); |
|
1443 TrainConsistChanged(first); |
|
1444 if (IsFrontEngine(first)) { |
|
1445 InvalidateWindow(WC_VEHICLE_DETAILS, first->index); |
|
1446 InvalidateWindow(WC_VEHICLE_REFIT, first->index); |
|
1447 UpdateTrainAcceleration(first); |
|
1448 } |
|
1449 } |
|
1450 |
|
1451 |
|
1452 /* (6.) Borked AI. If it sells an engine it expects all wagons lined |
|
1453 * up on a new line to be added to the newly built loco. Replace it is. |
|
1454 * Totally braindead cause building a new engine adds all loco-less |
|
1455 * engines to its train anyways */ |
|
1456 if (p2 == 2 && HASBIT(ori_subtype, Train_Front)) { |
|
1457 for (v = first; v != NULL; v = tmp) { |
|
1458 tmp = GetNextVehicle(v); |
|
1459 DoCommand(v->tile, v->index | INVALID_VEHICLE << 16, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
1460 } |
|
1461 } |
|
1462 } |
|
1463 } break; |
|
1464 case 1: { /* Delete wagon and all wagons after it given certain criteria */ |
|
1465 /* Start deleting every vehicle after the selected one |
|
1466 * If we encounter a matching rear-engine to a front-engine |
|
1467 * earlier in the chain (before deletion), leave it alone */ |
|
1468 for (; v != NULL; v = tmp) { |
|
1469 tmp = GetNextVehicle(v); |
|
1470 |
|
1471 if (IsMultiheaded(v)) { |
|
1472 if (IsTrainEngine(v)) { |
|
1473 /* We got a front engine of a multiheaded set. Now we will sell the rear end too */ |
|
1474 Vehicle *rear = v->u.rail.other_multiheaded_part; |
|
1475 |
|
1476 if (rear != NULL) { |
|
1477 cost -= rear->value; |
|
1478 if (flags & DC_EXEC) { |
|
1479 first = UnlinkWagon(rear, first); |
|
1480 DeleteDepotHighlightOfVehicle(rear); |
|
1481 DeleteVehicle(rear); |
|
1482 } |
|
1483 } |
|
1484 } else if (v->u.rail.other_multiheaded_part != NULL) { |
|
1485 /* The front to this engine is earlier in this train. Do nothing */ |
|
1486 continue; |
|
1487 } |
|
1488 } |
|
1489 |
|
1490 cost -= v->value; |
|
1491 if (flags & DC_EXEC) { |
|
1492 first = UnlinkWagon(v, first); |
|
1493 DeleteDepotHighlightOfVehicle(v); |
|
1494 DeleteVehicle(v); |
|
1495 } |
|
1496 } |
|
1497 |
|
1498 /* 3. If it is still a valid train after selling, update its acceleration and cached values */ |
|
1499 if (flags & DC_EXEC && first != NULL) { |
|
1500 NormaliseTrainConsist(first); |
|
1501 TrainConsistChanged(first); |
|
1502 if (IsFrontEngine(first)) UpdateTrainAcceleration(first); |
|
1503 InvalidateWindow(WC_VEHICLE_DETAILS, first->index); |
|
1504 InvalidateWindow(WC_VEHICLE_REFIT, first->index); |
|
1505 } |
|
1506 } break; |
|
1507 } |
|
1508 return cost; |
|
1509 } |
|
1510 |
|
1511 static void UpdateTrainDeltaXY(Vehicle *v, Direction direction) |
|
1512 { |
|
1513 #define MKIT(a,b,c,d) ((a&0xFF)<<24) | ((b&0xFF)<<16) | ((c&0xFF)<<8) | ((d&0xFF)<<0) |
|
1514 static const uint32 _delta_xy_table[8] = { |
|
1515 MKIT(3, 3, -1, -1), |
|
1516 MKIT(3, 7, -1, -3), |
|
1517 MKIT(3, 3, -1, -1), |
|
1518 MKIT(7, 3, -3, -1), |
|
1519 MKIT(3, 3, -1, -1), |
|
1520 MKIT(3, 7, -1, -3), |
|
1521 MKIT(3, 3, -1, -1), |
|
1522 MKIT(7, 3, -3, -1), |
|
1523 }; |
|
1524 #undef MKIT |
|
1525 |
|
1526 uint32 x = _delta_xy_table[direction]; |
|
1527 |
|
1528 v->x_offs = GB(x, 0, 8); |
|
1529 v->y_offs = GB(x, 8, 8); |
|
1530 v->sprite_width = GB(x, 16, 8); |
|
1531 v->sprite_height = GB(x, 24, 8); |
|
1532 } |
|
1533 |
|
1534 static void UpdateVarsAfterSwap(Vehicle *v) |
|
1535 { |
|
1536 UpdateTrainDeltaXY(v, v->direction); |
|
1537 v->cur_image = GetTrainImage(v, v->direction); |
|
1538 BeginVehicleMove(v); |
|
1539 VehiclePositionChanged(v); |
|
1540 EndVehicleMove(v); |
|
1541 } |
|
1542 |
|
1543 static void SetLastSpeed(Vehicle* v, int spd) |
|
1544 { |
|
1545 int old = v->u.rail.last_speed; |
|
1546 if (spd != old) { |
|
1547 v->u.rail.last_speed = spd; |
|
1548 if (_patches.vehicle_speed || (old == 0) != (spd == 0)) |
|
1549 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
|
1550 } |
|
1551 } |
|
1552 |
|
1553 static void SwapTrainFlags(byte *swap_flag1, byte *swap_flag2) |
|
1554 { |
|
1555 byte flag1, flag2; |
|
1556 |
|
1557 flag1 = *swap_flag1; |
|
1558 flag2 = *swap_flag2; |
|
1559 |
|
1560 /* Clear the flags */ |
|
1561 CLRBIT(*swap_flag1, VRF_GOINGUP); |
|
1562 CLRBIT(*swap_flag1, VRF_GOINGDOWN); |
|
1563 CLRBIT(*swap_flag2, VRF_GOINGUP); |
|
1564 CLRBIT(*swap_flag2, VRF_GOINGDOWN); |
|
1565 |
|
1566 /* Reverse the rail-flags (if needed) */ |
|
1567 if (HASBIT(flag1, VRF_GOINGUP)) { |
|
1568 SETBIT(*swap_flag2, VRF_GOINGDOWN); |
|
1569 } else if (HASBIT(flag1, VRF_GOINGDOWN)) { |
|
1570 SETBIT(*swap_flag2, VRF_GOINGUP); |
|
1571 } |
|
1572 if (HASBIT(flag2, VRF_GOINGUP)) { |
|
1573 SETBIT(*swap_flag1, VRF_GOINGDOWN); |
|
1574 } else if (HASBIT(flag2, VRF_GOINGDOWN)) { |
|
1575 SETBIT(*swap_flag1, VRF_GOINGUP); |
|
1576 } |
|
1577 } |
|
1578 |
|
1579 static void ReverseTrainSwapVeh(Vehicle *v, int l, int r) |
|
1580 { |
|
1581 Vehicle *a, *b; |
|
1582 |
|
1583 /* locate vehicles to swap */ |
|
1584 for (a = v; l != 0; l--) a = a->next; |
|
1585 for (b = v; r != 0; r--) b = b->next; |
|
1586 |
|
1587 if (a != b) { |
|
1588 /* swap the hidden bits */ |
|
1589 { |
|
1590 uint16 tmp = (a->vehstatus & ~VS_HIDDEN) | (b->vehstatus&VS_HIDDEN); |
|
1591 b->vehstatus = (b->vehstatus & ~VS_HIDDEN) | (a->vehstatus&VS_HIDDEN); |
|
1592 a->vehstatus = tmp; |
|
1593 } |
|
1594 |
|
1595 /* swap variables */ |
|
1596 swap_byte(&a->u.rail.track, &b->u.rail.track); |
|
1597 swap_byte(&a->direction, &b->direction); |
|
1598 |
|
1599 /* toggle direction */ |
|
1600 if (!(a->u.rail.track & 0x80)) a->direction = ReverseDir(a->direction); |
|
1601 if (!(b->u.rail.track & 0x80)) b->direction = ReverseDir(b->direction); |
|
1602 |
|
1603 /* swap more variables */ |
|
1604 swap_int32(&a->x_pos, &b->x_pos); |
|
1605 swap_int32(&a->y_pos, &b->y_pos); |
|
1606 swap_tile(&a->tile, &b->tile); |
|
1607 swap_byte(&a->z_pos, &b->z_pos); |
|
1608 |
|
1609 SwapTrainFlags(&a->u.rail.flags, &b->u.rail.flags); |
|
1610 |
|
1611 /* update other vars */ |
|
1612 UpdateVarsAfterSwap(a); |
|
1613 UpdateVarsAfterSwap(b); |
|
1614 |
|
1615 /* call the proper EnterTile function unless we are in a wormhole */ |
|
1616 if (!(a->u.rail.track & 0x40)) VehicleEnterTile(a, a->tile, a->x_pos, a->y_pos); |
|
1617 if (!(b->u.rail.track & 0x40)) VehicleEnterTile(b, b->tile, b->x_pos, b->y_pos); |
|
1618 } else { |
|
1619 if (!(a->u.rail.track & 0x80)) a->direction = ReverseDir(a->direction); |
|
1620 UpdateVarsAfterSwap(a); |
|
1621 |
|
1622 if (!(a->u.rail.track & 0x40)) VehicleEnterTile(a, a->tile, a->x_pos, a->y_pos); |
|
1623 } |
|
1624 |
|
1625 /* Update train's power incase tiles were different rail type */ |
|
1626 TrainPowerChanged(v); |
|
1627 } |
|
1628 |
|
1629 /* Check if the vehicle is a train and is on the tile we are testing */ |
|
1630 static void *TestTrainOnCrossing(Vehicle *v, void *data) |
|
1631 { |
|
1632 if (v->tile != *(const TileIndex*)data || v->type != VEH_Train) return NULL; |
|
1633 return v; |
|
1634 } |
|
1635 |
|
1636 static void DisableTrainCrossing(TileIndex tile) |
|
1637 { |
|
1638 if (IsLevelCrossingTile(tile) && |
|
1639 VehicleFromPos(tile, &tile, TestTrainOnCrossing) == NULL && // empty? |
|
1640 IsCrossingBarred(tile)) { |
|
1641 UnbarCrossing(tile); |
|
1642 MarkTileDirtyByTile(tile); |
|
1643 } |
|
1644 } |
|
1645 |
|
1646 /** |
|
1647 * Advances wagons for train reversing, needed for variable length wagons. |
|
1648 * Needs to be called once before the train is reversed, and once after it. |
|
1649 * @param v First vehicle in chain |
|
1650 * @param before Set to true for the call before reversing, false otherwise |
|
1651 */ |
|
1652 static void AdvanceWagons(Vehicle *v, bool before) |
|
1653 { |
|
1654 Vehicle* base; |
|
1655 Vehicle* first; |
|
1656 int length; |
|
1657 |
|
1658 base = v; |
|
1659 first = base->next; |
|
1660 length = CountVehiclesInChain(v); |
|
1661 |
|
1662 while (length > 2) { |
|
1663 Vehicle* last; |
|
1664 int differential; |
|
1665 int i; |
|
1666 |
|
1667 // find pairwise matching wagon |
|
1668 // start<>end, start+1<>end-1, ... |
|
1669 last = first; |
|
1670 for (i = length - 3; i > 0; i--) last = last->next; |
|
1671 |
|
1672 differential = last->u.rail.cached_veh_length - base->u.rail.cached_veh_length; |
|
1673 if (before) differential *= -1; |
|
1674 |
|
1675 if (differential > 0) { |
|
1676 Vehicle* tempnext; |
|
1677 |
|
1678 // disconnect last car to make sure only this subset moves |
|
1679 tempnext = last->next; |
|
1680 last->next = NULL; |
|
1681 |
|
1682 for (i = 0; i < differential; i++) TrainController(first, false); |
|
1683 |
|
1684 last->next = tempnext; |
|
1685 } |
|
1686 |
|
1687 base = first; |
|
1688 first = first->next; |
|
1689 length -= 2; |
|
1690 } |
|
1691 } |
|
1692 |
|
1693 |
|
1694 static void ReverseTrainDirection(Vehicle *v) |
|
1695 { |
|
1696 int l = 0, r = -1; |
|
1697 Vehicle *u; |
|
1698 |
|
1699 if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) { |
|
1700 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); |
|
1701 } |
|
1702 |
|
1703 /* Check if we were approaching a rail/road-crossing */ |
|
1704 { |
|
1705 TileIndex tile = v->tile; |
|
1706 DiagDirection dir = DirToDiagDir(v->direction); |
|
1707 |
|
1708 /* Determine the diagonal direction in which we will exit this tile */ |
|
1709 if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[dir]) { |
|
1710 dir = ChangeDiagDir(dir, DIAGDIRDIFF_90LEFT); |
|
1711 } |
|
1712 /* Calculate next tile */ |
|
1713 tile += TileOffsByDiagDir(dir); |
|
1714 |
|
1715 /* Check if the train left a rail/road-crossing */ |
|
1716 DisableTrainCrossing(tile); |
|
1717 } |
|
1718 |
|
1719 // count number of vehicles |
|
1720 u = v; |
|
1721 do r++; while ( (u = u->next) != NULL ); |
|
1722 |
|
1723 AdvanceWagons(v, true); |
|
1724 |
|
1725 /* swap start<>end, start+1<>end-1, ... */ |
|
1726 do { |
|
1727 ReverseTrainSwapVeh(v, l++, r--); |
|
1728 } while (l <= r); |
|
1729 |
|
1730 AdvanceWagons(v, false); |
|
1731 |
|
1732 if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) { |
|
1733 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); |
|
1734 } |
|
1735 |
|
1736 CLRBIT(v->u.rail.flags, VRF_REVERSING); |
|
1737 } |
|
1738 |
|
1739 /** Reverse train. |
|
1740 * @param tile unused |
|
1741 * @param p1 train to reverse |
|
1742 * @param p2 if true, reverse a unit in a train (needs to be in a depot) |
|
1743 */ |
|
1744 int32 CmdReverseTrainDirection(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
1745 { |
|
1746 Vehicle *v; |
|
1747 |
|
1748 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
|
1749 |
|
1750 v = GetVehicle(p1); |
|
1751 |
|
1752 if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR; |
|
1753 |
|
1754 if (p2) { |
|
1755 // turn a single unit around |
|
1756 Vehicle *front; |
|
1757 |
|
1758 if (IsMultiheaded(v) || HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_ARTIC_ENGINE)) { |
|
1759 return_cmd_error(STR_ONLY_TURN_SINGLE_UNIT); |
|
1760 } |
|
1761 |
|
1762 front = GetFirstVehicleInChain(v); |
|
1763 // make sure the vehicle is stopped in the depot |
|
1764 if (CheckTrainStoppedInDepot(front) < 0) { |
|
1765 return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED); |
|
1766 } |
|
1767 |
|
1768 if (flags & DC_EXEC) { |
|
1769 TOGGLEBIT(v->u.rail.flags, VRF_REVERSE_DIRECTION); |
|
1770 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
|
1771 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
1772 } |
|
1773 } else { |
|
1774 //turn the whole train around |
|
1775 if (v->u.rail.crash_anim_pos != 0 || v->breakdown_ctr != 0) return CMD_ERROR; |
|
1776 |
|
1777 if (flags & DC_EXEC) { |
|
1778 if (_patches.realistic_acceleration && v->cur_speed != 0) { |
|
1779 TOGGLEBIT(v->u.rail.flags, VRF_REVERSING); |
|
1780 } else { |
|
1781 v->cur_speed = 0; |
|
1782 SetLastSpeed(v, 0); |
|
1783 ReverseTrainDirection(v); |
|
1784 } |
|
1785 } |
|
1786 } |
|
1787 return 0; |
|
1788 } |
|
1789 |
|
1790 /** Force a train through a red signal |
|
1791 * @param tile unused |
|
1792 * @param p1 train to ignore the red signal |
|
1793 * @param p2 unused |
|
1794 */ |
|
1795 int32 CmdForceTrainProceed(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
1796 { |
|
1797 Vehicle *v; |
|
1798 |
|
1799 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
|
1800 |
|
1801 v = GetVehicle(p1); |
|
1802 |
|
1803 if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR; |
|
1804 |
|
1805 if (flags & DC_EXEC) v->u.rail.force_proceed = 0x50; |
|
1806 |
|
1807 return 0; |
|
1808 } |
|
1809 |
|
1810 /** Refits a train to the specified cargo type. |
|
1811 * @param tile unused |
|
1812 * @param p1 vehicle ID of the train to refit |
|
1813 * param p2 various bitstuffed elements |
|
1814 * - p2 = (bit 0-7) - the new cargo type to refit to |
|
1815 * - p2 = (bit 8-15) - the new cargo subtype to refit to |
|
1816 */ |
|
1817 int32 CmdRefitRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
1818 { |
|
1819 CargoID new_cid = GB(p2, 0, 8); |
|
1820 byte new_subtype = GB(p2, 8, 8); |
|
1821 Vehicle *v; |
|
1822 int32 cost; |
|
1823 uint num; |
|
1824 |
|
1825 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
|
1826 |
|
1827 v = GetVehicle(p1); |
|
1828 |
|
1829 if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR; |
|
1830 if (CheckTrainStoppedInDepot(v) < 0) return_cmd_error(STR_TRAIN_MUST_BE_STOPPED); |
|
1831 |
|
1832 /* Check cargo */ |
|
1833 if (new_cid > NUM_CARGO) return CMD_ERROR; |
|
1834 |
|
1835 SET_EXPENSES_TYPE(EXPENSES_TRAIN_RUN); |
|
1836 |
|
1837 cost = 0; |
|
1838 num = 0; |
|
1839 |
|
1840 do { |
|
1841 /* XXX: We also refit all the attached wagons en-masse if they |
|
1842 * can be refitted. This is how TTDPatch does it. TODO: Have |
|
1843 * some nice [Refit] button near each wagon. --pasky */ |
|
1844 if (!CanRefitTo(v->engine_type, new_cid)) continue; |
|
1845 |
|
1846 if (v->cargo_cap != 0) { |
|
1847 const RailVehicleInfo *rvi = RailVehInfo(v->engine_type); |
|
1848 uint16 amount = CALLBACK_FAILED; |
|
1849 |
|
1850 if (HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_REFIT_CAPACITY)) { |
|
1851 /* Back up the vehicle's cargo type */ |
|
1852 CargoID temp_cid = v->cargo_type; |
|
1853 byte temp_subtype = v->cargo_subtype; |
|
1854 v->cargo_type = new_cid; |
|
1855 v->cargo_subtype = new_subtype; |
|
1856 /* Check the refit capacity callback */ |
|
1857 amount = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v); |
|
1858 /* Restore the original cargo type */ |
|
1859 v->cargo_type = temp_cid; |
|
1860 v->cargo_subtype = temp_subtype; |
|
1861 } |
|
1862 |
|
1863 if (amount == CALLBACK_FAILED) { // callback failed or not used, use default |
|
1864 CargoID old_cid = rvi->cargo_type; |
|
1865 /* normally, the capacity depends on the cargo type, a rail vehicle can |
|
1866 * carry twice as much mail/goods as normal cargo, and four times as |
|
1867 * many passengers |
|
1868 */ |
|
1869 amount = rvi->capacity; |
|
1870 switch (old_cid) { |
|
1871 case CT_PASSENGERS: break; |
|
1872 case CT_MAIL: |
|
1873 case CT_GOODS: amount *= 2; break; |
|
1874 default: amount *= 4; break; |
|
1875 } |
|
1876 switch (new_cid) { |
|
1877 case CT_PASSENGERS: break; |
|
1878 case CT_MAIL: |
|
1879 case CT_GOODS: amount /= 2; break; |
|
1880 default: amount /= 4; break; |
|
1881 } |
|
1882 }; |
|
1883 |
|
1884 if (amount != 0) { |
|
1885 if (new_cid != v->cargo_type) { |
|
1886 cost += GetRefitCost(v->engine_type); |
|
1887 } |
|
1888 |
|
1889 num += amount; |
|
1890 if (flags & DC_EXEC) { |
|
1891 v->cargo_count = (v->cargo_type == new_cid) ? min(amount, v->cargo_count) : 0; |
|
1892 v->cargo_type = new_cid; |
|
1893 v->cargo_cap = amount; |
|
1894 v->cargo_subtype = new_subtype; |
|
1895 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
1896 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
|
1897 RebuildVehicleLists(); |
|
1898 } |
|
1899 } |
|
1900 } |
|
1901 } while ((v = v->next) != NULL); |
|
1902 |
|
1903 _returned_refit_capacity = num; |
|
1904 |
|
1905 /* Update the train's cached variables */ |
|
1906 if (flags & DC_EXEC) TrainConsistChanged(GetFirstVehicleInChain(GetVehicle(p1))); |
|
1907 |
|
1908 return cost; |
|
1909 } |
|
1910 |
|
1911 typedef struct TrainFindDepotData { |
|
1912 uint best_length; |
|
1913 TileIndex tile; |
|
1914 PlayerID owner; |
|
1915 /** |
|
1916 * true if reversing is necessary for the train to get to this depot |
|
1917 * This value is unused when new depot finding and NPF are both disabled |
|
1918 */ |
|
1919 bool reverse; |
|
1920 } TrainFindDepotData; |
|
1921 |
|
1922 static bool NtpCallbFindDepot(TileIndex tile, TrainFindDepotData *tfdd, int track, uint length) |
|
1923 { |
|
1924 if (IsTileType(tile, MP_RAILWAY) && |
|
1925 IsTileOwner(tile, tfdd->owner) && |
|
1926 IsRailDepot(tile)) { |
|
1927 /* approximate number of tiles by dividing by DIAG_FACTOR */ |
|
1928 tfdd->best_length = length / DIAG_FACTOR; |
|
1929 tfdd->tile = tile; |
|
1930 return true; |
|
1931 } |
|
1932 |
|
1933 return false; |
|
1934 } |
|
1935 |
|
1936 // returns the tile of a depot to goto to. The given vehicle must not be |
|
1937 // crashed! |
|
1938 static TrainFindDepotData FindClosestTrainDepot(Vehicle *v, int max_distance) |
|
1939 { |
|
1940 TrainFindDepotData tfdd; |
|
1941 TileIndex tile = v->tile; |
|
1942 |
|
1943 assert(!(v->vehstatus & VS_CRASHED)); |
|
1944 |
|
1945 tfdd.owner = v->owner; |
|
1946 tfdd.best_length = (uint)-1; |
|
1947 tfdd.reverse = false; |
|
1948 |
|
1949 if (IsTileDepotType(tile, TRANSPORT_RAIL)){ |
|
1950 tfdd.tile = tile; |
|
1951 tfdd.best_length = 0; |
|
1952 return tfdd; |
|
1953 } |
|
1954 |
|
1955 if (_patches.yapf.rail_use_yapf) { |
|
1956 bool found = YapfFindNearestRailDepotTwoWay(v, max_distance, NPF_INFINITE_PENALTY, &tfdd.tile, &tfdd.reverse); |
|
1957 tfdd.best_length = found ? max_distance / 2 : -1; // some fake distance or NOT_FOUND |
|
1958 } else if (_patches.new_pathfinding_all) { |
|
1959 NPFFoundTargetData ftd; |
|
1960 Vehicle* last = GetLastVehicleInChain(v); |
|
1961 Trackdir trackdir = GetVehicleTrackdir(v); |
|
1962 Trackdir trackdir_rev = ReverseTrackdir(GetVehicleTrackdir(last)); |
|
1963 |
|
1964 assert (trackdir != INVALID_TRACKDIR); |
|
1965 ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, last->tile, trackdir_rev, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes, NPF_INFINITE_PENALTY); |
|
1966 if (ftd.best_bird_dist == 0) { |
|
1967 /* Found target */ |
|
1968 tfdd.tile = ftd.node.tile; |
|
1969 /* Our caller expects a number of tiles, so we just approximate that |
|
1970 * number by this. It might not be completely what we want, but it will |
|
1971 * work for now :-) We can possibly change this when the old pathfinder |
|
1972 * is removed. */ |
|
1973 tfdd.best_length = ftd.best_path_dist / NPF_TILE_LENGTH; |
|
1974 if (NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE)) tfdd.reverse = true; |
|
1975 } |
|
1976 } else { |
|
1977 // search in the forward direction first. |
|
1978 DiagDirection i; |
|
1979 |
|
1980 i = DirToDiagDir(v->direction); |
|
1981 if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[i]) { |
|
1982 i = ChangeDiagDir(i, DIAGDIRDIFF_90LEFT); |
|
1983 } |
|
1984 NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd); |
|
1985 if (tfdd.best_length == (uint)-1){ |
|
1986 tfdd.reverse = true; |
|
1987 // search in backwards direction |
|
1988 i = ReverseDiagDir(DirToDiagDir(v->direction)); |
|
1989 if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[i]) { |
|
1990 i = ChangeDiagDir(i, DIAGDIRDIFF_90LEFT); |
|
1991 } |
|
1992 NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd); |
|
1993 } |
|
1994 } |
|
1995 |
|
1996 return tfdd; |
|
1997 } |
|
1998 |
|
1999 /** Send a train to a depot |
|
2000 * @param tile unused |
|
2001 * @param p1 train to send to the depot |
|
2002 * @param p2 various bitmasked elements |
|
2003 * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h) |
|
2004 * - p2 bit 8-10 - VLW flag (for mass goto depot) |
|
2005 */ |
|
2006 int32 CmdSendTrainToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
2007 { |
|
2008 Vehicle *v; |
|
2009 TrainFindDepotData tfdd; |
|
2010 |
|
2011 if (p2 & DEPOT_MASS_SEND) { |
|
2012 /* Mass goto depot requested */ |
|
2013 if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR; |
|
2014 return SendAllVehiclesToDepot(VEH_Train, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1); |
|
2015 } |
|
2016 |
|
2017 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
|
2018 |
|
2019 v = GetVehicle(p1); |
|
2020 |
|
2021 if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR; |
|
2022 |
|
2023 if (v->vehstatus & VS_CRASHED) return CMD_ERROR; |
|
2024 |
|
2025 if (v->current_order.type == OT_GOTO_DEPOT) { |
|
2026 if (!!(p2 & DEPOT_SERVICE) == HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT)) { |
|
2027 /* We called with a different DEPOT_SERVICE setting. |
|
2028 * Now we change the setting to apply the new one and let the vehicle head for the same depot. |
|
2029 * Note: the if is (true for requesting service == true for ordered to stop in depot) */ |
|
2030 if (flags & DC_EXEC) { |
|
2031 TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT); |
|
2032 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
|
2033 } |
|
2034 return 0; |
|
2035 } |
|
2036 |
|
2037 if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders |
|
2038 if (flags & DC_EXEC) { |
|
2039 if (HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) { |
|
2040 v->u.rail.days_since_order_progr = 0; |
|
2041 v->cur_order_index++; |
|
2042 } |
|
2043 |
|
2044 v->current_order.type = OT_DUMMY; |
|
2045 v->current_order.flags = 0; |
|
2046 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
|
2047 } |
|
2048 return 0; |
|
2049 } |
|
2050 |
|
2051 /* check if at a standstill (not stopped only) in a depot |
|
2052 * the check is down here to make it possible to alter stop/service for trains entering the depot */ |
|
2053 if (IsTileDepotType(v->tile, TRANSPORT_RAIL) && v->cur_speed == 0) return CMD_ERROR; |
|
2054 |
|
2055 tfdd = FindClosestTrainDepot(v, 0); |
|
2056 if (tfdd.best_length == (uint)-1) return_cmd_error(STR_883A_UNABLE_TO_FIND_ROUTE_TO); |
|
2057 |
|
2058 if (flags & DC_EXEC) { |
|
2059 v->dest_tile = tfdd.tile; |
|
2060 v->current_order.type = OT_GOTO_DEPOT; |
|
2061 v->current_order.flags = OF_NON_STOP; |
|
2062 if (!(p2 & DEPOT_SERVICE)) SETBIT(v->current_order.flags, OFB_HALT_IN_DEPOT); |
|
2063 v->current_order.dest = GetDepotByTile(tfdd.tile)->index; |
|
2064 v->current_order.refit_cargo = CT_INVALID; |
|
2065 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
|
2066 /* If there is no depot in front, reverse automatically */ |
|
2067 if (tfdd.reverse) |
|
2068 DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION); |
|
2069 } |
|
2070 |
|
2071 return 0; |
|
2072 } |
|
2073 |
|
2074 |
|
2075 void OnTick_Train(void) |
|
2076 { |
|
2077 _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1); |
|
2078 } |
|
2079 |
|
2080 static const int8 _vehicle_smoke_pos[8] = { |
|
2081 1, 1, 1, 0, -1, -1, -1, 0 |
|
2082 }; |
|
2083 |
|
2084 static void HandleLocomotiveSmokeCloud(const Vehicle* v) |
|
2085 { |
|
2086 const Vehicle* u; |
|
2087 bool sound = false; |
|
2088 |
|
2089 if (v->vehstatus & VS_TRAIN_SLOWING || v->load_unload_time_rem != 0 || v->cur_speed < 2) |
|
2090 return; |
|
2091 |
|
2092 u = v; |
|
2093 |
|
2094 do { |
|
2095 EngineID engtype = v->engine_type; |
|
2096 int effect_offset = GB(v->u.rail.cached_vis_effect, 0, 4) - 8; |
|
2097 byte effect_type = GB(v->u.rail.cached_vis_effect, 4, 2); |
|
2098 bool disable_effect = HASBIT(v->u.rail.cached_vis_effect, 6); |
|
2099 int x, y; |
|
2100 |
|
2101 // no smoke? |
|
2102 if ((RailVehInfo(engtype)->flags & RVI_WAGON && effect_type == 0) || |
|
2103 disable_effect || |
|
2104 GetEngine(engtype)->railtype > RAILTYPE_ELECTRIC || |
|
2105 v->vehstatus & VS_HIDDEN) { |
|
2106 continue; |
|
2107 } |
|
2108 |
|
2109 // No smoke in depots or tunnels |
|
2110 if (IsTileDepotType(v->tile, TRANSPORT_RAIL) || IsTunnelTile(v->tile)) continue; |
|
2111 |
|
2112 // No sparks for electric vehicles on nonelectrified tracks |
|
2113 if (!HasPowerOnRail(v->u.rail.railtype, GetTileRailType(v->tile, GetVehicleTrackdir(v)))) continue; |
|
2114 |
|
2115 if (effect_type == 0) { |
|
2116 // Use default effect type for engine class. |
|
2117 effect_type = RailVehInfo(engtype)->engclass; |
|
2118 } else { |
|
2119 effect_type--; |
|
2120 } |
|
2121 |
|
2122 x = _vehicle_smoke_pos[v->direction] * effect_offset; |
|
2123 y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset; |
|
2124 |
|
2125 if (HASBIT(v->u.rail.flags, VRF_REVERSE_DIRECTION)) { |
|
2126 x = -x; |
|
2127 y = -y; |
|
2128 } |
|
2129 |
|
2130 switch (effect_type) { |
|
2131 case 0: |
|
2132 // steam smoke. |
|
2133 if (GB(v->tick_counter, 0, 4) == 0) { |
|
2134 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE); |
|
2135 sound = true; |
|
2136 } |
|
2137 break; |
|
2138 |
|
2139 case 1: |
|
2140 // diesel smoke |
|
2141 if (u->cur_speed <= 40 && CHANCE16(15, 128)) { |
|
2142 CreateEffectVehicleRel(v, 0, 0, 10, EV_DIESEL_SMOKE); |
|
2143 sound = true; |
|
2144 } |
|
2145 break; |
|
2146 |
|
2147 case 2: |
|
2148 // blue spark |
|
2149 if (GB(v->tick_counter, 0, 2) == 0 && CHANCE16(1, 45)) { |
|
2150 CreateEffectVehicleRel(v, 0, 0, 10, EV_ELECTRIC_SPARK); |
|
2151 sound = true; |
|
2152 } |
|
2153 break; |
|
2154 } |
|
2155 } while ((v = v->next) != NULL); |
|
2156 |
|
2157 if (sound) PlayVehicleSound(u, VSE_TRAIN_EFFECT); |
|
2158 } |
|
2159 |
|
2160 static void TrainPlayLeaveStationSound(const Vehicle* v) |
|
2161 { |
|
2162 static const SoundFx sfx[] = { |
|
2163 SND_04_TRAIN, |
|
2164 SND_0A_TRAIN_HORN, |
|
2165 SND_0A_TRAIN_HORN |
|
2166 }; |
|
2167 |
|
2168 EngineID engtype = v->engine_type; |
|
2169 |
|
2170 if (PlayVehicleSound(v, VSE_START)) return; |
|
2171 |
|
2172 switch (GetEngine(engtype)->railtype) { |
|
2173 case RAILTYPE_RAIL: |
|
2174 case RAILTYPE_ELECTRIC: |
|
2175 SndPlayVehicleFx(sfx[RailVehInfo(engtype)->engclass], v); |
|
2176 break; |
|
2177 |
|
2178 case RAILTYPE_MONO: SndPlayVehicleFx(SND_47_MAGLEV_2, v); break; |
|
2179 case RAILTYPE_MAGLEV: SndPlayVehicleFx(SND_41_MAGLEV, v); break; |
|
2180 } |
|
2181 } |
|
2182 |
|
2183 static bool CheckTrainStayInDepot(Vehicle *v) |
|
2184 { |
|
2185 Vehicle *u; |
|
2186 |
|
2187 // bail out if not all wagons are in the same depot or not in a depot at all |
|
2188 for (u = v; u != NULL; u = u->next) { |
|
2189 if (u->u.rail.track != 0x80 || u->tile != v->tile) return false; |
|
2190 } |
|
2191 |
|
2192 // if the train got no power, then keep it in the depot |
|
2193 if (v->u.rail.cached_power == 0) { |
|
2194 v->vehstatus |= VS_STOPPED; |
|
2195 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
|
2196 return true; |
|
2197 } |
|
2198 |
|
2199 if (v->u.rail.force_proceed == 0) { |
|
2200 if (++v->load_unload_time_rem < 37) { |
|
2201 InvalidateWindowClasses(WC_TRAINS_LIST); |
|
2202 return true; |
|
2203 } |
|
2204 |
|
2205 v->load_unload_time_rem = 0; |
|
2206 |
|
2207 if (UpdateSignalsOnSegment(v->tile, DirToDiagDir(v->direction))) { |
|
2208 InvalidateWindowClasses(WC_TRAINS_LIST); |
|
2209 return true; |
|
2210 } |
|
2211 } |
|
2212 |
|
2213 VehicleServiceInDepot(v); |
|
2214 InvalidateWindowClasses(WC_TRAINS_LIST); |
|
2215 TrainPlayLeaveStationSound(v); |
|
2216 |
|
2217 v->u.rail.track = 1; |
|
2218 if (v->direction & 2) v->u.rail.track = 2; |
|
2219 |
|
2220 v->vehstatus &= ~VS_HIDDEN; |
|
2221 v->cur_speed = 0; |
|
2222 |
|
2223 UpdateTrainDeltaXY(v, v->direction); |
|
2224 v->cur_image = GetTrainImage(v, v->direction); |
|
2225 VehiclePositionChanged(v); |
|
2226 UpdateSignalsOnSegment(v->tile, DirToDiagDir(v->direction)); |
|
2227 UpdateTrainAcceleration(v); |
|
2228 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); |
|
2229 |
|
2230 return false; |
|
2231 } |
|
2232 |
|
2233 /* Check for station tiles */ |
|
2234 typedef struct TrainTrackFollowerData { |
|
2235 TileIndex dest_coords; |
|
2236 StationID station_index; // station index we're heading for |
|
2237 uint best_bird_dist; |
|
2238 uint best_track_dist; |
|
2239 byte best_track; |
|
2240 } TrainTrackFollowerData; |
|
2241 |
|
2242 static bool NtpCallbFindStation(TileIndex tile, TrainTrackFollowerData *ttfd, int track, uint length) |
|
2243 { |
|
2244 // heading for nowhere? |
|
2245 if (ttfd->dest_coords == 0) return false; |
|
2246 |
|
2247 // did we reach the final station? |
|
2248 if ((ttfd->station_index == INVALID_STATION && tile == ttfd->dest_coords) || ( |
|
2249 IsTileType(tile, MP_STATION) && |
|
2250 IsRailwayStation(tile) && |
|
2251 GetStationIndex(tile) == ttfd->station_index |
|
2252 )) { |
|
2253 /* We do not check for dest_coords if we have a station_index, |
|
2254 * because in that case the dest_coords are just an |
|
2255 * approximation of where the station is */ |
|
2256 // found station |
|
2257 ttfd->best_bird_dist = 0; // we found destination |
|
2258 ttfd->best_track = track; |
|
2259 return true; |
|
2260 } else { |
|
2261 uint dist; |
|
2262 |
|
2263 // didn't find station, keep track of the best path so far. |
|
2264 dist = DistanceManhattan(tile, ttfd->dest_coords); |
|
2265 if (dist < ttfd->best_bird_dist) { |
|
2266 ttfd->best_bird_dist = dist; |
|
2267 ttfd->best_track = track; |
|
2268 } |
|
2269 return false; |
|
2270 } |
|
2271 } |
|
2272 |
|
2273 static void FillWithStationData(TrainTrackFollowerData* fd, const Vehicle* v) |
|
2274 { |
|
2275 fd->dest_coords = v->dest_tile; |
|
2276 if (v->current_order.type == OT_GOTO_STATION) { |
|
2277 fd->station_index = v->current_order.dest; |
|
2278 } else { |
|
2279 fd->station_index = INVALID_STATION; |
|
2280 } |
|
2281 } |
|
2282 |
|
2283 static const byte _initial_tile_subcoord[6][4][3] = { |
|
2284 {{ 15, 8, 1 }, { 0, 0, 0 }, { 0, 8, 5 }, { 0, 0, 0 }}, |
|
2285 {{ 0, 0, 0 }, { 8, 0, 3 }, { 0, 0, 0 }, { 8, 15, 7 }}, |
|
2286 {{ 0, 0, 0 }, { 7, 0, 2 }, { 0, 7, 6 }, { 0, 0, 0 }}, |
|
2287 {{ 15, 8, 2 }, { 0, 0, 0 }, { 0, 0, 0 }, { 8, 15, 6 }}, |
|
2288 {{ 15, 7, 0 }, { 8, 0, 4 }, { 0, 0, 0 }, { 0, 0, 0 }}, |
|
2289 {{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 8, 4 }, { 7, 15, 0 }}, |
|
2290 }; |
|
2291 |
|
2292 static const uint32 _reachable_tracks[4] = { |
|
2293 0x10091009, |
|
2294 0x00160016, |
|
2295 0x05200520, |
|
2296 0x2A002A00, |
|
2297 }; |
|
2298 |
|
2299 static const byte _search_directions[6][4] = { |
|
2300 { 0, 9, 2, 9 }, // track 1 |
|
2301 { 9, 1, 9, 3 }, // track 2 |
|
2302 { 9, 0, 3, 9 }, // track upper |
|
2303 { 1, 9, 9, 2 }, // track lower |
|
2304 { 3, 2, 9, 9 }, // track left |
|
2305 { 9, 9, 1, 0 }, // track right |
|
2306 }; |
|
2307 |
|
2308 static const byte _pick_track_table[6] = {1, 3, 2, 2, 0, 0}; |
|
2309 |
|
2310 /* choose a track */ |
|
2311 static byte ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackdirBits trackdirbits) |
|
2312 { |
|
2313 TrainTrackFollowerData fd; |
|
2314 uint best_track; |
|
2315 // pathfinders are able to tell that route was only 'guessed' |
|
2316 bool path_not_found = false; |
|
2317 |
|
2318 #ifdef PF_BENCHMARK |
|
2319 TIC() |
|
2320 #endif |
|
2321 |
|
2322 assert((trackdirbits & ~0x3F) == 0); |
|
2323 |
|
2324 /* quick return in case only one possible track is available */ |
|
2325 if (KILL_FIRST_BIT(trackdirbits) == 0) return FIND_FIRST_BIT(trackdirbits); |
|
2326 |
|
2327 if (_patches.yapf.rail_use_yapf) { |
|
2328 Trackdir trackdir = YapfChooseRailTrack(v, tile, enterdir, trackdirbits, &path_not_found); |
|
2329 if (trackdir != INVALID_TRACKDIR) { |
|
2330 best_track = TrackdirToTrack(trackdir); |
|
2331 } else { |
|
2332 best_track = FIND_FIRST_BIT(TrackdirBitsToTrackBits(trackdirbits)); |
|
2333 } |
|
2334 } else if (_patches.new_pathfinding_all) { /* Use a new pathfinding for everything */ |
|
2335 void* perf = NpfBeginInterval(); |
|
2336 int time = 0; |
|
2337 |
|
2338 NPFFindStationOrTileData fstd; |
|
2339 NPFFoundTargetData ftd; |
|
2340 Trackdir trackdir; |
|
2341 |
|
2342 NPFFillWithOrderData(&fstd, v); |
|
2343 /* The enterdir for the new tile, is the exitdir for the old tile */ |
|
2344 trackdir = GetVehicleTrackdir(v); |
|
2345 assert(trackdir != 0xff); |
|
2346 |
|
2347 ftd = NPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes); |
|
2348 |
|
2349 if (ftd.best_trackdir == 0xff) { |
|
2350 /* We are already at our target. Just do something */ |
|
2351 //TODO: maybe display error? |
|
2352 //TODO: go straight ahead if possible? |
|
2353 best_track = FIND_FIRST_BIT(trackdirbits); |
|
2354 } else { |
|
2355 /* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains |
|
2356 the direction we need to take to get there, if ftd.best_bird_dist is not 0, |
|
2357 we did not find our target, but ftd.best_trackdir contains the direction leading |
|
2358 to the tile closest to our target. */ |
|
2359 if (ftd.best_bird_dist != 0) path_not_found = true; |
|
2360 /* Discard enterdir information, making it a normal track */ |
|
2361 best_track = TrackdirToTrack(ftd.best_trackdir); |
|
2362 } |
|
2363 |
|
2364 time = NpfEndInterval(perf); |
|
2365 DEBUG(yapf, 4, "[NPFT] %d us - %d rounds - %d open - %d closed -- ", time, 0, _aystar_stats_open_size, _aystar_stats_closed_size); |
|
2366 } else { |
|
2367 void* perf = NpfBeginInterval(); |
|
2368 int time = 0; |
|
2369 |
|
2370 FillWithStationData(&fd, v); |
|
2371 |
|
2372 /* New train pathfinding */ |
|
2373 fd.best_bird_dist = (uint)-1; |
|
2374 fd.best_track_dist = (uint)-1; |
|
2375 fd.best_track = 0xFF; |
|
2376 |
|
2377 NewTrainPathfind(tile - TileOffsByDiagDir(enterdir), v->dest_tile, |
|
2378 v->u.rail.compatible_railtypes, enterdir, (NTPEnumProc*)NtpCallbFindStation, &fd); |
|
2379 |
|
2380 // check whether the path was found or only 'guessed' |
|
2381 if (fd.best_bird_dist != 0) path_not_found = true; |
|
2382 |
|
2383 if (fd.best_track == 0xff) { |
|
2384 // blaha |
|
2385 best_track = FIND_FIRST_BIT(trackdirbits); |
|
2386 } else { |
|
2387 best_track = fd.best_track & 7; |
|
2388 } |
|
2389 |
|
2390 time = NpfEndInterval(perf); |
|
2391 DEBUG(yapf, 4, "[NTPT] %d us - %d rounds - %d open - %d closed -- ", time, 0, 0, 0); |
|
2392 } |
|
2393 // handle "path not found" state |
|
2394 if (path_not_found) { |
|
2395 // PF didn't find the route |
|
2396 if (!HASBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) { |
|
2397 // it is first time the problem occurred, set the "path not found" flag |
|
2398 SETBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION); |
|
2399 // and notify user about the event |
|
2400 if (_patches.lost_train_warn && v->owner == _local_player) { |
|
2401 SetDParam(0, v->unitnumber); |
|
2402 AddNewsItem( |
|
2403 STR_TRAIN_IS_LOST, |
|
2404 NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), |
|
2405 v->index, |
|
2406 0); |
|
2407 } |
|
2408 } |
|
2409 } else { |
|
2410 // route found, is the train marked with "path not found" flag? |
|
2411 if (HASBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) { |
|
2412 // clear the flag as the PF's problem was solved |
|
2413 CLRBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION); |
|
2414 // can we also delete the "News" item somehow? |
|
2415 } |
|
2416 } |
|
2417 |
|
2418 #ifdef PF_BENCHMARK |
|
2419 TOC("PF time = ", 1) |
|
2420 #endif |
|
2421 |
|
2422 return best_track; |
|
2423 } |
|
2424 |
|
2425 |
|
2426 static bool CheckReverseTrain(Vehicle *v) |
|
2427 { |
|
2428 TrainTrackFollowerData fd; |
|
2429 int i, r; |
|
2430 int best_track; |
|
2431 uint best_bird_dist = 0; |
|
2432 uint best_track_dist = 0; |
|
2433 uint reverse, reverse_best; |
|
2434 |
|
2435 if (_opt.diff.line_reverse_mode != 0 || |
|
2436 v->u.rail.track & 0xC0 || |
|
2437 !(v->direction & 1)) |
|
2438 return false; |
|
2439 |
|
2440 FillWithStationData(&fd, v); |
|
2441 |
|
2442 best_track = -1; |
|
2443 reverse_best = reverse = 0; |
|
2444 |
|
2445 assert(v->u.rail.track); |
|
2446 |
|
2447 i = _search_directions[FIND_FIRST_BIT(v->u.rail.track)][DirToDiagDir(v->direction)]; |
|
2448 |
|
2449 if (_patches.yapf.rail_use_yapf) { |
|
2450 reverse_best = YapfCheckReverseTrain(v); |
|
2451 } else if (_patches.new_pathfinding_all) { /* Use a new pathfinding for everything */ |
|
2452 NPFFindStationOrTileData fstd; |
|
2453 NPFFoundTargetData ftd; |
|
2454 byte trackdir, trackdir_rev; |
|
2455 Vehicle* last = GetLastVehicleInChain(v); |
|
2456 |
|
2457 NPFFillWithOrderData(&fstd, v); |
|
2458 |
|
2459 trackdir = GetVehicleTrackdir(v); |
|
2460 trackdir_rev = ReverseTrackdir(GetVehicleTrackdir(last)); |
|
2461 assert(trackdir != 0xff); |
|
2462 assert(trackdir_rev != 0xff); |
|
2463 |
|
2464 ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, last->tile, trackdir_rev, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes); |
|
2465 if (ftd.best_bird_dist != 0) { |
|
2466 /* We didn't find anything, just keep on going straight ahead */ |
|
2467 reverse_best = false; |
|
2468 } else { |
|
2469 if (NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE)) { |
|
2470 reverse_best = true; |
|
2471 } else { |
|
2472 reverse_best = false; |
|
2473 } |
|
2474 } |
|
2475 } else { |
|
2476 for (;;) { |
|
2477 fd.best_bird_dist = (uint)-1; |
|
2478 fd.best_track_dist = (uint)-1; |
|
2479 |
|
2480 NewTrainPathfind(v->tile, v->dest_tile, v->u.rail.compatible_railtypes, reverse ^ i, (NTPEnumProc*)NtpCallbFindStation, &fd); |
|
2481 |
|
2482 if (best_track != -1) { |
|
2483 if (best_bird_dist != 0) { |
|
2484 if (fd.best_bird_dist != 0) { |
|
2485 /* neither reached the destination, pick the one with the smallest bird dist */ |
|
2486 if (fd.best_bird_dist > best_bird_dist) goto bad; |
|
2487 if (fd.best_bird_dist < best_bird_dist) goto good; |
|
2488 } else { |
|
2489 /* we found the destination for the first time */ |
|
2490 goto good; |
|
2491 } |
|
2492 } else { |
|
2493 if (fd.best_bird_dist != 0) { |
|
2494 /* didn't find destination, but we've found the destination previously */ |
|
2495 goto bad; |
|
2496 } else { |
|
2497 /* both old & new reached the destination, compare track length */ |
|
2498 if (fd.best_track_dist > best_track_dist) goto bad; |
|
2499 if (fd.best_track_dist < best_track_dist) goto good; |
|
2500 } |
|
2501 } |
|
2502 |
|
2503 /* if we reach this position, there's two paths of equal value so far. |
|
2504 * pick one randomly. */ |
|
2505 r = GB(Random(), 0, 8); |
|
2506 if (_pick_track_table[i] == (v->direction & 3)) r += 80; |
|
2507 if (_pick_track_table[best_track] == (v->direction & 3)) r -= 80; |
|
2508 if (r <= 127) goto bad; |
|
2509 } |
|
2510 good:; |
|
2511 best_track = i; |
|
2512 best_bird_dist = fd.best_bird_dist; |
|
2513 best_track_dist = fd.best_track_dist; |
|
2514 reverse_best = reverse; |
|
2515 bad:; |
|
2516 if (reverse != 0) break; |
|
2517 reverse = 2; |
|
2518 } |
|
2519 } |
|
2520 |
|
2521 return reverse_best != 0; |
|
2522 } |
|
2523 |
|
2524 static bool ProcessTrainOrder(Vehicle *v) |
|
2525 { |
|
2526 const Order *order; |
|
2527 bool at_waypoint = false; |
|
2528 |
|
2529 switch (v->current_order.type) { |
|
2530 case OT_GOTO_DEPOT: |
|
2531 if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return false; |
|
2532 if ((v->current_order.flags & OF_SERVICE_IF_NEEDED) && |
|
2533 !VehicleNeedsService(v)) { |
|
2534 v->cur_order_index++; |
|
2535 } |
|
2536 break; |
|
2537 |
|
2538 case OT_LOADING: |
|
2539 case OT_LEAVESTATION: |
|
2540 return false; |
|
2541 |
|
2542 default: break; |
|
2543 } |
|
2544 |
|
2545 // check if we've reached the waypoint? |
|
2546 if (v->current_order.type == OT_GOTO_WAYPOINT && v->tile == v->dest_tile) { |
|
2547 v->cur_order_index++; |
|
2548 at_waypoint = true; |
|
2549 } |
|
2550 |
|
2551 // check if we've reached a non-stop station while TTDPatch nonstop is enabled.. |
|
2552 if (_patches.new_nonstop && |
|
2553 v->current_order.flags & OF_NON_STOP && |
|
2554 IsTileType(v->tile, MP_STATION) && |
|
2555 v->current_order.dest == GetStationIndex(v->tile)) { |
|
2556 v->cur_order_index++; |
|
2557 } |
|
2558 |
|
2559 // Get the current order |
|
2560 if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0; |
|
2561 |
|
2562 order = GetVehicleOrder(v, v->cur_order_index); |
|
2563 |
|
2564 // If no order, do nothing. |
|
2565 if (order == NULL) { |
|
2566 v->current_order.type = OT_NOTHING; |
|
2567 v->current_order.flags = 0; |
|
2568 v->dest_tile = 0; |
|
2569 return false; |
|
2570 } |
|
2571 |
|
2572 // If it is unchanged, keep it. |
|
2573 if (order->type == v->current_order.type && |
|
2574 order->flags == v->current_order.flags && |
|
2575 order->dest == v->current_order.dest) |
|
2576 return false; |
|
2577 |
|
2578 // Otherwise set it, and determine the destination tile. |
|
2579 v->current_order = *order; |
|
2580 |
|
2581 v->dest_tile = 0; |
|
2582 |
|
2583 InvalidateVehicleOrder(v); |
|
2584 |
|
2585 switch (order->type) { |
|
2586 case OT_GOTO_STATION: |
|
2587 if (order->dest == v->last_station_visited) |
|
2588 v->last_station_visited = INVALID_STATION; |
|
2589 v->dest_tile = GetStation(order->dest)->xy; |
|
2590 break; |
|
2591 |
|
2592 case OT_GOTO_DEPOT: |
|
2593 v->dest_tile = GetDepot(order->dest)->xy; |
|
2594 break; |
|
2595 |
|
2596 case OT_GOTO_WAYPOINT: |
|
2597 v->dest_tile = GetWaypoint(order->dest)->xy; |
|
2598 break; |
|
2599 |
|
2600 default: |
|
2601 return false; |
|
2602 } |
|
2603 |
|
2604 return !at_waypoint && CheckReverseTrain(v); |
|
2605 } |
|
2606 |
|
2607 static void MarkTrainDirty(Vehicle *v) |
|
2608 { |
|
2609 do { |
|
2610 v->cur_image = GetTrainImage(v, v->direction); |
|
2611 MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1); |
|
2612 } while ((v = v->next) != NULL); |
|
2613 } |
|
2614 |
|
2615 static void HandleTrainLoading(Vehicle *v, bool mode) |
|
2616 { |
|
2617 if (v->current_order.type == OT_NOTHING) return; |
|
2618 |
|
2619 if (v->current_order.type != OT_DUMMY) { |
|
2620 if (v->current_order.type != OT_LOADING) return; |
|
2621 if (mode) return; |
|
2622 |
|
2623 // don't mark the train as lost if we're loading on the final station. |
|
2624 if (v->current_order.flags & OF_NON_STOP) |
|
2625 v->u.rail.days_since_order_progr = 0; |
|
2626 |
|
2627 if (--v->load_unload_time_rem) return; |
|
2628 |
|
2629 if (CanFillVehicle(v) && (v->current_order.flags & OF_FULL_LOAD || |
|
2630 (_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED)))) { |
|
2631 v->u.rail.days_since_order_progr = 0; /* Prevent a train lost message for full loading trains */ |
|
2632 SET_EXPENSES_TYPE(EXPENSES_TRAIN_INC); |
|
2633 if (LoadUnloadVehicle(v, false)) { |
|
2634 InvalidateWindow(WC_TRAINS_LIST, v->owner); |
|
2635 MarkTrainDirty(v); |
|
2636 |
|
2637 // need to update acceleration and cached values since the goods on the train changed. |
|
2638 TrainCargoChanged(v); |
|
2639 UpdateTrainAcceleration(v); |
|
2640 } |
|
2641 return; |
|
2642 } |
|
2643 |
|
2644 TrainPlayLeaveStationSound(v); |
|
2645 |
|
2646 { |
|
2647 Order b = v->current_order; |
|
2648 v->current_order.type = OT_LEAVESTATION; |
|
2649 v->current_order.flags = 0; |
|
2650 |
|
2651 // If this was not the final order, don't remove it from the list. |
|
2652 if (!(b.flags & OF_NON_STOP)) return; |
|
2653 } |
|
2654 } |
|
2655 |
|
2656 v->u.rail.days_since_order_progr = 0; |
|
2657 v->cur_order_index++; |
|
2658 InvalidateVehicleOrder(v); |
|
2659 } |
|
2660 |
|
2661 static int UpdateTrainSpeed(Vehicle *v) |
|
2662 { |
|
2663 uint spd; |
|
2664 uint accel; |
|
2665 |
|
2666 if (v->vehstatus & VS_STOPPED || HASBIT(v->u.rail.flags, VRF_REVERSING)) { |
|
2667 if (_patches.realistic_acceleration) { |
|
2668 accel = GetTrainAcceleration(v, AM_BRAKE) * 2; |
|
2669 } else { |
|
2670 accel = v->acceleration * -2; |
|
2671 } |
|
2672 } else { |
|
2673 if (_patches.realistic_acceleration) { |
|
2674 accel = GetTrainAcceleration(v, AM_ACCEL); |
|
2675 } else { |
|
2676 accel = v->acceleration; |
|
2677 } |
|
2678 } |
|
2679 |
|
2680 spd = v->subspeed + accel * 2; |
|
2681 v->subspeed = (byte)spd; |
|
2682 { |
|
2683 int tempmax = v->max_speed; |
|
2684 if (v->cur_speed > v->max_speed) |
|
2685 tempmax = v->cur_speed - (v->cur_speed / 10) - 1; |
|
2686 v->cur_speed = spd = clamp(v->cur_speed + ((int)spd >> 8), 0, tempmax); |
|
2687 } |
|
2688 |
|
2689 if (!(v->direction & 1)) spd = spd * 3 >> 2; |
|
2690 |
|
2691 spd += v->progress; |
|
2692 v->progress = (byte)spd; |
|
2693 return (spd >> 8); |
|
2694 } |
|
2695 |
|
2696 static void TrainEnterStation(Vehicle *v, StationID station) |
|
2697 { |
|
2698 Station *st; |
|
2699 uint32 flags; |
|
2700 |
|
2701 v->last_station_visited = station; |
|
2702 |
|
2703 /* check if a train ever visited this station before */ |
|
2704 st = GetStation(station); |
|
2705 if (!(st->had_vehicle_of_type & HVOT_TRAIN)) { |
|
2706 st->had_vehicle_of_type |= HVOT_TRAIN; |
|
2707 SetDParam(0, st->index); |
|
2708 flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0); |
|
2709 AddNewsItem( |
|
2710 STR_8801_CITIZENS_CELEBRATE_FIRST, |
|
2711 flags, |
|
2712 v->index, |
|
2713 0 |
|
2714 ); |
|
2715 } |
|
2716 |
|
2717 // Did we reach the final destination? |
|
2718 if (v->current_order.type == OT_GOTO_STATION && |
|
2719 v->current_order.dest == station) { |
|
2720 // Yeah, keep the load/unload flags |
|
2721 // Non Stop now means if the order should be increased. |
|
2722 v->current_order.type = OT_LOADING; |
|
2723 v->current_order.flags &= OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER; |
|
2724 v->current_order.flags |= OF_NON_STOP; |
|
2725 } else { |
|
2726 // No, just do a simple load |
|
2727 v->current_order.type = OT_LOADING; |
|
2728 v->current_order.flags = 0; |
|
2729 } |
|
2730 v->current_order.dest = 0; |
|
2731 |
|
2732 SET_EXPENSES_TYPE(EXPENSES_TRAIN_INC); |
|
2733 if (LoadUnloadVehicle(v, true) != 0) { |
|
2734 InvalidateWindow(WC_TRAINS_LIST, v->owner); |
|
2735 TrainCargoChanged(v); |
|
2736 UpdateTrainAcceleration(v); |
|
2737 } |
|
2738 MarkTrainDirty(v); |
|
2739 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
|
2740 } |
|
2741 |
|
2742 static byte AfterSetTrainPos(Vehicle *v, bool new_tile) |
|
2743 { |
|
2744 byte new_z, old_z; |
|
2745 |
|
2746 // need this hint so it returns the right z coordinate on bridges. |
|
2747 new_z = GetSlopeZ(v->x_pos, v->y_pos); |
|
2748 |
|
2749 old_z = v->z_pos; |
|
2750 v->z_pos = new_z; |
|
2751 |
|
2752 if (new_tile) { |
|
2753 CLRBIT(v->u.rail.flags, VRF_GOINGUP); |
|
2754 CLRBIT(v->u.rail.flags, VRF_GOINGDOWN); |
|
2755 |
|
2756 if (new_z != old_z) { |
|
2757 TileIndex tile = TileVirtXY(v->x_pos, v->y_pos); |
|
2758 |
|
2759 // XXX workaround, whole UP/DOWN detection needs overhaul |
|
2760 if (!IsTunnelTile(tile)) { |
|
2761 SETBIT(v->u.rail.flags, (new_z > old_z) ? VRF_GOINGUP : VRF_GOINGDOWN); |
|
2762 } |
|
2763 } |
|
2764 } |
|
2765 |
|
2766 VehiclePositionChanged(v); |
|
2767 EndVehicleMove(v); |
|
2768 return old_z; |
|
2769 } |
|
2770 |
|
2771 static const Direction _new_vehicle_direction_table[11] = { |
|
2772 DIR_N , DIR_NW, DIR_W , 0, |
|
2773 DIR_NE, DIR_N , DIR_SW, 0, |
|
2774 DIR_E , DIR_SE, DIR_S |
|
2775 }; |
|
2776 |
|
2777 static Direction GetNewVehicleDirectionByTile(TileIndex new_tile, TileIndex old_tile) |
|
2778 { |
|
2779 uint offs = (TileY(new_tile) - TileY(old_tile) + 1) * 4 + |
|
2780 TileX(new_tile) - TileX(old_tile) + 1; |
|
2781 assert(offs < 11); |
|
2782 return _new_vehicle_direction_table[offs]; |
|
2783 } |
|
2784 |
|
2785 static Direction GetNewVehicleDirection(const Vehicle *v, int x, int y) |
|
2786 { |
|
2787 uint offs = (y - v->y_pos + 1) * 4 + (x - v->x_pos + 1); |
|
2788 assert(offs < 11); |
|
2789 return _new_vehicle_direction_table[offs]; |
|
2790 } |
|
2791 |
|
2792 static int GetDirectionToVehicle(const Vehicle *v, int x, int y) |
|
2793 { |
|
2794 byte offs; |
|
2795 |
|
2796 x -= v->x_pos; |
|
2797 if (x >= 0) { |
|
2798 offs = (x > 2) ? 0 : 1; |
|
2799 } else { |
|
2800 offs = (x < -2) ? 2 : 1; |
|
2801 } |
|
2802 |
|
2803 y -= v->y_pos; |
|
2804 if (y >= 0) { |
|
2805 offs += ((y > 2) ? 0 : 1) * 4; |
|
2806 } else { |
|
2807 offs += ((y < -2) ? 2 : 1) * 4; |
|
2808 } |
|
2809 |
|
2810 assert(offs < 11); |
|
2811 return _new_vehicle_direction_table[offs]; |
|
2812 } |
|
2813 |
|
2814 /* Check if the vehicle is compatible with the specified tile */ |
|
2815 static bool CheckCompatibleRail(const Vehicle *v, TileIndex tile) |
|
2816 { |
|
2817 switch (GetTileType(tile)) { |
|
2818 case MP_RAILWAY_BRIDGE: |
|
2819 case MP_RAILWAY: |
|
2820 case MP_STATION: |
|
2821 // normal tracks, jump to owner check |
|
2822 break; |
|
2823 |
|
2824 case MP_STREET: |
|
2825 // tracks over roads, do owner check of tracks |
|
2826 return |
|
2827 IsTileOwner(tile, v->owner) && ( |
|
2828 !IsFrontEngine(v) || |
|
2829 IsCompatibleRail(v->u.rail.railtype, GetRailTypeCrossing(tile)) |
|
2830 ); |
|
2831 |
|
2832 default: |
|
2833 return true; |
|
2834 } |
|
2835 |
|
2836 return |
|
2837 IsTileOwner(tile, v->owner) && ( |
|
2838 !IsFrontEngine(v) || |
|
2839 HASBIT(v->u.rail.compatible_railtypes, GetRailType(tile)) |
|
2840 ); |
|
2841 } |
|
2842 |
|
2843 typedef struct { |
|
2844 byte small_turn, large_turn; |
|
2845 byte z_up; // fraction to remove when moving up |
|
2846 byte z_down; // fraction to remove when moving down |
|
2847 } RailtypeSlowdownParams; |
|
2848 |
|
2849 static const RailtypeSlowdownParams _railtype_slowdown[] = { |
|
2850 // normal accel |
|
2851 {256 / 4, 256 / 2, 256 / 4, 2}, // normal |
|
2852 {256 / 4, 256 / 2, 256 / 4, 2}, // electrified |
|
2853 {256 / 4, 256 / 2, 256 / 4, 2}, // monorail |
|
2854 {0, 256 / 2, 256 / 4, 2}, // maglev |
|
2855 }; |
|
2856 |
|
2857 /* Modify the speed of the vehicle due to a turn */ |
|
2858 static void AffectSpeedByDirChange(Vehicle* v, Direction new_dir) |
|
2859 { |
|
2860 DirDiff diff; |
|
2861 const RailtypeSlowdownParams *rsp; |
|
2862 |
|
2863 if (_patches.realistic_acceleration) return; |
|
2864 |
|
2865 diff = DirDifference(v->direction, new_dir); |
|
2866 if (diff == DIRDIFF_SAME) return; |
|
2867 |
|
2868 rsp = &_railtype_slowdown[v->u.rail.railtype]; |
|
2869 v->cur_speed -= (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT ? rsp->small_turn : rsp->large_turn) * v->cur_speed >> 8; |
|
2870 } |
|
2871 |
|
2872 /* Modify the speed of the vehicle due to a change in altitude */ |
|
2873 static void AffectSpeedByZChange(Vehicle *v, byte old_z) |
|
2874 { |
|
2875 const RailtypeSlowdownParams *rsp; |
|
2876 if (old_z == v->z_pos || _patches.realistic_acceleration) return; |
|
2877 |
|
2878 rsp = &_railtype_slowdown[v->u.rail.railtype]; |
|
2879 |
|
2880 if (old_z < v->z_pos) { |
|
2881 v->cur_speed -= (v->cur_speed * rsp->z_up >> 8); |
|
2882 } else { |
|
2883 uint16 spd = v->cur_speed + rsp->z_down; |
|
2884 if (spd <= v->max_speed) v->cur_speed = spd; |
|
2885 } |
|
2886 } |
|
2887 |
|
2888 static const DiagDirection _otherside_signal_directions[] = { |
|
2889 DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE, 0, 0, |
|
2890 DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE |
|
2891 }; |
|
2892 |
|
2893 static void TrainMovedChangeSignals(TileIndex tile, DiagDirection dir) |
|
2894 { |
|
2895 if ( (IsTileType(tile, MP_RAILWAY) || IsTileType(tile, MP_RAILWAY_BRIDGE)) && |
|
2896 GetRailTileType(tile) == RAIL_TILE_SIGNALS) { |
|
2897 uint i = FindFirstBit2x64(GetTrackBits(tile) * 0x101 & _reachable_tracks[dir]); |
|
2898 UpdateSignalsOnSegment(tile, _otherside_signal_directions[i]); |
|
2899 } |
|
2900 } |
|
2901 |
|
2902 |
|
2903 typedef struct TrainCollideChecker { |
|
2904 const Vehicle *v; |
|
2905 const Vehicle *v_skip; |
|
2906 } TrainCollideChecker; |
|
2907 |
|
2908 static void *FindTrainCollideEnum(Vehicle *v, void *data) |
|
2909 { |
|
2910 const TrainCollideChecker* tcc = data; |
|
2911 |
|
2912 if (v != tcc->v && |
|
2913 v != tcc->v_skip && |
|
2914 v->type == VEH_Train && |
|
2915 v->u.rail.track != 0x80 && |
|
2916 myabs(v->z_pos - tcc->v->z_pos) <= 6 && |
|
2917 myabs(v->x_pos - tcc->v->x_pos) < 6 && |
|
2918 myabs(v->y_pos - tcc->v->y_pos) < 6) { |
|
2919 return v; |
|
2920 } else { |
|
2921 return NULL; |
|
2922 } |
|
2923 } |
|
2924 |
|
2925 static void SetVehicleCrashed(Vehicle *v) |
|
2926 { |
|
2927 Vehicle *u; |
|
2928 |
|
2929 if (v->u.rail.crash_anim_pos != 0) return; |
|
2930 |
|
2931 v->u.rail.crash_anim_pos++; |
|
2932 |
|
2933 u = v; |
|
2934 BEGIN_ENUM_WAGONS(v) |
|
2935 v->vehstatus |= VS_CRASHED; |
|
2936 END_ENUM_WAGONS(v) |
|
2937 |
|
2938 InvalidateWindowWidget(WC_VEHICLE_VIEW, u->index, STATUS_BAR); |
|
2939 } |
|
2940 |
|
2941 static uint CountPassengersInTrain(const Vehicle* v) |
|
2942 { |
|
2943 uint num = 0; |
|
2944 BEGIN_ENUM_WAGONS(v) |
|
2945 if (v->cargo_type == CT_PASSENGERS) num += v->cargo_count; |
|
2946 END_ENUM_WAGONS(v) |
|
2947 return num; |
|
2948 } |
|
2949 |
|
2950 /* |
|
2951 * Checks whether the specified train has a collision with another vehicle. If |
|
2952 * so, destroys this vehicle, and the other vehicle if its subtype has TS_Front. |
|
2953 * Reports the incident in a flashy news item, modifies station ratings and |
|
2954 * plays a sound. |
|
2955 */ |
|
2956 static void CheckTrainCollision(Vehicle *v) |
|
2957 { |
|
2958 TrainCollideChecker tcc; |
|
2959 Vehicle *coll; |
|
2960 Vehicle *realcoll; |
|
2961 uint num; |
|
2962 |
|
2963 /* can't collide in depot */ |
|
2964 if (v->u.rail.track == 0x80) return; |
|
2965 |
|
2966 assert(v->u.rail.track == 0x40 || TileVirtXY(v->x_pos, v->y_pos) == v->tile); |
|
2967 |
|
2968 tcc.v = v; |
|
2969 tcc.v_skip = v->next; |
|
2970 |
|
2971 /* find colliding vehicle */ |
|
2972 realcoll = VehicleFromPos(TileVirtXY(v->x_pos, v->y_pos), &tcc, FindTrainCollideEnum); |
|
2973 if (realcoll == NULL) return; |
|
2974 |
|
2975 coll = GetFirstVehicleInChain(realcoll); |
|
2976 |
|
2977 /* it can't collide with its own wagons */ |
|
2978 if (v == coll || |
|
2979 (v->u.rail.track & 0x40 && (v->direction & 2) != (realcoll->direction & 2))) |
|
2980 return; |
|
2981 |
|
2982 //two drivers + passangers killed in train v |
|
2983 num = 2 + CountPassengersInTrain(v); |
|
2984 if (!(coll->vehstatus & VS_CRASHED)) |
|
2985 //two drivers + passangers killed in train coll (if it was not crashed already) |
|
2986 num += 2 + CountPassengersInTrain(coll); |
|
2987 |
|
2988 SetVehicleCrashed(v); |
|
2989 if (IsFrontEngine(coll)) SetVehicleCrashed(coll); |
|
2990 |
|
2991 SetDParam(0, num); |
|
2992 AddNewsItem(STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL, |
|
2993 NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, 0), |
|
2994 v->index, |
|
2995 0 |
|
2996 ); |
|
2997 |
|
2998 ModifyStationRatingAround(v->tile, v->owner, -160, 30); |
|
2999 SndPlayVehicleFx(SND_13_BIG_CRASH, v); |
|
3000 } |
|
3001 |
|
3002 typedef struct VehicleAtSignalData { |
|
3003 TileIndex tile; |
|
3004 Direction direction; |
|
3005 } VehicleAtSignalData; |
|
3006 |
|
3007 static void *CheckVehicleAtSignal(Vehicle *v, void *data) |
|
3008 { |
|
3009 const VehicleAtSignalData* vasd = data; |
|
3010 |
|
3011 if (v->type == VEH_Train && IsFrontEngine(v) && v->tile == vasd->tile) { |
|
3012 DirDiff diff = ChangeDirDiff(DirDifference(v->direction, vasd->direction), DIRDIFF_90RIGHT); |
|
3013 |
|
3014 if (diff == DIRDIFF_90RIGHT || (v->cur_speed <= 5 && diff <= DIRDIFF_REVERSE)) return v; |
|
3015 } |
|
3016 return NULL; |
|
3017 } |
|
3018 |
|
3019 static void TrainController(Vehicle *v, bool update_image) |
|
3020 { |
|
3021 Vehicle *prev; |
|
3022 GetNewVehiclePosResult gp; |
|
3023 uint32 r, tracks,ts; |
|
3024 int i; |
|
3025 DiagDirection enterdir; |
|
3026 Direction dir; |
|
3027 Direction newdir; |
|
3028 Direction chosen_dir; |
|
3029 byte chosen_track; |
|
3030 byte old_z; |
|
3031 |
|
3032 /* For every vehicle after and including the given vehicle */ |
|
3033 for (prev = GetPrevVehicleInChain(v); v != NULL; prev = v, v = v->next) { |
|
3034 |
|
3035 bool in_tunnel_or_bridge = (v->u.rail.track == 0x40); |
|
3036 bool entering_new_tile; |
|
3037 |
|
3038 BeginVehicleMove(v); |
|
3039 |
|
3040 entering_new_tile = !GetNewVehiclePos(v, &gp); |
|
3041 |
|
3042 if (in_tunnel_or_bridge) { |
|
3043 uint32 res; |
|
3044 /* in tunnel or on a bridge */ |
|
3045 |
|
3046 SetSpeedLimitOnBridge(v); |
|
3047 |
|
3048 if (!(IsTunnelTile(gp.new_tile) || IsBridgeTile(gp.new_tile)) || !((res = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y)) & 0x4)) { |
|
3049 /* stay in tunnel/bridge wormhole */ |
|
3050 v->x_pos = gp.x; |
|
3051 v->y_pos = gp.y; |
|
3052 VehiclePositionChanged(v); |
|
3053 if (!(v->vehstatus & VS_HIDDEN)) EndVehicleMove(v); |
|
3054 continue; |
|
3055 } |
|
3056 /* left tunnel/bridge wormhole */ |
|
3057 dir = v->direction; |
|
3058 enterdir = INVALID_DIAGDIR; |
|
3059 if (IsBridgeTile(gp.new_tile) && res & 0x4) { |
|
3060 /* ok we have just left the bridge (because the status was "onbridge" before and we got |
|
3061 a return value of 4 from VehicleEnterTile. we know the enterdir from the bridge ramp |
|
3062 direction, and act as if we entered the tile normally (hence the goto) */ |
|
3063 dir = v->direction; |
|
3064 enterdir = ReverseDiagDir(GetBridgeRampDirection(gp.new_tile)); |
|
3065 in_tunnel_or_bridge = false; |
|
3066 } |
|
3067 } else { |
|
3068 /* Not inside tunnel */ |
|
3069 if (!entering_new_tile) { |
|
3070 /* Staying in the old tile */ |
|
3071 if (v->u.rail.track == 0x80) { |
|
3072 /* inside depot */ |
|
3073 gp.x = v->x_pos; |
|
3074 gp.y = v->y_pos; |
|
3075 } else { |
|
3076 /* is not inside depot */ |
|
3077 |
|
3078 if (!TrainCheckIfLineEnds(v)) return; |
|
3079 |
|
3080 r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); |
|
3081 if (r & 0x8) { |
|
3082 DEBUG(misc, 2, "%x & 0x8 (in tile)", r); |
|
3083 goto invalid_rail; |
|
3084 } |
|
3085 if (r & 0x2) { |
|
3086 TrainEnterStation(v, r >> 8); |
|
3087 return; |
|
3088 } |
|
3089 |
|
3090 if (v->current_order.type == OT_LEAVESTATION) { |
|
3091 v->current_order.type = OT_NOTHING; |
|
3092 v->current_order.flags = 0; |
|
3093 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
|
3094 } |
|
3095 } |
|
3096 } else { |
|
3097 /* A new tile is about to be entered normally (not from bridge wormhole to ramp) */ |
|
3098 /* Determine what direction we're entering the new tile from */ |
|
3099 dir = GetNewVehicleDirectionByTile(gp.new_tile, gp.old_tile); |
|
3100 enterdir = DirToDiagDir(dir); |
|
3101 assert(enterdir==0 || enterdir==1 || enterdir==2 || enterdir==3); |
|
3102 } |
|
3103 } |
|
3104 if (entering_new_tile && !in_tunnel_or_bridge) { |
|
3105 /* A new tile is about to be entered. */ |
|
3106 byte bits; |
|
3107 |
|
3108 /* Get the status of the tracks in the new tile and mask |
|
3109 * away the bits that aren't reachable. */ |
|
3110 ts = GetTileTrackStatus(gp.new_tile, TRANSPORT_RAIL) & _reachable_tracks[enterdir]; |
|
3111 |
|
3112 /* Combine the from & to directions. |
|
3113 * Now, the lower byte contains the track status, and the byte at bit 16 contains |
|
3114 * the signal status. */ |
|
3115 tracks = ts | (ts >> 8); |
|
3116 bits = tracks & 0xFF; |
|
3117 if ((_patches.new_pathfinding_all || _patches.yapf.rail_use_yapf) && _patches.forbid_90_deg && prev == NULL) { |
|
3118 /* We allow wagons to make 90 deg turns, because forbid_90_deg |
|
3119 * can be switched on halfway a turn */ |
|
3120 bits &= ~TrackCrossesTracks(FIND_FIRST_BIT(v->u.rail.track)); |
|
3121 } |
|
3122 |
|
3123 if (bits == 0) { |
|
3124 DEBUG(misc, 2, "%x == 0", bits); |
|
3125 goto invalid_rail; |
|
3126 } |
|
3127 |
|
3128 /* Check if the new tile contrains tracks that are compatible |
|
3129 * with the current train, if not, bail out. */ |
|
3130 if (!CheckCompatibleRail(v, gp.new_tile)) { |
|
3131 DEBUG(misc, 2, "!CheckCompatibleRail(%p, %x)", v, gp.new_tile); |
|
3132 goto invalid_rail; |
|
3133 } |
|
3134 |
|
3135 if (prev == NULL) { |
|
3136 /* Currently the locomotive is active. Determine which one of the |
|
3137 * available tracks to choose */ |
|
3138 chosen_track = 1 << ChooseTrainTrack(v, gp.new_tile, enterdir, bits); |
|
3139 assert(chosen_track & tracks); |
|
3140 |
|
3141 /* Check if it's a red signal and that force proceed is not clicked. */ |
|
3142 if ( (tracks>>16)&chosen_track && v->u.rail.force_proceed == 0) goto red_light; |
|
3143 } else { |
|
3144 static byte _matching_tracks[8] = {0x30, 1, 0xC, 2, 0x30, 1, 0xC, 2}; |
|
3145 |
|
3146 /* The wagon is active, simply follow the prev vehicle. */ |
|
3147 chosen_track = (byte)(_matching_tracks[GetDirectionToVehicle(prev, gp.x, gp.y)] & bits); |
|
3148 } |
|
3149 |
|
3150 /* make sure chosen track is a valid track */ |
|
3151 assert(chosen_track==1 || chosen_track==2 || chosen_track==4 || chosen_track==8 || chosen_track==16 || chosen_track==32); |
|
3152 |
|
3153 /* Update XY to reflect the entrance to the new tile, and select the direction to use */ |
|
3154 { |
|
3155 const byte *b = _initial_tile_subcoord[FIND_FIRST_BIT(chosen_track)][enterdir]; |
|
3156 gp.x = (gp.x & ~0xF) | b[0]; |
|
3157 gp.y = (gp.y & ~0xF) | b[1]; |
|
3158 chosen_dir = b[2]; |
|
3159 } |
|
3160 |
|
3161 /* Call the landscape function and tell it that the vehicle entered the tile */ |
|
3162 r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); |
|
3163 if (r & 0x8) { |
|
3164 DEBUG(misc, 2, "%x & 0x8 (new tile)", r); |
|
3165 goto invalid_rail; |
|
3166 } |
|
3167 |
|
3168 if (IsLevelCrossingTile(v->tile) && v->next == NULL) { |
|
3169 UnbarCrossing(v->tile); |
|
3170 MarkTileDirtyByTile(v->tile); |
|
3171 } |
|
3172 |
|
3173 if (IsFrontEngine(v)) v->load_unload_time_rem = 0; |
|
3174 |
|
3175 if (!(r&0x4)) { |
|
3176 v->tile = gp.new_tile; |
|
3177 |
|
3178 if (GetTileRailType(gp.new_tile, chosen_track) != GetTileRailType(gp.old_tile, v->u.rail.track)) { |
|
3179 TrainPowerChanged(GetFirstVehicleInChain(v)); |
|
3180 } |
|
3181 |
|
3182 v->u.rail.track = chosen_track; |
|
3183 assert(v->u.rail.track); |
|
3184 } |
|
3185 |
|
3186 if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir); |
|
3187 |
|
3188 /* Signals can only change when the first |
|
3189 * (above) or the last vehicle moves. */ |
|
3190 if (v->next == NULL) |
|
3191 TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir)); |
|
3192 |
|
3193 if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir); |
|
3194 |
|
3195 v->direction = chosen_dir; |
|
3196 } |
|
3197 |
|
3198 /* update image of train, as well as delta XY */ |
|
3199 newdir = GetNewVehicleDirection(v, gp.x, gp.y); |
|
3200 UpdateTrainDeltaXY(v, newdir); |
|
3201 if (update_image) v->cur_image = GetTrainImage(v, newdir); |
|
3202 |
|
3203 v->x_pos = gp.x; |
|
3204 v->y_pos = gp.y; |
|
3205 |
|
3206 /* update the Z position of the vehicle */ |
|
3207 old_z = AfterSetTrainPos(v, (gp.new_tile != gp.old_tile)); |
|
3208 |
|
3209 if (prev == NULL) { |
|
3210 /* This is the first vehicle in the train */ |
|
3211 AffectSpeedByZChange(v, old_z); |
|
3212 } |
|
3213 } |
|
3214 return; |
|
3215 |
|
3216 invalid_rail: |
|
3217 /* We've reached end of line?? */ |
|
3218 if (prev != NULL) error("!Disconnecting train"); |
|
3219 goto reverse_train_direction; |
|
3220 |
|
3221 red_light: { |
|
3222 /* We're in front of a red signal ?? */ |
|
3223 /* find the first set bit in ts. need to do it in 2 steps, since |
|
3224 * FIND_FIRST_BIT only handles 6 bits at a time. */ |
|
3225 i = FindFirstBit2x64(ts); |
|
3226 |
|
3227 if (!HasSignalOnTrackdir(gp.new_tile, ReverseTrackdir(i))) { |
|
3228 v->cur_speed = 0; |
|
3229 v->subspeed = 0; |
|
3230 v->progress = 255 - 100; |
|
3231 if (++v->load_unload_time_rem < _patches.wait_oneway_signal * 20) return; |
|
3232 } else if (HasSignalOnTrackdir(gp.new_tile, i)){ |
|
3233 v->cur_speed = 0; |
|
3234 v->subspeed = 0; |
|
3235 v->progress = 255-10; |
|
3236 if (++v->load_unload_time_rem < _patches.wait_twoway_signal * 73) { |
|
3237 TileIndex o_tile = gp.new_tile + TileOffsByDiagDir(enterdir); |
|
3238 VehicleAtSignalData vasd; |
|
3239 vasd.tile = o_tile; |
|
3240 vasd.direction = ReverseDir(dir); |
|
3241 |
|
3242 /* check if a train is waiting on the other side */ |
|
3243 if (VehicleFromPos(o_tile, &vasd, CheckVehicleAtSignal) == NULL) return; |
|
3244 } |
|
3245 } |
|
3246 } |
|
3247 |
|
3248 reverse_train_direction: |
|
3249 v->load_unload_time_rem = 0; |
|
3250 v->cur_speed = 0; |
|
3251 v->subspeed = 0; |
|
3252 ReverseTrainDirection(v); |
|
3253 } |
|
3254 |
|
3255 extern TileIndex CheckTunnelBusy(TileIndex tile, uint *length); |
|
3256 |
|
3257 /** |
|
3258 * Deletes/Clears the last wagon of a crashed train. It takes the engine of the |
|
3259 * train, then goes to the last wagon and deletes that. Each call to this function |
|
3260 * will remove the last wagon of a crashed train. If this wagon was on a crossing, |
|
3261 * or inside a tunnel, recalculate the signals as they might need updating |
|
3262 * @param v the @Vehicle of which last wagon is to be removed |
|
3263 */ |
|
3264 static void DeleteLastWagon(Vehicle *v) |
|
3265 { |
|
3266 Vehicle *u = v; |
|
3267 |
|
3268 /* Go to the last wagon and delete the link pointing there |
|
3269 * *u is then the one-before-last wagon, and *v the last |
|
3270 * one which will physicially be removed */ |
|
3271 for (; v->next != NULL; v = v->next) u = v; |
|
3272 u->next = NULL; |
|
3273 |
|
3274 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
3275 DeleteWindowById(WC_VEHICLE_VIEW, v->index); |
|
3276 RebuildVehicleLists(); |
|
3277 InvalidateWindow(WC_COMPANY, v->owner); |
|
3278 |
|
3279 BeginVehicleMove(v); |
|
3280 EndVehicleMove(v); |
|
3281 DeleteVehicle(v); |
|
3282 |
|
3283 if (!(v->u.rail.track & 0xC0)) |
|
3284 SetSignalsOnBothDir(v->tile, FIND_FIRST_BIT(v->u.rail.track)); |
|
3285 |
|
3286 /* Check if the wagon was on a road/rail-crossing and disable it if no |
|
3287 * others are on it */ |
|
3288 DisableTrainCrossing(v->tile); |
|
3289 |
|
3290 if ( (v->u.rail.track == 0x40 && v->vehstatus & VS_HIDDEN) ) { // inside a tunnel |
|
3291 TileIndex endtile = CheckTunnelBusy(v->tile, NULL); |
|
3292 |
|
3293 if (endtile == INVALID_TILE) return; // tunnel is busy (error returned) |
|
3294 |
|
3295 switch (v->direction) { |
|
3296 case 1: |
|
3297 case 5: |
|
3298 SetSignalsOnBothDir(v->tile, 0); |
|
3299 SetSignalsOnBothDir(endtile, 0); |
|
3300 break; |
|
3301 |
|
3302 case 3: |
|
3303 case 7: |
|
3304 SetSignalsOnBothDir(v->tile, 1); |
|
3305 SetSignalsOnBothDir(endtile, 1); |
|
3306 break; |
|
3307 |
|
3308 default: |
|
3309 break; |
|
3310 } |
|
3311 } |
|
3312 } |
|
3313 |
|
3314 static void ChangeTrainDirRandomly(Vehicle *v) |
|
3315 { |
|
3316 static const DirDiff delta[] = { |
|
3317 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT |
|
3318 }; |
|
3319 |
|
3320 do { |
|
3321 /* We don't need to twist around vehicles if they're not visible */ |
|
3322 if (!(v->vehstatus & VS_HIDDEN)) { |
|
3323 v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]); |
|
3324 BeginVehicleMove(v); |
|
3325 UpdateTrainDeltaXY(v, v->direction); |
|
3326 v->cur_image = GetTrainImage(v, v->direction); |
|
3327 /* Refrain from updating the z position of the vehicle when on |
|
3328 a bridge, because AfterSetTrainPos will put the vehicle under |
|
3329 the bridge in that case */ |
|
3330 if (!(v->u.rail.track & 0x40)) AfterSetTrainPos(v, false); |
|
3331 } |
|
3332 } while ((v = v->next) != NULL); |
|
3333 } |
|
3334 |
|
3335 static void HandleCrashedTrain(Vehicle *v) |
|
3336 { |
|
3337 int state = ++v->u.rail.crash_anim_pos; |
|
3338 uint32 r; |
|
3339 Vehicle *u; |
|
3340 |
|
3341 if (state == 4 && !(v->u.rail.track & VS_HIDDEN)) { |
|
3342 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE); |
|
3343 } |
|
3344 |
|
3345 if (state <= 200 && CHANCE16R(1, 7, r)) { |
|
3346 int index = (r * 10 >> 16); |
|
3347 |
|
3348 u = v; |
|
3349 do { |
|
3350 if (--index < 0) { |
|
3351 r = Random(); |
|
3352 |
|
3353 CreateEffectVehicleRel(u, |
|
3354 GB(r, 8, 3) + 2, |
|
3355 GB(r, 16, 3) + 2, |
|
3356 GB(r, 0, 3) + 5, |
|
3357 EV_EXPLOSION_SMALL); |
|
3358 break; |
|
3359 } |
|
3360 } while ((u = u->next) != NULL); |
|
3361 } |
|
3362 |
|
3363 if (state <= 240 && !(v->tick_counter & 3)) ChangeTrainDirRandomly(v); |
|
3364 |
|
3365 if (state >= 4440 && !(v->tick_counter&0x1F)) { |
|
3366 DeleteLastWagon(v); |
|
3367 InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train); |
|
3368 } |
|
3369 } |
|
3370 |
|
3371 static void HandleBrokenTrain(Vehicle *v) |
|
3372 { |
|
3373 if (v->breakdown_ctr != 1) { |
|
3374 v->breakdown_ctr = 1; |
|
3375 v->cur_speed = 0; |
|
3376 |
|
3377 if (v->breakdowns_since_last_service != 255) |
|
3378 v->breakdowns_since_last_service++; |
|
3379 |
|
3380 InvalidateWindow(WC_VEHICLE_VIEW, v->index); |
|
3381 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
3382 |
|
3383 if (!PlayVehicleSound(v, VSE_BREAKDOWN)) { |
|
3384 SndPlayVehicleFx((_opt.landscape != LT_CANDY) ? |
|
3385 SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v); |
|
3386 } |
|
3387 |
|
3388 if (!(v->vehstatus & VS_HIDDEN)) { |
|
3389 Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE); |
|
3390 if (u != NULL) u->u.special.unk0 = v->breakdown_delay * 2; |
|
3391 } |
|
3392 } |
|
3393 |
|
3394 if (!(v->tick_counter & 3)) { |
|
3395 if (!--v->breakdown_delay) { |
|
3396 v->breakdown_ctr = 0; |
|
3397 InvalidateWindow(WC_VEHICLE_VIEW, v->index); |
|
3398 } |
|
3399 } |
|
3400 } |
|
3401 |
|
3402 static const byte _breakdown_speeds[16] = { |
|
3403 225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15 |
|
3404 }; |
|
3405 |
|
3406 static bool TrainCheckIfLineEnds(Vehicle *v) |
|
3407 { |
|
3408 TileIndex tile; |
|
3409 uint x,y; |
|
3410 uint16 break_speed; |
|
3411 DiagDirection dir; |
|
3412 int t; |
|
3413 uint32 ts; |
|
3414 |
|
3415 t = v->breakdown_ctr; |
|
3416 if (t > 1) { |
|
3417 v->vehstatus |= VS_TRAIN_SLOWING; |
|
3418 |
|
3419 break_speed = _breakdown_speeds[GB(~t, 4, 4)]; |
|
3420 if (break_speed < v->cur_speed) v->cur_speed = break_speed; |
|
3421 } else { |
|
3422 v->vehstatus &= ~VS_TRAIN_SLOWING; |
|
3423 } |
|
3424 |
|
3425 if (v->u.rail.track & 0x40) return true; // exit if inside a tunnel |
|
3426 if (v->u.rail.track & 0x80) return true; // exit if inside a depot |
|
3427 |
|
3428 tile = v->tile; |
|
3429 |
|
3430 if (IsTileType(tile, MP_TUNNEL) && DiagDirToDir(GetTunnelDirection(tile)) == v->direction) return true; |
|
3431 |
|
3432 if (IsTileType(tile, MP_RAILWAY_BRIDGE)) { |
|
3433 /* on a bridge ramp, we can always access the bridge body if we are driving in the right direction */ |
|
3434 DiagDirection dir = GetBridgeRampDirection(tile); |
|
3435 Direction vdir = v->direction; |
|
3436 |
|
3437 switch (dir) { |
|
3438 /* XXX - Isn't there a simpler way to do this? */ |
|
3439 case DIAGDIR_NE: if (vdir == DIR_N || vdir == DIR_NE || vdir == DIR_E) return true; break; |
|
3440 case DIAGDIR_SE: if (vdir == DIR_S || vdir == DIR_SE || vdir == DIR_E) return true; break; |
|
3441 case DIAGDIR_SW: if (vdir == DIR_S || vdir == DIR_SW || vdir == DIR_W) return true; break; |
|
3442 case DIAGDIR_NW: if (vdir == DIR_N || vdir == DIR_NW || vdir == DIR_W) return true; break; |
|
3443 default: NOT_REACHED(); |
|
3444 } |
|
3445 } |
|
3446 |
|
3447 // depot? |
|
3448 /* XXX -- When enabled, this makes it possible to crash trains of others |
|
3449 (by building a depot right against a station) */ |
|
3450 /* if (IsTileType(tile, MP_RAILWAY) && GetRailTileType(tile) == RAIL_TILE_DEPOT_WAYPOINT) |
|
3451 return true;*/ |
|
3452 |
|
3453 /* Determine the non-diagonal direction in which we will exit this tile */ |
|
3454 dir = DirToDiagDir(v->direction); |
|
3455 if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[dir]) { |
|
3456 dir = ChangeDiagDir(dir, DIAGDIRDIFF_90LEFT); |
|
3457 } |
|
3458 /* Calculate next tile */ |
|
3459 tile += TileOffsByDiagDir(dir); |
|
3460 // determine the track status on the next tile. |
|
3461 ts = GetTileTrackStatus(tile, TRANSPORT_RAIL) & _reachable_tracks[dir]; |
|
3462 |
|
3463 /* Calc position within the current tile ?? */ |
|
3464 x = v->x_pos & 0xF; |
|
3465 y = v->y_pos & 0xF; |
|
3466 |
|
3467 switch (v->direction) { |
|
3468 case DIR_N : x = ~x + ~y + 24; break; |
|
3469 case DIR_NW: x = y; /* FALLTHROUGH */ |
|
3470 case DIR_NE: x = ~x + 16; break; |
|
3471 case DIR_E : x = ~x + y + 8; break; |
|
3472 case DIR_SE: x = y; break; |
|
3473 case DIR_S : x = x + y - 8; break; |
|
3474 case DIR_W : x = ~y + x + 8; break; |
|
3475 } |
|
3476 |
|
3477 if (GB(ts, 0, 16) != 0) { |
|
3478 /* If we approach a rail-piece which we can't enter, or the back of a depot, don't enter it! */ |
|
3479 if (x + 4 >= TILE_SIZE && |
|
3480 (!CheckCompatibleRail(v, tile) || |
|
3481 (IsTileDepotType(tile, TRANSPORT_RAIL) && |
|
3482 GetRailDepotDirection(tile) == dir))) { |
|
3483 v->cur_speed = 0; |
|
3484 ReverseTrainDirection(v); |
|
3485 return false; |
|
3486 } |
|
3487 if ((ts &= (ts >> 16)) == 0) { |
|
3488 // make a rail/road crossing red |
|
3489 if (IsLevelCrossingTile(tile)) { |
|
3490 if (!IsCrossingBarred(tile)) { |
|
3491 BarCrossing(tile); |
|
3492 SndPlayVehicleFx(SND_0E_LEVEL_CROSSING, v); |
|
3493 MarkTileDirtyByTile(tile); |
|
3494 } |
|
3495 } |
|
3496 return true; |
|
3497 } |
|
3498 } else if (x + 4 >= TILE_SIZE) { |
|
3499 v->cur_speed = 0; |
|
3500 ReverseTrainDirection(v); |
|
3501 return false; |
|
3502 } |
|
3503 |
|
3504 // slow down |
|
3505 v->vehstatus |= VS_TRAIN_SLOWING; |
|
3506 break_speed = _breakdown_speeds[x & 0xF]; |
|
3507 if (!(v->direction & 1)) break_speed >>= 1; |
|
3508 if (break_speed < v->cur_speed) v->cur_speed = break_speed; |
|
3509 |
|
3510 return true; |
|
3511 } |
|
3512 |
|
3513 static void TrainLocoHandler(Vehicle *v, bool mode) |
|
3514 { |
|
3515 int j; |
|
3516 |
|
3517 /* train has crashed? */ |
|
3518 if (v->u.rail.crash_anim_pos != 0) { |
|
3519 if (!mode) HandleCrashedTrain(v); |
|
3520 return; |
|
3521 } |
|
3522 |
|
3523 if (v->u.rail.force_proceed != 0) v->u.rail.force_proceed--; |
|
3524 |
|
3525 /* train is broken down? */ |
|
3526 if (v->breakdown_ctr != 0) { |
|
3527 if (v->breakdown_ctr <= 2) { |
|
3528 HandleBrokenTrain(v); |
|
3529 return; |
|
3530 } |
|
3531 v->breakdown_ctr--; |
|
3532 } |
|
3533 |
|
3534 if (HASBIT(v->u.rail.flags, VRF_REVERSING) && v->cur_speed == 0) { |
|
3535 ReverseTrainDirection(v); |
|
3536 } |
|
3537 |
|
3538 /* exit if train is stopped */ |
|
3539 if (v->vehstatus & VS_STOPPED && v->cur_speed == 0) return; |
|
3540 |
|
3541 if (ProcessTrainOrder(v)) { |
|
3542 v->load_unload_time_rem = 0; |
|
3543 v->cur_speed = 0; |
|
3544 v->subspeed = 0; |
|
3545 ReverseTrainDirection(v); |
|
3546 return; |
|
3547 } |
|
3548 |
|
3549 HandleTrainLoading(v, mode); |
|
3550 |
|
3551 if (v->current_order.type == OT_LOADING) return; |
|
3552 |
|
3553 if (CheckTrainStayInDepot(v)) return; |
|
3554 |
|
3555 if (!mode) HandleLocomotiveSmokeCloud(v); |
|
3556 |
|
3557 j = UpdateTrainSpeed(v); |
|
3558 if (j == 0) { |
|
3559 // if the vehicle has speed 0, update the last_speed field. |
|
3560 if (v->cur_speed != 0) return; |
|
3561 } else { |
|
3562 TrainCheckIfLineEnds(v); |
|
3563 |
|
3564 do { |
|
3565 TrainController(v, true); |
|
3566 CheckTrainCollision(v); |
|
3567 if (v->cur_speed <= 0x100) |
|
3568 break; |
|
3569 } while (--j != 0); |
|
3570 } |
|
3571 |
|
3572 SetLastSpeed(v, v->cur_speed); |
|
3573 } |
|
3574 |
|
3575 |
|
3576 void Train_Tick(Vehicle *v) |
|
3577 { |
|
3578 if (_age_cargo_skip_counter == 0 && v->cargo_days != 0xff) |
|
3579 v->cargo_days++; |
|
3580 |
|
3581 v->tick_counter++; |
|
3582 |
|
3583 if (IsFrontEngine(v)) { |
|
3584 TrainLocoHandler(v, false); |
|
3585 |
|
3586 // make sure vehicle wasn't deleted. |
|
3587 if (v->type == VEH_Train && IsFrontEngine(v)) |
|
3588 TrainLocoHandler(v, true); |
|
3589 } else if (IsFreeWagon(v) && HASBITS(v->vehstatus, VS_CRASHED)) { |
|
3590 // Delete flooded standalone wagon |
|
3591 if (++v->u.rail.crash_anim_pos >= 4400) |
|
3592 DeleteVehicle(v); |
|
3593 } |
|
3594 } |
|
3595 |
|
3596 #define MAX_ACCEPTABLE_DEPOT_DIST 16 |
|
3597 |
|
3598 static void CheckIfTrainNeedsService(Vehicle *v) |
|
3599 { |
|
3600 const Depot* depot; |
|
3601 TrainFindDepotData tfdd; |
|
3602 |
|
3603 if (_patches.servint_trains == 0) return; |
|
3604 if (!VehicleNeedsService(v)) return; |
|
3605 if (v->vehstatus & VS_STOPPED) return; |
|
3606 if (_patches.gotodepot && VehicleHasDepotOrders(v)) return; |
|
3607 |
|
3608 // Don't interfere with a depot visit scheduled by the user, or a |
|
3609 // depot visit by the order list. |
|
3610 if (v->current_order.type == OT_GOTO_DEPOT && |
|
3611 (v->current_order.flags & (OF_HALT_IN_DEPOT | OF_PART_OF_ORDERS)) != 0) |
|
3612 return; |
|
3613 |
|
3614 if (CheckTrainIsInsideDepot(v)) { |
|
3615 VehicleServiceInDepot(v); |
|
3616 return; |
|
3617 } |
|
3618 |
|
3619 tfdd = FindClosestTrainDepot(v, MAX_ACCEPTABLE_DEPOT_DIST); |
|
3620 /* Only go to the depot if it is not too far out of our way. */ |
|
3621 if (tfdd.best_length == (uint)-1 || tfdd.best_length > MAX_ACCEPTABLE_DEPOT_DIST) { |
|
3622 if (v->current_order.type == OT_GOTO_DEPOT) { |
|
3623 /* If we were already heading for a depot but it has |
|
3624 * suddenly moved farther away, we continue our normal |
|
3625 * schedule? */ |
|
3626 v->current_order.type = OT_DUMMY; |
|
3627 v->current_order.flags = 0; |
|
3628 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
|
3629 } |
|
3630 return; |
|
3631 } |
|
3632 |
|
3633 depot = GetDepotByTile(tfdd.tile); |
|
3634 |
|
3635 if (v->current_order.type == OT_GOTO_DEPOT && |
|
3636 v->current_order.dest != depot->index && |
|
3637 !CHANCE16(3, 16)) { |
|
3638 return; |
|
3639 } |
|
3640 |
|
3641 v->current_order.type = OT_GOTO_DEPOT; |
|
3642 v->current_order.flags = OF_NON_STOP; |
|
3643 v->current_order.dest = depot->index; |
|
3644 v->dest_tile = tfdd.tile; |
|
3645 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
|
3646 } |
|
3647 |
|
3648 int32 GetTrainRunningCost(const Vehicle *v) |
|
3649 { |
|
3650 int32 cost = 0; |
|
3651 |
|
3652 do { |
|
3653 const RailVehicleInfo *rvi = RailVehInfo(v->engine_type); |
|
3654 if (rvi->running_cost_base > 0) |
|
3655 cost += rvi->running_cost_base * _price.running_rail[rvi->running_cost_class]; |
|
3656 } while ((v = GetNextVehicle(v)) != NULL); |
|
3657 |
|
3658 return cost; |
|
3659 } |
|
3660 |
|
3661 void OnNewDay_Train(Vehicle *v) |
|
3662 { |
|
3663 TileIndex tile; |
|
3664 |
|
3665 if ((++v->day_counter & 7) == 0) DecreaseVehicleValue(v); |
|
3666 |
|
3667 if (IsFrontEngine(v)) { |
|
3668 CheckVehicleBreakdown(v); |
|
3669 AgeVehicle(v); |
|
3670 |
|
3671 CheckIfTrainNeedsService(v); |
|
3672 |
|
3673 CheckOrders(v); |
|
3674 |
|
3675 /* update destination */ |
|
3676 if (v->current_order.type == OT_GOTO_STATION && |
|
3677 (tile = GetStation(v->current_order.dest)->train_tile) != 0) { |
|
3678 v->dest_tile = tile; |
|
3679 } |
|
3680 |
|
3681 if ((v->vehstatus & VS_STOPPED) == 0) { |
|
3682 /* running costs */ |
|
3683 int32 cost = GetTrainRunningCost(v) / 364; |
|
3684 |
|
3685 v->profit_this_year -= cost >> 8; |
|
3686 |
|
3687 SET_EXPENSES_TYPE(EXPENSES_TRAIN_RUN); |
|
3688 SubtractMoneyFromPlayerFract(v->owner, cost); |
|
3689 |
|
3690 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
3691 InvalidateWindowClasses(WC_TRAINS_LIST); |
|
3692 } |
|
3693 } |
|
3694 } |
|
3695 |
|
3696 void TrainsYearlyLoop(void) |
|
3697 { |
|
3698 Vehicle *v; |
|
3699 |
|
3700 FOR_ALL_VEHICLES(v) { |
|
3701 if (v->type == VEH_Train && IsFrontEngine(v)) { |
|
3702 |
|
3703 // show warning if train is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) |
|
3704 if (_patches.train_income_warn && v->owner == _local_player && v->age >= 730 && v->profit_this_year < 0) { |
|
3705 SetDParam(1, v->profit_this_year); |
|
3706 SetDParam(0, v->unitnumber); |
|
3707 AddNewsItem( |
|
3708 STR_TRAIN_IS_UNPROFITABLE, |
|
3709 NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), |
|
3710 v->index, |
|
3711 0); |
|
3712 } |
|
3713 |
|
3714 v->profit_last_year = v->profit_this_year; |
|
3715 v->profit_this_year = 0; |
|
3716 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
3717 } |
|
3718 } |
|
3719 } |
|
3720 |
|
3721 |
|
3722 void InitializeTrains(void) |
|
3723 { |
|
3724 _age_cargo_skip_counter = 1; |
|
3725 } |
|
3726 |
|
3727 /* |
|
3728 * Link front and rear multiheaded engines to each other |
|
3729 * This is done when loading a savegame |
|
3730 */ |
|
3731 void ConnectMultiheadedTrains(void) |
|
3732 { |
|
3733 Vehicle *v; |
|
3734 |
|
3735 FOR_ALL_VEHICLES(v) { |
|
3736 if (v->type == VEH_Train) { |
|
3737 v->u.rail.other_multiheaded_part = NULL; |
|
3738 } |
|
3739 } |
|
3740 |
|
3741 FOR_ALL_VEHICLES(v) { |
|
3742 if (v->type == VEH_Train && IsFrontEngine(v)) { |
|
3743 Vehicle *u = v; |
|
3744 |
|
3745 BEGIN_ENUM_WAGONS(u) { |
|
3746 if (u->u.rail.other_multiheaded_part != NULL) continue; // we already linked this one |
|
3747 |
|
3748 if (IsMultiheaded(u)) { |
|
3749 if (!IsTrainEngine(u)) { |
|
3750 /* we got a rear car without a front car. We will convert it to a front one */ |
|
3751 SetTrainEngine(u); |
|
3752 u->spritenum--; |
|
3753 } |
|
3754 |
|
3755 { |
|
3756 Vehicle *w; |
|
3757 |
|
3758 for (w = u->next; w != NULL && (w->engine_type != u->engine_type || w->u.rail.other_multiheaded_part != NULL); w = GetNextVehicle(w)); |
|
3759 if (w != NULL) { |
|
3760 /* we found a car to partner with this engine. Now we will make sure it face the right way */ |
|
3761 if (IsTrainEngine(w)) { |
|
3762 ClearTrainEngine(w); |
|
3763 w->spritenum++; |
|
3764 } |
|
3765 } |
|
3766 |
|
3767 if (w != NULL) { |
|
3768 w->u.rail.other_multiheaded_part = u; |
|
3769 u->u.rail.other_multiheaded_part = w; |
|
3770 } else { |
|
3771 /* we got a front car and no rear cars. We will fake this one for forget that it should have been multiheaded */ |
|
3772 ClearMultiheaded(u); |
|
3773 } |
|
3774 } |
|
3775 } |
|
3776 } END_ENUM_WAGONS(u) |
|
3777 } |
|
3778 } |
|
3779 } |
|
3780 |
|
3781 /* |
|
3782 * Converts all trains to the new subtype format introduced in savegame 16.2 |
|
3783 * It also links multiheaded engines or make them forget they are multiheaded if no suitable partner is found |
|
3784 */ |
|
3785 void ConvertOldMultiheadToNew(void) |
|
3786 { |
|
3787 Vehicle *v; |
|
3788 FOR_ALL_VEHICLES(v) { |
|
3789 if (v->type == VEH_Train) { |
|
3790 SETBIT(v->subtype, 7); // indicates that it's the old format and needs to be converted in the next loop |
|
3791 } |
|
3792 } |
|
3793 |
|
3794 FOR_ALL_VEHICLES(v) { |
|
3795 if (v->type == VEH_Train) { |
|
3796 if (HASBIT(v->subtype, 7) && ((v->subtype & ~0x80) == 0 || (v->subtype & ~0x80) == 4)) { |
|
3797 Vehicle *u = v; |
|
3798 |
|
3799 BEGIN_ENUM_WAGONS(u) { |
|
3800 const RailVehicleInfo *rvi = RailVehInfo(u->engine_type); |
|
3801 |
|
3802 CLRBIT(u->subtype, 7); |
|
3803 switch (u->subtype) { |
|
3804 case 0: /* TS_Front_Engine */ |
|
3805 if (rvi->flags & RVI_MULTIHEAD) SetMultiheaded(u); |
|
3806 SetFrontEngine(u); |
|
3807 SetTrainEngine(u); |
|
3808 break; |
|
3809 |
|
3810 case 1: /* TS_Artic_Part */ |
|
3811 u->subtype = 0; |
|
3812 SetArticulatedPart(u); |
|
3813 break; |
|
3814 |
|
3815 case 2: /* TS_Not_First */ |
|
3816 u->subtype = 0; |
|
3817 if (rvi->flags & RVI_WAGON) { |
|
3818 // normal wagon |
|
3819 SetTrainWagon(u); |
|
3820 break; |
|
3821 } |
|
3822 if (rvi->flags & RVI_MULTIHEAD && rvi->image_index == u->spritenum - 1) { |
|
3823 // rear end of a multiheaded engine |
|
3824 SetMultiheaded(u); |
|
3825 break; |
|
3826 } |
|
3827 if (rvi->flags & RVI_MULTIHEAD) SetMultiheaded(u); |
|
3828 SetTrainEngine(u); |
|
3829 break; |
|
3830 |
|
3831 case 4: /* TS_Free_Car */ |
|
3832 u->subtype = 0; |
|
3833 SetTrainWagon(u); |
|
3834 SetFreeWagon(u); |
|
3835 break; |
|
3836 default: NOT_REACHED(); break; |
|
3837 } |
|
3838 } END_ENUM_WAGONS(u) |
|
3839 } |
|
3840 } |
|
3841 } |
|
3842 } |