|
1 /* $Id$ */ |
|
2 |
|
3 #include "stdafx.h" |
|
4 #include "openttd.h" |
|
5 #include "road_map.h" |
|
6 #include "roadveh.h" |
|
7 #include "ship.h" |
|
8 #include "spritecache.h" |
|
9 #include "table/sprites.h" |
|
10 #include "table/strings.h" |
|
11 #include "functions.h" |
|
12 #include "map.h" |
|
13 #include "tile.h" |
|
14 #include "vehicle.h" |
|
15 #include "gfx.h" |
|
16 #include "viewport.h" |
|
17 #include "news.h" |
|
18 #include "command.h" |
|
19 #include "saveload.h" |
|
20 #include "player.h" |
|
21 #include "engine.h" |
|
22 #include "sound.h" |
|
23 #include "debug.h" |
|
24 #include "vehicle_gui.h" |
|
25 #include "depot.h" |
|
26 #include "station.h" |
|
27 #include "rail.h" |
|
28 #include "train.h" |
|
29 #include "aircraft.h" |
|
30 #include "industry_map.h" |
|
31 #include "station_map.h" |
|
32 #include "water_map.h" |
|
33 #include "network/network.h" |
|
34 #include "yapf/yapf.h" |
|
35 #include "date.h" |
|
36 #include "newgrf_engine.h" |
|
37 #include "newgrf_sound.h" |
|
38 |
|
39 #define INVALID_COORD (-0x8000) |
|
40 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6)) |
|
41 |
|
42 /* |
|
43 * These command macros are used to call vehicle type specific commands with non type specific commands |
|
44 * it should be used like: DoCommandP(x, y, p1, p2, flags, CMD_STARTSTOP_VEH(v->type)) |
|
45 * that line will start/stop a vehicle nomatter what type it is |
|
46 * VEH_Train is used as an offset because the vehicle type values doesn't start with 0 |
|
47 */ |
|
48 |
|
49 #define CMD_BUILD_VEH(x) _veh_build_proc_table[ x - VEH_Train] |
|
50 #define CMD_SELL_VEH(x) _veh_sell_proc_table [ x - VEH_Train] |
|
51 #define CMD_REFIT_VEH(x) _veh_refit_proc_table[ x - VEH_Train] |
|
52 |
|
53 static const uint32 _veh_build_proc_table[] = { |
|
54 CMD_BUILD_RAIL_VEHICLE, |
|
55 CMD_BUILD_ROAD_VEH, |
|
56 CMD_BUILD_SHIP, |
|
57 CMD_BUILD_AIRCRAFT, |
|
58 }; |
|
59 static const uint32 _veh_sell_proc_table[] = { |
|
60 CMD_SELL_RAIL_WAGON, |
|
61 CMD_SELL_ROAD_VEH, |
|
62 CMD_SELL_SHIP, |
|
63 CMD_SELL_AIRCRAFT, |
|
64 }; |
|
65 |
|
66 static const uint32 _veh_refit_proc_table[] = { |
|
67 CMD_REFIT_RAIL_VEHICLE, |
|
68 CMD_REFIT_ROAD_VEH, |
|
69 CMD_REFIT_SHIP, |
|
70 CMD_REFIT_AIRCRAFT, |
|
71 }; |
|
72 |
|
73 const uint32 _send_to_depot_proc_table[] = { |
|
74 CMD_SEND_TRAIN_TO_DEPOT, |
|
75 CMD_SEND_ROADVEH_TO_DEPOT, |
|
76 CMD_SEND_SHIP_TO_DEPOT, |
|
77 CMD_SEND_AIRCRAFT_TO_HANGAR, |
|
78 }; |
|
79 |
|
80 |
|
81 enum { |
|
82 BLOCKS_FOR_SPECIAL_VEHICLES = 2, ///< Blocks needed for special vehicles |
|
83 }; |
|
84 |
|
85 /** |
|
86 * Called if a new block is added to the vehicle-pool |
|
87 */ |
|
88 static void VehiclePoolNewBlock(uint start_item) |
|
89 { |
|
90 Vehicle *v; |
|
91 |
|
92 /* We don't use FOR_ALL here, because FOR_ALL skips invalid items. |
|
93 * TODO - This is just a temporary stage, this will be removed. */ |
|
94 for (v = GetVehicle(start_item); v != NULL; v = (v->index + 1U < GetVehiclePoolSize()) ? GetVehicle(v->index + 1) : NULL) v->index = start_item++; |
|
95 } |
|
96 |
|
97 /* Initialize the vehicle-pool */ |
|
98 DEFINE_OLD_POOL(Vehicle, Vehicle, VehiclePoolNewBlock, NULL) |
|
99 |
|
100 void VehicleServiceInDepot(Vehicle *v) |
|
101 { |
|
102 v->date_of_last_service = _date; |
|
103 v->breakdowns_since_last_service = 0; |
|
104 v->reliability = GetEngine(v->engine_type)->reliability; |
|
105 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated |
|
106 } |
|
107 |
|
108 bool VehicleNeedsService(const Vehicle *v) |
|
109 { |
|
110 if (v->vehstatus & VS_CRASHED) |
|
111 return false; /* Crashed vehicles don't need service anymore */ |
|
112 |
|
113 if (_patches.no_servicing_if_no_breakdowns && _opt.diff.vehicle_breakdowns == 0) { |
|
114 return EngineHasReplacementForPlayer(GetPlayer(v->owner), v->engine_type); /* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off */ |
|
115 } |
|
116 |
|
117 return _patches.servint_ispercent ? |
|
118 (v->reliability < GetEngine(v->engine_type)->reliability * (100 - v->service_interval) / 100) : |
|
119 (v->date_of_last_service + v->service_interval < _date); |
|
120 } |
|
121 |
|
122 StringID VehicleInTheWayErrMsg(const Vehicle* v) |
|
123 { |
|
124 switch (v->type) { |
|
125 case VEH_Train: return STR_8803_TRAIN_IN_THE_WAY; |
|
126 case VEH_Road: return STR_9000_ROAD_VEHICLE_IN_THE_WAY; |
|
127 case VEH_Aircraft: return STR_A015_AIRCRAFT_IN_THE_WAY; |
|
128 default: return STR_980E_SHIP_IN_THE_WAY; |
|
129 } |
|
130 } |
|
131 |
|
132 static void *EnsureNoVehicleProc(Vehicle *v, void *data) |
|
133 { |
|
134 if (v->tile != *(const TileIndex*)data || v->type == VEH_Disaster) |
|
135 return NULL; |
|
136 |
|
137 _error_message = VehicleInTheWayErrMsg(v); |
|
138 return v; |
|
139 } |
|
140 |
|
141 bool EnsureNoVehicle(TileIndex tile) |
|
142 { |
|
143 return VehicleFromPos(tile, &tile, EnsureNoVehicleProc) == NULL; |
|
144 } |
|
145 |
|
146 static void *EnsureNoVehicleProcZ(Vehicle *v, void *data) |
|
147 { |
|
148 const TileInfo *ti = data; |
|
149 |
|
150 if (v->tile != ti->tile || v->type == VEH_Disaster) return NULL; |
|
151 if (v->z_pos > ti->z) return NULL; |
|
152 |
|
153 _error_message = VehicleInTheWayErrMsg(v); |
|
154 return v; |
|
155 } |
|
156 |
|
157 |
|
158 bool EnsureNoVehicleOnGround(TileIndex tile) |
|
159 { |
|
160 TileInfo ti; |
|
161 |
|
162 ti.tile = tile; |
|
163 ti.z = GetTileMaxZ(tile); |
|
164 return VehicleFromPos(tile, &ti, EnsureNoVehicleProcZ) == NULL; |
|
165 } |
|
166 |
|
167 Vehicle *FindVehicleOnTileZ(TileIndex tile, byte z) |
|
168 { |
|
169 TileInfo ti; |
|
170 |
|
171 ti.tile = tile; |
|
172 ti.z = z; |
|
173 |
|
174 return VehicleFromPos(tile, &ti, EnsureNoVehicleProcZ); |
|
175 } |
|
176 |
|
177 Vehicle *FindVehicleBetween(TileIndex from, TileIndex to, byte z) |
|
178 { |
|
179 int x1 = TileX(from); |
|
180 int y1 = TileY(from); |
|
181 int x2 = TileX(to); |
|
182 int y2 = TileY(to); |
|
183 Vehicle *veh; |
|
184 |
|
185 /* Make sure x1 < x2 or y1 < y2 */ |
|
186 if (x1 > x2 || y1 > y2) { |
|
187 intswap(x1,x2); |
|
188 intswap(y1,y2); |
|
189 } |
|
190 FOR_ALL_VEHICLES(veh) { |
|
191 if ((veh->type == VEH_Train || veh->type == VEH_Road) && (z==0xFF || veh->z_pos == z)) { |
|
192 if ((veh->x_pos>>4) >= x1 && (veh->x_pos>>4) <= x2 && |
|
193 (veh->y_pos>>4) >= y1 && (veh->y_pos>>4) <= y2) { |
|
194 return veh; |
|
195 } |
|
196 } |
|
197 } |
|
198 return NULL; |
|
199 } |
|
200 |
|
201 |
|
202 static void UpdateVehiclePosHash(Vehicle* v, int x, int y); |
|
203 |
|
204 void VehiclePositionChanged(Vehicle *v) |
|
205 { |
|
206 int img = v->cur_image; |
|
207 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos); |
|
208 const Sprite* spr = GetSprite(img); |
|
209 |
|
210 pt.x += spr->x_offs; |
|
211 pt.y += spr->y_offs; |
|
212 |
|
213 UpdateVehiclePosHash(v, pt.x, pt.y); |
|
214 |
|
215 v->left_coord = pt.x; |
|
216 v->top_coord = pt.y; |
|
217 v->right_coord = pt.x + spr->width + 2; |
|
218 v->bottom_coord = pt.y + spr->height + 2; |
|
219 } |
|
220 |
|
221 // Called after load to update coordinates |
|
222 void AfterLoadVehicles(void) |
|
223 { |
|
224 Vehicle *v; |
|
225 |
|
226 FOR_ALL_VEHICLES(v) { |
|
227 v->first = NULL; |
|
228 if (v->type == VEH_Train) v->u.rail.first_engine = INVALID_ENGINE; |
|
229 } |
|
230 |
|
231 FOR_ALL_VEHICLES(v) { |
|
232 if (v->type == VEH_Train && (IsFrontEngine(v) || IsFreeWagon(v))) |
|
233 TrainConsistChanged(v); |
|
234 } |
|
235 |
|
236 FOR_ALL_VEHICLES(v) { |
|
237 switch (v->type) { |
|
238 case VEH_Train: v->cur_image = GetTrainImage(v, v->direction); break; |
|
239 case VEH_Road: v->cur_image = GetRoadVehImage(v, v->direction); break; |
|
240 case VEH_Ship: v->cur_image = GetShipImage(v, v->direction); break; |
|
241 case VEH_Aircraft: |
|
242 if (v->subtype == 0 || v->subtype == 2) { |
|
243 v->cur_image = GetAircraftImage(v, v->direction); |
|
244 if (v->next != NULL) v->next->cur_image = v->cur_image; |
|
245 } |
|
246 break; |
|
247 default: break; |
|
248 } |
|
249 |
|
250 v->left_coord = INVALID_COORD; |
|
251 VehiclePositionChanged(v); |
|
252 } |
|
253 } |
|
254 |
|
255 static Vehicle *InitializeVehicle(Vehicle *v) |
|
256 { |
|
257 VehicleID index = v->index; |
|
258 memset(v, 0, sizeof(Vehicle)); |
|
259 v->index = index; |
|
260 |
|
261 assert(v->orders == NULL); |
|
262 |
|
263 v->left_coord = INVALID_COORD; |
|
264 v->first = NULL; |
|
265 v->next = NULL; |
|
266 v->next_hash = NULL; |
|
267 v->string_id = 0; |
|
268 v->next_shared = NULL; |
|
269 v->prev_shared = NULL; |
|
270 v->depot_list = NULL; |
|
271 v->random_bits = 0; |
|
272 return v; |
|
273 } |
|
274 |
|
275 /** |
|
276 * Get a value for a vehicle's random_bits. |
|
277 * @return A random value from 0 to 255. |
|
278 */ |
|
279 byte VehicleRandomBits(void) |
|
280 { |
|
281 return GB(Random(), 0, 8); |
|
282 } |
|
283 |
|
284 Vehicle *ForceAllocateSpecialVehicle(void) |
|
285 { |
|
286 /* This stays a strange story.. there should always be room for special |
|
287 * vehicles (special effects all over the map), but with 65k of vehicles |
|
288 * is this realistic to double-check for that? For now we just reserve |
|
289 * BLOCKS_FOR_SPECIAL_VEHICLES times block_size vehicles that may only |
|
290 * be used for special vehicles.. should work nicely :) */ |
|
291 |
|
292 Vehicle *v; |
|
293 |
|
294 /* We don't use FOR_ALL here, because FOR_ALL skips invalid items. |
|
295 * TODO - This is just a temporary stage, this will be removed. */ |
|
296 for (v = GetVehicle(0); v != NULL; v = (v->index + 1U < GetVehiclePoolSize()) ? GetVehicle(v->index + 1) : NULL) { |
|
297 /* No more room for the special vehicles, return NULL */ |
|
298 if (v->index >= (1 << Vehicle_POOL_BLOCK_SIZE_BITS) * BLOCKS_FOR_SPECIAL_VEHICLES) |
|
299 return NULL; |
|
300 |
|
301 if (!IsValidVehicle(v)) return InitializeVehicle(v); |
|
302 } |
|
303 |
|
304 return NULL; |
|
305 } |
|
306 |
|
307 /* |
|
308 * finds a free vehicle in the memory or allocates a new one |
|
309 * returns a pointer to the first free vehicle or NULL if all vehicles are in use |
|
310 * *skip_vehicles is an offset to where in the array we should begin looking |
|
311 * this is to avoid looping though the same vehicles more than once after we learned that they are not free |
|
312 * this feature is used by AllocateVehicles() since it need to allocate more than one and when |
|
313 * another block is added to _Vehicle_pool, since we only do that when we know it's already full |
|
314 */ |
|
315 static Vehicle *AllocateSingleVehicle(VehicleID *skip_vehicles) |
|
316 { |
|
317 /* See note by ForceAllocateSpecialVehicle() why we skip the |
|
318 * first blocks */ |
|
319 Vehicle *v; |
|
320 const int offset = (1 << Vehicle_POOL_BLOCK_SIZE_BITS) * BLOCKS_FOR_SPECIAL_VEHICLES; |
|
321 |
|
322 /* We don't use FOR_ALL here, because FOR_ALL skips invalid items. |
|
323 * TODO - This is just a temporary stage, this will be removed. */ |
|
324 if (*skip_vehicles < (_Vehicle_pool.total_items - offset)) { // make sure the offset in the array is not larger than the array itself |
|
325 for (v = GetVehicle(offset + *skip_vehicles); v != NULL; v = (v->index + 1U < GetVehiclePoolSize()) ? GetVehicle(v->index + 1) : NULL) { |
|
326 (*skip_vehicles)++; |
|
327 if (!IsValidVehicle(v)) return InitializeVehicle(v); |
|
328 } |
|
329 } |
|
330 |
|
331 /* Check if we can add a block to the pool */ |
|
332 if (AddBlockToPool(&_Vehicle_pool)) |
|
333 return AllocateSingleVehicle(skip_vehicles); |
|
334 |
|
335 return NULL; |
|
336 } |
|
337 |
|
338 |
|
339 Vehicle *AllocateVehicle(void) |
|
340 { |
|
341 VehicleID counter = 0; |
|
342 return AllocateSingleVehicle(&counter); |
|
343 } |
|
344 |
|
345 |
|
346 /** Allocates a lot of vehicles and frees them again |
|
347 * @param vl pointer to an array of vehicles to get allocated. Can be NULL if the vehicles aren't needed (makes it test only) |
|
348 * @param num number of vehicles to allocate room for |
|
349 * @return true if there is room to allocate all the vehicles |
|
350 */ |
|
351 bool AllocateVehicles(Vehicle **vl, int num) |
|
352 { |
|
353 int i; |
|
354 Vehicle *v; |
|
355 VehicleID counter = 0; |
|
356 |
|
357 for (i = 0; i != num; i++) { |
|
358 v = AllocateSingleVehicle(&counter); |
|
359 if (v == NULL) { |
|
360 return false; |
|
361 } |
|
362 if (vl != NULL) { |
|
363 vl[i] = v; |
|
364 } |
|
365 } |
|
366 |
|
367 return true; |
|
368 } |
|
369 |
|
370 |
|
371 static Vehicle *_vehicle_position_hash[0x1000]; |
|
372 |
|
373 void *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc) |
|
374 { |
|
375 Point pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, 0); |
|
376 |
|
377 // The hash area to scan |
|
378 const int xl = GB(pt.x - 174, 7, 6); |
|
379 const int xu = GB(pt.x + 104, 7, 6); |
|
380 const int yl = GB(pt.y - 294, 6, 6) << 6; |
|
381 const int yu = GB(pt.y + 56, 6, 6) << 6; |
|
382 |
|
383 int x; |
|
384 int y; |
|
385 |
|
386 for (y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) { |
|
387 for (x = xl;; x = (x + 1) & 0x3F) { |
|
388 Vehicle *v = _vehicle_position_hash[(x + y) & 0xFFFF]; |
|
389 |
|
390 while (v != NULL) { |
|
391 void* a = proc(v, data); |
|
392 |
|
393 if (a != NULL) return a; |
|
394 v = v->next_hash; |
|
395 } |
|
396 |
|
397 if (x == xu) break; |
|
398 } |
|
399 |
|
400 if (y == yu) break; |
|
401 } |
|
402 return NULL; |
|
403 } |
|
404 |
|
405 |
|
406 static void UpdateVehiclePosHash(Vehicle* v, int x, int y) |
|
407 { |
|
408 Vehicle **old_hash, **new_hash; |
|
409 int old_x = v->left_coord; |
|
410 int old_y = v->top_coord; |
|
411 |
|
412 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x,y)]; |
|
413 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)]; |
|
414 |
|
415 if (old_hash == new_hash) return; |
|
416 |
|
417 /* remove from hash table? */ |
|
418 if (old_hash != NULL) { |
|
419 Vehicle *last = NULL; |
|
420 Vehicle *u = *old_hash; |
|
421 while (u != v) { |
|
422 last = u; |
|
423 u = u->next_hash; |
|
424 assert(u != NULL); |
|
425 } |
|
426 |
|
427 if (last == NULL) { |
|
428 *old_hash = v->next_hash; |
|
429 } else { |
|
430 last->next_hash = v->next_hash; |
|
431 } |
|
432 } |
|
433 |
|
434 /* insert into hash table? */ |
|
435 if (new_hash != NULL) { |
|
436 v->next_hash = *new_hash; |
|
437 *new_hash = v; |
|
438 } |
|
439 } |
|
440 |
|
441 void ResetVehiclePosHash(void) |
|
442 { |
|
443 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash)); |
|
444 } |
|
445 |
|
446 void InitializeVehicles(void) |
|
447 { |
|
448 uint i; |
|
449 |
|
450 /* Clean the vehicle pool, and reserve enough blocks |
|
451 * for the special vehicles, plus one for all the other |
|
452 * vehicles (which is increased on-the-fly) */ |
|
453 CleanPool(&_Vehicle_pool); |
|
454 AddBlockToPool(&_Vehicle_pool); |
|
455 for (i = 0; i < BLOCKS_FOR_SPECIAL_VEHICLES; i++) { |
|
456 AddBlockToPool(&_Vehicle_pool); |
|
457 } |
|
458 |
|
459 ResetVehiclePosHash(); |
|
460 } |
|
461 |
|
462 Vehicle *GetLastVehicleInChain(Vehicle *v) |
|
463 { |
|
464 while (v->next != NULL) v = v->next; |
|
465 return v; |
|
466 } |
|
467 |
|
468 /** Finds the previous vehicle in a chain, by a brute force search. |
|
469 * This old function is REALLY slow because it searches through all vehicles to |
|
470 * find the previous vehicle, but if v->first has not been set, then this function |
|
471 * will need to be used to find the previous one. This function should never be |
|
472 * called by anything but GetFirstVehicleInChain |
|
473 */ |
|
474 static Vehicle *GetPrevVehicleInChain_bruteforce(const Vehicle *v) |
|
475 { |
|
476 Vehicle *u; |
|
477 |
|
478 FOR_ALL_VEHICLES(u) if (u->type == VEH_Train && u->next == v) return u; |
|
479 |
|
480 return NULL; |
|
481 } |
|
482 |
|
483 /** Find the previous vehicle in a chain, by using the v->first cache. |
|
484 * While this function is fast, it cannot be used in the GetFirstVehicleInChain |
|
485 * function, otherwise you'll end up in an infinite loop call |
|
486 */ |
|
487 Vehicle *GetPrevVehicleInChain(const Vehicle *v) |
|
488 { |
|
489 Vehicle *u; |
|
490 assert(v != NULL); |
|
491 |
|
492 u = GetFirstVehicleInChain(v); |
|
493 |
|
494 // Check to see if this is the first |
|
495 if (v == u) return NULL; |
|
496 |
|
497 for (; u->next != v; u = u->next) assert(u->next != NULL); |
|
498 |
|
499 return u; |
|
500 } |
|
501 |
|
502 /** Finds the first vehicle in a chain. |
|
503 * This function reads out the v->first cache. Should the cache be dirty, |
|
504 * it determines the first vehicle in a chain, and updates the cache. |
|
505 */ |
|
506 Vehicle *GetFirstVehicleInChain(const Vehicle *v) |
|
507 { |
|
508 Vehicle* u; |
|
509 |
|
510 assert(v != NULL); |
|
511 |
|
512 if (v->first != NULL) { |
|
513 if (IsFrontEngine(v->first) || IsFreeWagon(v->first)) return v->first; |
|
514 |
|
515 DEBUG(misc, 0, "v->first cache faulty. We shouldn't be here, rebuilding cache!"); |
|
516 } |
|
517 |
|
518 /* It is the fact (currently) that newly built vehicles do not have |
|
519 * their ->first pointer set. When this is the case, go up to the |
|
520 * first engine and set the pointers correctly. Also the first pointer |
|
521 * is not saved in a savegame, so this has to be fixed up after loading */ |
|
522 |
|
523 /* Find the 'locomotive' or the first wagon in a chain */ |
|
524 while ((u = GetPrevVehicleInChain_bruteforce(v)) != NULL) v = u; |
|
525 |
|
526 /* Set the first pointer of all vehicles in that chain to the first wagon */ |
|
527 if (IsFrontEngine(v) || IsFreeWagon(v)) |
|
528 for (u = (Vehicle *)v; u != NULL; u = u->next) u->first = (Vehicle *)v; |
|
529 |
|
530 return (Vehicle*)v; |
|
531 } |
|
532 |
|
533 uint CountVehiclesInChain(const Vehicle* v) |
|
534 { |
|
535 uint count = 0; |
|
536 do count++; while ((v = v->next) != NULL); |
|
537 return count; |
|
538 } |
|
539 |
|
540 /** Check if a vehicle is counted in num_engines in each player struct |
|
541 * @param *v Vehicle to test |
|
542 * @return true if the vehicle is counted in num_engines |
|
543 */ |
|
544 bool IsEngineCountable(const Vehicle *v) |
|
545 { |
|
546 switch (v->type) { |
|
547 case VEH_Aircraft: return (v->subtype <= 2); // don't count plane shadows and helicopter rotors |
|
548 case VEH_Train: |
|
549 return !IsArticulatedPart(v) && // tenders and other articulated parts |
|
550 (!IsMultiheaded(v) || IsTrainEngine(v)); // rear parts of multiheaded engines |
|
551 case VEH_Road: |
|
552 case VEH_Ship: |
|
553 return true; |
|
554 default: return false; // Only count player buildable vehicles |
|
555 } |
|
556 } |
|
557 |
|
558 void DestroyVehicle(Vehicle *v) |
|
559 { |
|
560 if (IsEngineCountable(v)) GetPlayer(v->owner)->num_engines[v->engine_type]--; |
|
561 |
|
562 DeleteVehicleNews(v->index, INVALID_STRING_ID); |
|
563 |
|
564 DeleteName(v->string_id); |
|
565 if (v->type == VEH_Road) ClearSlot(v); |
|
566 |
|
567 if (v->type != VEH_Train || (v->type == VEH_Train && (IsFrontEngine(v) || IsFreeWagon(v)))) { |
|
568 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); |
|
569 } |
|
570 |
|
571 UpdateVehiclePosHash(v, INVALID_COORD, 0); |
|
572 v->next_hash = NULL; |
|
573 if (v->orders != NULL) DeleteVehicleOrders(v); |
|
574 |
|
575 /* Now remove any artic part. This will trigger an other |
|
576 * destroy vehicle, which on his turn can remove any |
|
577 * other artic parts. */ |
|
578 if (EngineHasArticPart(v)) DeleteVehicle(v->next); |
|
579 } |
|
580 |
|
581 void DeleteVehicleChain(Vehicle *v) |
|
582 { |
|
583 do { |
|
584 Vehicle *u = v; |
|
585 v = GetNextVehicle(v); |
|
586 DeleteVehicle(u); |
|
587 } while (v != NULL); |
|
588 } |
|
589 |
|
590 |
|
591 void Aircraft_Tick(Vehicle *v); |
|
592 void RoadVeh_Tick(Vehicle *v); |
|
593 void Ship_Tick(Vehicle *v); |
|
594 void Train_Tick(Vehicle *v); |
|
595 static void EffectVehicle_Tick(Vehicle *v); |
|
596 void DisasterVehicle_Tick(Vehicle *v); |
|
597 static int32 MaybeReplaceVehicle(Vehicle *v, bool check, bool display_costs); |
|
598 |
|
599 // head of the linked list to tell what vehicles that visited a depot in a tick |
|
600 static Vehicle* _first_veh_in_depot_list; |
|
601 |
|
602 /** Adds a vehicle to the list of vehicles, that visited a depot this tick |
|
603 * @param *v vehicle to add |
|
604 */ |
|
605 void VehicleEnteredDepotThisTick(Vehicle *v) |
|
606 { |
|
607 // we need to set v->leave_depot_instantly as we have no control of it's contents at this time |
|
608 if (HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT) && !HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS) && v->current_order.type == OT_GOTO_DEPOT) { |
|
609 // we keep the vehicle in the depot since the user ordered it to stay |
|
610 v->leave_depot_instantly = false; |
|
611 } else { |
|
612 // the vehicle do not plan on stopping in the depot, so we stop it to ensure that it will not reserve the path |
|
613 // out of the depot before we might autoreplace it to a different engine. The new engine would not own the reserved path |
|
614 // we store that we stopped the vehicle, so autoreplace can start it again |
|
615 v->vehstatus |= VS_STOPPED; |
|
616 v->leave_depot_instantly = true; |
|
617 } |
|
618 |
|
619 if (_first_veh_in_depot_list == NULL) { |
|
620 _first_veh_in_depot_list = v; |
|
621 } else { |
|
622 Vehicle *w = _first_veh_in_depot_list; |
|
623 while (w->depot_list != NULL) w = w->depot_list; |
|
624 w->depot_list = v; |
|
625 } |
|
626 } |
|
627 |
|
628 typedef void VehicleTickProc(Vehicle*); |
|
629 static VehicleTickProc* _vehicle_tick_procs[] = { |
|
630 Train_Tick, |
|
631 RoadVeh_Tick, |
|
632 Ship_Tick, |
|
633 Aircraft_Tick, |
|
634 EffectVehicle_Tick, |
|
635 DisasterVehicle_Tick, |
|
636 }; |
|
637 |
|
638 void CallVehicleTicks(void) |
|
639 { |
|
640 Vehicle *v; |
|
641 |
|
642 #ifdef ENABLE_NETWORK |
|
643 // hotfix for desync problem: |
|
644 // for MP games invalidate the YAPF cache every tick to keep it exactly the same on the server and all clients |
|
645 if (_networking) { |
|
646 YapfNotifyTrackLayoutChange(0, 0); |
|
647 } |
|
648 #endif //ENABLE_NETWORK |
|
649 |
|
650 _first_veh_in_depot_list = NULL; // now we are sure it's initialized at the start of each tick |
|
651 |
|
652 FOR_ALL_VEHICLES(v) { |
|
653 _vehicle_tick_procs[v->type - 0x10](v); |
|
654 |
|
655 switch (v->type) { |
|
656 case VEH_Train: |
|
657 case VEH_Road: |
|
658 case VEH_Aircraft: |
|
659 case VEH_Ship: |
|
660 if (v->type == VEH_Train && IsTrainWagon(v)) continue; |
|
661 if (v->type == VEH_Aircraft && v->subtype > 0) continue; |
|
662 |
|
663 v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed; |
|
664 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */ |
|
665 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING); |
|
666 |
|
667 /* Play an alterate running sound every 16 ticks */ |
|
668 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16); |
|
669 } |
|
670 } |
|
671 |
|
672 // now we handle all the vehicles that entered a depot this tick |
|
673 v = _first_veh_in_depot_list; |
|
674 while (v != NULL) { |
|
675 Vehicle *w = v->depot_list; |
|
676 v->depot_list = NULL; // it should always be NULL at the end of each tick |
|
677 MaybeReplaceVehicle(v, false, true); |
|
678 v = w; |
|
679 } |
|
680 } |
|
681 |
|
682 static bool CanFillVehicle_FullLoadAny(Vehicle *v) |
|
683 { |
|
684 uint32 full = 0, not_full = 0; |
|
685 bool keep_loading = false; |
|
686 const GoodsEntry *ge = GetStation(v->last_station_visited)->goods; |
|
687 |
|
688 //special handling of aircraft |
|
689 |
|
690 //if the aircraft carries passengers and is NOT full, then |
|
691 //continue loading, no matter how much mail is in |
|
692 if (v->type == VEH_Aircraft && |
|
693 v->cargo_type == CT_PASSENGERS && |
|
694 v->cargo_cap != v->cargo_count) { |
|
695 return true; |
|
696 } |
|
697 |
|
698 // patch should return "true" to continue loading, i.e. when there is no cargo type that is fully loaded. |
|
699 do { |
|
700 //Should never happen, but just in case future additions change this |
|
701 assert(v->cargo_type<32); |
|
702 |
|
703 if (v->cargo_cap != 0) { |
|
704 uint32 mask = 1 << v->cargo_type; |
|
705 |
|
706 if (v->cargo_cap == v->cargo_count) { |
|
707 full |= mask; |
|
708 } else if (GB(ge[v->cargo_type].waiting_acceptance, 0, 12) > 0 || |
|
709 (HASBIT(v->load_status, LS_CARGO_UNLOADING) && (ge[v->cargo_type].waiting_acceptance & 0x8000))) { |
|
710 /* If there is any cargo waiting, or this vehicle is still unloading |
|
711 * and the station accepts the cargo, don't leave the station. */ |
|
712 keep_loading = true; |
|
713 } else { |
|
714 not_full |= mask; |
|
715 } |
|
716 } |
|
717 } while ((v = v->next) != NULL); |
|
718 |
|
719 // continue loading if there is a non full cargo type and no cargo type that is full |
|
720 return keep_loading || (not_full && (full & ~not_full) == 0); |
|
721 } |
|
722 |
|
723 bool CanFillVehicle(Vehicle *v) |
|
724 { |
|
725 TileIndex tile = v->tile; |
|
726 |
|
727 if (IsTileType(tile, MP_STATION) || |
|
728 (v->type == VEH_Ship && ( |
|
729 IsTileType(TILE_ADDXY(tile, 1, 0), MP_STATION) || |
|
730 IsTileType(TILE_ADDXY(tile, -1, 0), MP_STATION) || |
|
731 IsTileType(TILE_ADDXY(tile, 0, 1), MP_STATION) || |
|
732 IsTileType(TILE_ADDXY(tile, 0, -1), MP_STATION) || |
|
733 IsTileType(TILE_ADDXY(tile, -2, 0), MP_STATION) |
|
734 ))) { |
|
735 |
|
736 // If patch is active, use alternative CanFillVehicle-function |
|
737 if (_patches.full_load_any && v->current_order.flags & OF_FULL_LOAD) return CanFillVehicle_FullLoadAny(v); |
|
738 |
|
739 do { |
|
740 if (v->cargo_count != v->cargo_cap) return true; |
|
741 } while ((v = v->next) != NULL); |
|
742 } |
|
743 return false; |
|
744 } |
|
745 |
|
746 /** Check if a given engine type can be refitted to a given cargo |
|
747 * @param engine_type Engine type to check |
|
748 * @param cid_to check refit to this cargo-type |
|
749 * @return true if it is possible, false otherwise |
|
750 */ |
|
751 bool CanRefitTo(EngineID engine_type, CargoID cid_to) |
|
752 { |
|
753 CargoID cid = _global_cargo_id[_opt_ptr->landscape][cid_to]; |
|
754 return HASBIT(EngInfo(engine_type)->refit_mask, cid); |
|
755 } |
|
756 |
|
757 /** Find the first cargo type that an engine can be refitted to. |
|
758 * @param engine Which engine to find cargo for. |
|
759 * @return A climate dependent cargo type. CT_INVALID is returned if not refittable. |
|
760 */ |
|
761 CargoID FindFirstRefittableCargo(EngineID engine_type) |
|
762 { |
|
763 CargoID cid; |
|
764 uint32 refit_mask = EngInfo(engine_type)->refit_mask; |
|
765 |
|
766 if (refit_mask != 0) { |
|
767 for (cid = CT_PASSENGERS; cid < NUM_CARGO; cid++) { |
|
768 if (HASBIT(refit_mask, _global_cargo_id[_opt_ptr->landscape][cid])) return cid; |
|
769 } |
|
770 } |
|
771 |
|
772 return CT_INVALID; |
|
773 } |
|
774 |
|
775 /** Learn the price of refitting a certain engine |
|
776 * @param engine Which engine to refit |
|
777 * @return Price for refitting |
|
778 */ |
|
779 int32 GetRefitCost(EngineID engine_type) |
|
780 { |
|
781 int32 base_cost = 0; |
|
782 |
|
783 switch (GetEngine(engine_type)->type) { |
|
784 case VEH_Ship: base_cost = _price.ship_base; break; |
|
785 case VEH_Road: base_cost = _price.roadveh_base; break; |
|
786 case VEH_Aircraft: base_cost = _price.aircraft_base; break; |
|
787 case VEH_Train: |
|
788 base_cost = 2 * ((RailVehInfo(engine_type)->flags & RVI_WAGON) ? |
|
789 _price.build_railwagon : _price.build_railvehicle); |
|
790 break; |
|
791 default: NOT_REACHED(); break; |
|
792 } |
|
793 return (EngInfo(engine_type)->refit_cost * base_cost) >> 10; |
|
794 } |
|
795 |
|
796 static void DoDrawVehicle(const Vehicle *v) |
|
797 { |
|
798 uint32 image = v->cur_image; |
|
799 |
|
800 if (v->vehstatus & VS_SHADOW) { |
|
801 MAKE_TRANSPARENT(image); |
|
802 } else if (v->vehstatus & VS_DEFPAL) { |
|
803 image |= (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); |
|
804 } |
|
805 |
|
806 AddSortableSpriteToDraw(image, v->x_pos + v->x_offs, v->y_pos + v->y_offs, |
|
807 v->sprite_width, v->sprite_height, v->z_height, v->z_pos); |
|
808 } |
|
809 |
|
810 void ViewportAddVehicles(DrawPixelInfo *dpi) |
|
811 { |
|
812 // The bounding rectangle |
|
813 const int l = dpi->left; |
|
814 const int r = dpi->left + dpi->width; |
|
815 const int t = dpi->top; |
|
816 const int b = dpi->top + dpi->height; |
|
817 |
|
818 // The hash area to scan |
|
819 const int xl = GB(l - 70, 7, 6); |
|
820 const int xu = GB(r, 7, 6); |
|
821 const int yl = GB(t - 70, 6, 6) << 6; |
|
822 const int yu = GB(b, 6, 6) << 6; |
|
823 |
|
824 int x; |
|
825 int y; |
|
826 |
|
827 for (y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) { |
|
828 for (x = xl;; x = (x + 1) & 0x3F) { |
|
829 const Vehicle *v = _vehicle_position_hash[(x + y) & 0xFFFF]; |
|
830 |
|
831 while (v != NULL) { |
|
832 if (!(v->vehstatus & VS_HIDDEN) && |
|
833 l <= v->right_coord && |
|
834 t <= v->bottom_coord && |
|
835 r >= v->left_coord && |
|
836 b >= v->top_coord) { |
|
837 DoDrawVehicle(v); |
|
838 } |
|
839 v = v->next_hash; |
|
840 } |
|
841 |
|
842 if (x == xu) break; |
|
843 } |
|
844 |
|
845 if (y == yu) break; |
|
846 } |
|
847 } |
|
848 |
|
849 static void ChimneySmokeInit(Vehicle *v) |
|
850 { |
|
851 uint32 r = Random(); |
|
852 v->cur_image = SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3); |
|
853 v->progress = GB(r, 16, 3); |
|
854 } |
|
855 |
|
856 static void ChimneySmokeTick(Vehicle *v) |
|
857 { |
|
858 if (v->progress > 0) { |
|
859 v->progress--; |
|
860 } else { |
|
861 TileIndex tile; |
|
862 |
|
863 BeginVehicleMove(v); |
|
864 |
|
865 tile = TileVirtXY(v->x_pos, v->y_pos); |
|
866 if (!IsTileType(tile, MP_INDUSTRY)) { |
|
867 EndVehicleMove(v); |
|
868 DeleteVehicle(v); |
|
869 return; |
|
870 } |
|
871 |
|
872 if (v->cur_image != SPR_CHIMNEY_SMOKE_7) { |
|
873 v->cur_image++; |
|
874 } else { |
|
875 v->cur_image = SPR_CHIMNEY_SMOKE_0; |
|
876 } |
|
877 v->progress = 7; |
|
878 VehiclePositionChanged(v); |
|
879 EndVehicleMove(v); |
|
880 } |
|
881 } |
|
882 |
|
883 static void SteamSmokeInit(Vehicle *v) |
|
884 { |
|
885 v->cur_image = SPR_STEAM_SMOKE_0; |
|
886 v->progress = 12; |
|
887 } |
|
888 |
|
889 static void SteamSmokeTick(Vehicle *v) |
|
890 { |
|
891 bool moved = false; |
|
892 |
|
893 BeginVehicleMove(v); |
|
894 |
|
895 v->progress++; |
|
896 |
|
897 if ((v->progress & 7) == 0) { |
|
898 v->z_pos++; |
|
899 moved = true; |
|
900 } |
|
901 |
|
902 if ((v->progress & 0xF) == 4) { |
|
903 if (v->cur_image != SPR_STEAM_SMOKE_4) { |
|
904 v->cur_image++; |
|
905 } else { |
|
906 EndVehicleMove(v); |
|
907 DeleteVehicle(v); |
|
908 return; |
|
909 } |
|
910 moved = true; |
|
911 } |
|
912 |
|
913 if (moved) { |
|
914 VehiclePositionChanged(v); |
|
915 EndVehicleMove(v); |
|
916 } |
|
917 } |
|
918 |
|
919 static void DieselSmokeInit(Vehicle *v) |
|
920 { |
|
921 v->cur_image = SPR_DIESEL_SMOKE_0; |
|
922 v->progress = 0; |
|
923 } |
|
924 |
|
925 static void DieselSmokeTick(Vehicle *v) |
|
926 { |
|
927 v->progress++; |
|
928 |
|
929 if ((v->progress & 3) == 0) { |
|
930 BeginVehicleMove(v); |
|
931 v->z_pos++; |
|
932 VehiclePositionChanged(v); |
|
933 EndVehicleMove(v); |
|
934 } else if ((v->progress & 7) == 1) { |
|
935 BeginVehicleMove(v); |
|
936 if (v->cur_image != SPR_DIESEL_SMOKE_5) { |
|
937 v->cur_image++; |
|
938 VehiclePositionChanged(v); |
|
939 EndVehicleMove(v); |
|
940 } else { |
|
941 EndVehicleMove(v); |
|
942 DeleteVehicle(v); |
|
943 } |
|
944 } |
|
945 } |
|
946 |
|
947 static void ElectricSparkInit(Vehicle *v) |
|
948 { |
|
949 v->cur_image = SPR_ELECTRIC_SPARK_0; |
|
950 v->progress = 1; |
|
951 } |
|
952 |
|
953 static void ElectricSparkTick(Vehicle *v) |
|
954 { |
|
955 if (v->progress < 2) { |
|
956 v->progress++; |
|
957 } else { |
|
958 v->progress = 0; |
|
959 BeginVehicleMove(v); |
|
960 if (v->cur_image != SPR_ELECTRIC_SPARK_5) { |
|
961 v->cur_image++; |
|
962 VehiclePositionChanged(v); |
|
963 EndVehicleMove(v); |
|
964 } else { |
|
965 EndVehicleMove(v); |
|
966 DeleteVehicle(v); |
|
967 } |
|
968 } |
|
969 } |
|
970 |
|
971 static void SmokeInit(Vehicle *v) |
|
972 { |
|
973 v->cur_image = SPR_SMOKE_0; |
|
974 v->progress = 12; |
|
975 } |
|
976 |
|
977 static void SmokeTick(Vehicle *v) |
|
978 { |
|
979 bool moved = false; |
|
980 |
|
981 BeginVehicleMove(v); |
|
982 |
|
983 v->progress++; |
|
984 |
|
985 if ((v->progress & 3) == 0) { |
|
986 v->z_pos++; |
|
987 moved = true; |
|
988 } |
|
989 |
|
990 if ((v->progress & 0xF) == 4) { |
|
991 if (v->cur_image != SPR_SMOKE_4) { |
|
992 v->cur_image++; |
|
993 } else { |
|
994 EndVehicleMove(v); |
|
995 DeleteVehicle(v); |
|
996 return; |
|
997 } |
|
998 moved = true; |
|
999 } |
|
1000 |
|
1001 if (moved) { |
|
1002 VehiclePositionChanged(v); |
|
1003 EndVehicleMove(v); |
|
1004 } |
|
1005 } |
|
1006 |
|
1007 static void ExplosionLargeInit(Vehicle *v) |
|
1008 { |
|
1009 v->cur_image = SPR_EXPLOSION_LARGE_0; |
|
1010 v->progress = 0; |
|
1011 } |
|
1012 |
|
1013 static void ExplosionLargeTick(Vehicle *v) |
|
1014 { |
|
1015 v->progress++; |
|
1016 if ((v->progress & 3) == 0) { |
|
1017 BeginVehicleMove(v); |
|
1018 if (v->cur_image != SPR_EXPLOSION_LARGE_F) { |
|
1019 v->cur_image++; |
|
1020 VehiclePositionChanged(v); |
|
1021 EndVehicleMove(v); |
|
1022 } else { |
|
1023 EndVehicleMove(v); |
|
1024 DeleteVehicle(v); |
|
1025 } |
|
1026 } |
|
1027 } |
|
1028 |
|
1029 static void BreakdownSmokeInit(Vehicle *v) |
|
1030 { |
|
1031 v->cur_image = SPR_BREAKDOWN_SMOKE_0; |
|
1032 v->progress = 0; |
|
1033 } |
|
1034 |
|
1035 static void BreakdownSmokeTick(Vehicle *v) |
|
1036 { |
|
1037 v->progress++; |
|
1038 if ((v->progress & 7) == 0) { |
|
1039 BeginVehicleMove(v); |
|
1040 if (v->cur_image != SPR_BREAKDOWN_SMOKE_3) { |
|
1041 v->cur_image++; |
|
1042 } else { |
|
1043 v->cur_image = SPR_BREAKDOWN_SMOKE_0; |
|
1044 } |
|
1045 VehiclePositionChanged(v); |
|
1046 EndVehicleMove(v); |
|
1047 } |
|
1048 |
|
1049 v->u.special.unk0--; |
|
1050 if (v->u.special.unk0 == 0) { |
|
1051 BeginVehicleMove(v); |
|
1052 EndVehicleMove(v); |
|
1053 DeleteVehicle(v); |
|
1054 } |
|
1055 } |
|
1056 |
|
1057 static void ExplosionSmallInit(Vehicle *v) |
|
1058 { |
|
1059 v->cur_image = SPR_EXPLOSION_SMALL_0; |
|
1060 v->progress = 0; |
|
1061 } |
|
1062 |
|
1063 static void ExplosionSmallTick(Vehicle *v) |
|
1064 { |
|
1065 v->progress++; |
|
1066 if ((v->progress & 3) == 0) { |
|
1067 BeginVehicleMove(v); |
|
1068 if (v->cur_image != SPR_EXPLOSION_SMALL_B) { |
|
1069 v->cur_image++; |
|
1070 VehiclePositionChanged(v); |
|
1071 EndVehicleMove(v); |
|
1072 } else { |
|
1073 EndVehicleMove(v); |
|
1074 DeleteVehicle(v); |
|
1075 } |
|
1076 } |
|
1077 } |
|
1078 |
|
1079 static void BulldozerInit(Vehicle *v) |
|
1080 { |
|
1081 v->cur_image = SPR_BULLDOZER_NE; |
|
1082 v->progress = 0; |
|
1083 v->u.special.unk0 = 0; |
|
1084 v->u.special.unk2 = 0; |
|
1085 } |
|
1086 |
|
1087 typedef struct BulldozerMovement { |
|
1088 byte direction:2; |
|
1089 byte image:2; |
|
1090 byte duration:3; |
|
1091 } BulldozerMovement; |
|
1092 |
|
1093 static const BulldozerMovement _bulldozer_movement[] = { |
|
1094 { 0, 0, 4 }, |
|
1095 { 3, 3, 4 }, |
|
1096 { 2, 2, 7 }, |
|
1097 { 0, 2, 7 }, |
|
1098 { 1, 1, 3 }, |
|
1099 { 2, 2, 7 }, |
|
1100 { 0, 2, 7 }, |
|
1101 { 1, 1, 3 }, |
|
1102 { 2, 2, 7 }, |
|
1103 { 0, 2, 7 }, |
|
1104 { 3, 3, 6 }, |
|
1105 { 2, 2, 6 }, |
|
1106 { 1, 1, 7 }, |
|
1107 { 3, 1, 7 }, |
|
1108 { 0, 0, 3 }, |
|
1109 { 1, 1, 7 }, |
|
1110 { 3, 1, 7 }, |
|
1111 { 0, 0, 3 }, |
|
1112 { 1, 1, 7 }, |
|
1113 { 3, 1, 7 } |
|
1114 }; |
|
1115 |
|
1116 static const struct { |
|
1117 int8 x; |
|
1118 int8 y; |
|
1119 } _inc_by_dir[] = { |
|
1120 { -1, 0 }, |
|
1121 { 0, 1 }, |
|
1122 { 1, 0 }, |
|
1123 { 0, -1 } |
|
1124 }; |
|
1125 |
|
1126 static void BulldozerTick(Vehicle *v) |
|
1127 { |
|
1128 v->progress++; |
|
1129 if ((v->progress & 7) == 0) { |
|
1130 const BulldozerMovement* b = &_bulldozer_movement[v->u.special.unk0]; |
|
1131 |
|
1132 BeginVehicleMove(v); |
|
1133 |
|
1134 v->cur_image = SPR_BULLDOZER_NE + b->image; |
|
1135 |
|
1136 v->x_pos += _inc_by_dir[b->direction].x; |
|
1137 v->y_pos += _inc_by_dir[b->direction].y; |
|
1138 |
|
1139 v->u.special.unk2++; |
|
1140 if (v->u.special.unk2 >= b->duration) { |
|
1141 v->u.special.unk2 = 0; |
|
1142 v->u.special.unk0++; |
|
1143 if (v->u.special.unk0 == lengthof(_bulldozer_movement)) { |
|
1144 EndVehicleMove(v); |
|
1145 DeleteVehicle(v); |
|
1146 return; |
|
1147 } |
|
1148 } |
|
1149 VehiclePositionChanged(v); |
|
1150 EndVehicleMove(v); |
|
1151 } |
|
1152 } |
|
1153 |
|
1154 static void BubbleInit(Vehicle *v) |
|
1155 { |
|
1156 v->cur_image = SPR_BUBBLE_GENERATE_0; |
|
1157 v->spritenum = 0; |
|
1158 v->progress = 0; |
|
1159 } |
|
1160 |
|
1161 typedef struct BubbleMovement { |
|
1162 int8 x:4; |
|
1163 int8 y:4; |
|
1164 int8 z:4; |
|
1165 byte image:4; |
|
1166 } BubbleMovement; |
|
1167 |
|
1168 #define MK(x, y, z, i) { x, y, z, i } |
|
1169 #define ME(i) { i, 4, 0, 0 } |
|
1170 |
|
1171 static const BubbleMovement _bubble_float_sw[] = { |
|
1172 MK(0, 0, 1, 0), |
|
1173 MK(1, 0, 1, 1), |
|
1174 MK(0, 0, 1, 0), |
|
1175 MK(1, 0, 1, 2), |
|
1176 ME(1) |
|
1177 }; |
|
1178 |
|
1179 |
|
1180 static const BubbleMovement _bubble_float_ne[] = { |
|
1181 MK( 0, 0, 1, 0), |
|
1182 MK(-1, 0, 1, 1), |
|
1183 MK( 0, 0, 1, 0), |
|
1184 MK(-1, 0, 1, 2), |
|
1185 ME(1) |
|
1186 }; |
|
1187 |
|
1188 static const BubbleMovement _bubble_float_se[] = { |
|
1189 MK(0, 0, 1, 0), |
|
1190 MK(0, 1, 1, 1), |
|
1191 MK(0, 0, 1, 0), |
|
1192 MK(0, 1, 1, 2), |
|
1193 ME(1) |
|
1194 }; |
|
1195 |
|
1196 static const BubbleMovement _bubble_float_nw[] = { |
|
1197 MK(0, 0, 1, 0), |
|
1198 MK(0, -1, 1, 1), |
|
1199 MK(0, 0, 1, 0), |
|
1200 MK(0, -1, 1, 2), |
|
1201 ME(1) |
|
1202 }; |
|
1203 |
|
1204 static const BubbleMovement _bubble_burst[] = { |
|
1205 MK(0, 0, 1, 2), |
|
1206 MK(0, 0, 1, 7), |
|
1207 MK(0, 0, 1, 8), |
|
1208 MK(0, 0, 1, 9), |
|
1209 ME(0) |
|
1210 }; |
|
1211 |
|
1212 static const BubbleMovement _bubble_absorb[] = { |
|
1213 MK(0, 0, 1, 0), |
|
1214 MK(0, 0, 1, 1), |
|
1215 MK(0, 0, 1, 0), |
|
1216 MK(0, 0, 1, 2), |
|
1217 MK(0, 0, 1, 0), |
|
1218 MK(0, 0, 1, 1), |
|
1219 MK(0, 0, 1, 0), |
|
1220 MK(0, 0, 1, 2), |
|
1221 MK(0, 0, 1, 0), |
|
1222 MK(0, 0, 1, 1), |
|
1223 MK(0, 0, 1, 0), |
|
1224 MK(0, 0, 1, 2), |
|
1225 MK(0, 0, 1, 0), |
|
1226 MK(0, 0, 1, 1), |
|
1227 MK(0, 0, 1, 0), |
|
1228 MK(0, 0, 1, 2), |
|
1229 MK(0, 0, 1, 0), |
|
1230 MK(0, 0, 1, 1), |
|
1231 MK(0, 0, 1, 0), |
|
1232 MK(0, 0, 1, 2), |
|
1233 MK(0, 0, 1, 0), |
|
1234 MK(0, 0, 1, 1), |
|
1235 MK(0, 0, 1, 0), |
|
1236 MK(0, 0, 1, 2), |
|
1237 MK(0, 0, 1, 0), |
|
1238 MK(0, 0, 1, 1), |
|
1239 MK(0, 0, 1, 0), |
|
1240 MK(0, 0, 1, 2), |
|
1241 MK(0, 0, 1, 0), |
|
1242 MK(0, 0, 1, 1), |
|
1243 MK(0, 0, 1, 0), |
|
1244 MK(0, 0, 1, 2), |
|
1245 MK(0, 0, 1, 0), |
|
1246 MK(0, 0, 1, 1), |
|
1247 MK(0, 0, 1, 0), |
|
1248 MK(0, 0, 1, 2), |
|
1249 MK(0, 0, 1, 0), |
|
1250 MK(0, 0, 1, 1), |
|
1251 MK(0, 0, 1, 0), |
|
1252 MK(0, 0, 1, 2), |
|
1253 MK(0, 0, 1, 0), |
|
1254 MK(0, 0, 1, 1), |
|
1255 MK(0, 0, 1, 0), |
|
1256 MK(0, 0, 1, 2), |
|
1257 MK(0, 0, 1, 0), |
|
1258 MK(0, 0, 1, 1), |
|
1259 MK(0, 0, 1, 0), |
|
1260 MK(0, 0, 1, 2), |
|
1261 MK(0, 0, 1, 0), |
|
1262 MK(0, 0, 1, 1), |
|
1263 MK(0, 0, 1, 0), |
|
1264 MK(0, 0, 1, 2), |
|
1265 MK(0, 0, 1, 0), |
|
1266 MK(0, 0, 1, 1), |
|
1267 MK(0, 0, 1, 0), |
|
1268 MK(0, 0, 1, 2), |
|
1269 MK(0, 0, 1, 0), |
|
1270 MK(0, 0, 1, 1), |
|
1271 MK(0, 0, 1, 0), |
|
1272 MK(0, 0, 1, 2), |
|
1273 MK(0, 0, 1, 0), |
|
1274 MK(0, 0, 1, 1), |
|
1275 MK(2, 1, 3, 0), |
|
1276 MK(1, 1, 3, 1), |
|
1277 MK(2, 1, 3, 0), |
|
1278 MK(1, 1, 3, 2), |
|
1279 MK(2, 1, 3, 0), |
|
1280 MK(1, 1, 3, 1), |
|
1281 MK(2, 1, 3, 0), |
|
1282 MK(1, 0, 1, 2), |
|
1283 MK(0, 0, 1, 0), |
|
1284 MK(1, 0, 1, 1), |
|
1285 MK(0, 0, 1, 0), |
|
1286 MK(1, 0, 1, 2), |
|
1287 MK(0, 0, 1, 0), |
|
1288 MK(1, 0, 1, 1), |
|
1289 MK(0, 0, 1, 0), |
|
1290 MK(1, 0, 1, 2), |
|
1291 ME(2), |
|
1292 MK(0, 0, 0, 0xA), |
|
1293 MK(0, 0, 0, 0xB), |
|
1294 MK(0, 0, 0, 0xC), |
|
1295 MK(0, 0, 0, 0xD), |
|
1296 MK(0, 0, 0, 0xE), |
|
1297 ME(0) |
|
1298 }; |
|
1299 #undef ME |
|
1300 #undef MK |
|
1301 |
|
1302 static const BubbleMovement * const _bubble_movement[] = { |
|
1303 _bubble_float_sw, |
|
1304 _bubble_float_ne, |
|
1305 _bubble_float_se, |
|
1306 _bubble_float_nw, |
|
1307 _bubble_burst, |
|
1308 _bubble_absorb, |
|
1309 }; |
|
1310 |
|
1311 static void BubbleTick(Vehicle *v) |
|
1312 { |
|
1313 /* |
|
1314 * Warning: those effects can NOT use Random(), and have to use |
|
1315 * InteractiveRandom(), because somehow someone forgot to save |
|
1316 * spritenum to the savegame, and so it will cause desyncs in |
|
1317 * multiplayer!! (that is: in ToyLand) |
|
1318 */ |
|
1319 uint et; |
|
1320 const BubbleMovement *b; |
|
1321 |
|
1322 v->progress++; |
|
1323 if ((v->progress & 3) != 0) |
|
1324 return; |
|
1325 |
|
1326 BeginVehicleMove(v); |
|
1327 |
|
1328 if (v->spritenum == 0) { |
|
1329 v->cur_image++; |
|
1330 if (v->cur_image < SPR_BUBBLE_GENERATE_3) { |
|
1331 VehiclePositionChanged(v); |
|
1332 EndVehicleMove(v); |
|
1333 return; |
|
1334 } |
|
1335 if (v->u.special.unk2 != 0) { |
|
1336 v->spritenum = GB(InteractiveRandom(), 0, 2) + 1; |
|
1337 } else { |
|
1338 v->spritenum = 6; |
|
1339 } |
|
1340 et = 0; |
|
1341 } else { |
|
1342 et = v->engine_type + 1; |
|
1343 } |
|
1344 |
|
1345 b = &_bubble_movement[v->spritenum - 1][et]; |
|
1346 |
|
1347 if (b->y == 4 && b->x == 0) { |
|
1348 EndVehicleMove(v); |
|
1349 DeleteVehicle(v); |
|
1350 return; |
|
1351 } |
|
1352 |
|
1353 if (b->y == 4 && b->x == 1) { |
|
1354 if (v->z_pos > 180 || CHANCE16I(1, 96, InteractiveRandom())) { |
|
1355 v->spritenum = 5; |
|
1356 SndPlayVehicleFx(SND_2F_POP, v); |
|
1357 } |
|
1358 et = 0; |
|
1359 } |
|
1360 |
|
1361 if (b->y == 4 && b->x == 2) { |
|
1362 TileIndex tile; |
|
1363 |
|
1364 et++; |
|
1365 SndPlayVehicleFx(SND_31_EXTRACT, v); |
|
1366 |
|
1367 tile = TileVirtXY(v->x_pos, v->y_pos); |
|
1368 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == 0xA2) AddAnimatedTile(tile); |
|
1369 } |
|
1370 |
|
1371 v->engine_type = et; |
|
1372 b = &_bubble_movement[v->spritenum - 1][et]; |
|
1373 |
|
1374 v->x_pos += b->x; |
|
1375 v->y_pos += b->y; |
|
1376 v->z_pos += b->z; |
|
1377 v->cur_image = SPR_BUBBLE_0 + b->image; |
|
1378 |
|
1379 VehiclePositionChanged(v); |
|
1380 EndVehicleMove(v); |
|
1381 } |
|
1382 |
|
1383 |
|
1384 typedef void EffectInitProc(Vehicle *v); |
|
1385 typedef void EffectTickProc(Vehicle *v); |
|
1386 |
|
1387 static EffectInitProc * const _effect_init_procs[] = { |
|
1388 ChimneySmokeInit, |
|
1389 SteamSmokeInit, |
|
1390 DieselSmokeInit, |
|
1391 ElectricSparkInit, |
|
1392 SmokeInit, |
|
1393 ExplosionLargeInit, |
|
1394 BreakdownSmokeInit, |
|
1395 ExplosionSmallInit, |
|
1396 BulldozerInit, |
|
1397 BubbleInit, |
|
1398 }; |
|
1399 |
|
1400 static EffectTickProc * const _effect_tick_procs[] = { |
|
1401 ChimneySmokeTick, |
|
1402 SteamSmokeTick, |
|
1403 DieselSmokeTick, |
|
1404 ElectricSparkTick, |
|
1405 SmokeTick, |
|
1406 ExplosionLargeTick, |
|
1407 BreakdownSmokeTick, |
|
1408 ExplosionSmallTick, |
|
1409 BulldozerTick, |
|
1410 BubbleTick, |
|
1411 }; |
|
1412 |
|
1413 |
|
1414 Vehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicle type) |
|
1415 { |
|
1416 Vehicle *v; |
|
1417 |
|
1418 v = ForceAllocateSpecialVehicle(); |
|
1419 if (v != NULL) { |
|
1420 v->type = VEH_Special; |
|
1421 v->subtype = type; |
|
1422 v->x_pos = x; |
|
1423 v->y_pos = y; |
|
1424 v->z_pos = z; |
|
1425 v->z_height = v->sprite_width = v->sprite_height = 1; |
|
1426 v->x_offs = v->y_offs = 0; |
|
1427 v->tile = 0; |
|
1428 v->vehstatus = VS_UNCLICKABLE; |
|
1429 |
|
1430 _effect_init_procs[type](v); |
|
1431 |
|
1432 VehiclePositionChanged(v); |
|
1433 BeginVehicleMove(v); |
|
1434 EndVehicleMove(v); |
|
1435 } |
|
1436 return v; |
|
1437 } |
|
1438 |
|
1439 Vehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicle type) |
|
1440 { |
|
1441 int safe_x = clamp(x, 0, MapMaxX() * TILE_SIZE); |
|
1442 int safe_y = clamp(y, 0, MapMaxY() * TILE_SIZE); |
|
1443 return CreateEffectVehicle(x, y, GetSlopeZ(safe_x, safe_y) + z, type); |
|
1444 } |
|
1445 |
|
1446 Vehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicle type) |
|
1447 { |
|
1448 return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type); |
|
1449 } |
|
1450 |
|
1451 static void EffectVehicle_Tick(Vehicle *v) |
|
1452 { |
|
1453 _effect_tick_procs[v->subtype](v); |
|
1454 } |
|
1455 |
|
1456 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y) |
|
1457 { |
|
1458 Vehicle *found = NULL, *v; |
|
1459 uint dist, best_dist = (uint)-1; |
|
1460 |
|
1461 if ( (uint)(x -= vp->left) >= (uint)vp->width || |
|
1462 (uint)(y -= vp->top) >= (uint)vp->height) |
|
1463 return NULL; |
|
1464 |
|
1465 x = (x << vp->zoom) + vp->virtual_left; |
|
1466 y = (y << vp->zoom) + vp->virtual_top; |
|
1467 |
|
1468 FOR_ALL_VEHICLES(v) { |
|
1469 if ((v->vehstatus & (VS_HIDDEN|VS_UNCLICKABLE)) == 0 && |
|
1470 x >= v->left_coord && x <= v->right_coord && |
|
1471 y >= v->top_coord && y <= v->bottom_coord) { |
|
1472 |
|
1473 dist = max( |
|
1474 myabs( ((v->left_coord + v->right_coord)>>1) - x ), |
|
1475 myabs( ((v->top_coord + v->bottom_coord)>>1) - y ) |
|
1476 ); |
|
1477 |
|
1478 if (dist < best_dist) { |
|
1479 found = v; |
|
1480 best_dist = dist; |
|
1481 } |
|
1482 } |
|
1483 } |
|
1484 |
|
1485 return found; |
|
1486 } |
|
1487 |
|
1488 |
|
1489 void DecreaseVehicleValue(Vehicle *v) |
|
1490 { |
|
1491 v->value -= v->value >> 8; |
|
1492 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
1493 } |
|
1494 |
|
1495 static const byte _breakdown_chance[64] = { |
|
1496 3, 3, 3, 3, 3, 3, 3, 3, |
|
1497 4, 4, 5, 5, 6, 6, 7, 7, |
|
1498 8, 8, 9, 9, 10, 10, 11, 11, |
|
1499 12, 13, 13, 13, 13, 14, 15, 16, |
|
1500 17, 19, 21, 25, 28, 31, 34, 37, |
|
1501 40, 44, 48, 52, 56, 60, 64, 68, |
|
1502 72, 80, 90, 100, 110, 120, 130, 140, |
|
1503 150, 170, 190, 210, 230, 250, 250, 250, |
|
1504 }; |
|
1505 |
|
1506 void CheckVehicleBreakdown(Vehicle *v) |
|
1507 { |
|
1508 int rel, rel_old; |
|
1509 uint32 r; |
|
1510 int chance; |
|
1511 |
|
1512 /* decrease reliability */ |
|
1513 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0); |
|
1514 if ((rel_old >> 8) != (rel >> 8)) |
|
1515 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
1516 |
|
1517 if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED || |
|
1518 v->cur_speed < 5 || _game_mode == GM_MENU) { |
|
1519 return; |
|
1520 } |
|
1521 |
|
1522 r = Random(); |
|
1523 |
|
1524 /* increase chance of failure */ |
|
1525 chance = v->breakdown_chance + 1; |
|
1526 if (CHANCE16I(1,25,r)) chance += 25; |
|
1527 v->breakdown_chance = min(255, chance); |
|
1528 |
|
1529 /* calculate reliability value to use in comparison */ |
|
1530 rel = v->reliability; |
|
1531 if (v->type == VEH_Ship) rel += 0x6666; |
|
1532 |
|
1533 /* disabled breakdowns? */ |
|
1534 if (_opt.diff.vehicle_breakdowns < 1) return; |
|
1535 |
|
1536 /* reduced breakdowns? */ |
|
1537 if (_opt.diff.vehicle_breakdowns == 1) rel += 0x6666; |
|
1538 |
|
1539 /* check if to break down */ |
|
1540 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) { |
|
1541 v->breakdown_ctr = GB(r, 16, 6) + 0x3F; |
|
1542 v->breakdown_delay = GB(r, 24, 7) + 0x80; |
|
1543 v->breakdown_chance = 0; |
|
1544 } |
|
1545 } |
|
1546 |
|
1547 static const StringID _vehicle_type_names[4] = { |
|
1548 STR_019F_TRAIN, |
|
1549 STR_019C_ROAD_VEHICLE, |
|
1550 STR_019E_SHIP, |
|
1551 STR_019D_AIRCRAFT, |
|
1552 }; |
|
1553 |
|
1554 static void ShowVehicleGettingOld(Vehicle *v, StringID msg) |
|
1555 { |
|
1556 if (v->owner != _local_player) return; |
|
1557 |
|
1558 // Do not show getting-old message if autorenew is active |
|
1559 if (GetPlayer(v->owner)->engine_renew) return; |
|
1560 |
|
1561 SetDParam(0, _vehicle_type_names[v->type - 0x10]); |
|
1562 SetDParam(1, v->unitnumber); |
|
1563 AddNewsItem(msg, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0); |
|
1564 } |
|
1565 |
|
1566 void AgeVehicle(Vehicle *v) |
|
1567 { |
|
1568 int age; |
|
1569 |
|
1570 if (v->age < 65535) |
|
1571 v->age++; |
|
1572 |
|
1573 age = v->age - v->max_age; |
|
1574 if (age == 366*0 || age == 366*1 || age == 366*2 || age == 366*3 || age == 366*4) |
|
1575 v->reliability_spd_dec <<= 1; |
|
1576 |
|
1577 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
1578 |
|
1579 if (age == -366) { |
|
1580 ShowVehicleGettingOld(v, STR_01A0_IS_GETTING_OLD); |
|
1581 } else if (age == 0) { |
|
1582 ShowVehicleGettingOld(v, STR_01A1_IS_GETTING_VERY_OLD); |
|
1583 } else if (age == 366*1 || age == 366*2 || age == 366*3 || age == 366*4 || age == 366*5) { |
|
1584 ShowVehicleGettingOld(v, STR_01A2_IS_GETTING_VERY_OLD_AND); |
|
1585 } |
|
1586 } |
|
1587 |
|
1588 /** Starts or stops a lot of vehicles |
|
1589 * @param tile Tile of the depot where the vehicles are started/stopped (only used for depots) |
|
1590 * @param p1 Station/Order/Depot ID (only used for vehicle list windows) |
|
1591 * @param p2 bitmask |
|
1592 * - bit 0-4 Vehicle type |
|
1593 * - bit 5 false = start vehicles, true = stop vehicles |
|
1594 * - bit 6 if set, then it's a vehicle list window, not a depot and Tile is ignored in this case |
|
1595 * - bit 8-11 Vehicle List Window type (ignored unless bit 1 is set) |
|
1596 */ |
|
1597 int32 CmdMassStartStopVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
1598 { |
|
1599 Vehicle **vl = NULL; |
|
1600 uint16 engine_list_length = 0; |
|
1601 uint16 engine_count = 0; |
|
1602 int32 return_value = CMD_ERROR; |
|
1603 uint i; |
|
1604 uint stop_command; |
|
1605 byte vehicle_type = GB(p2, 0, 5); |
|
1606 bool start_stop = HASBIT(p2, 5); |
|
1607 bool vehicle_list_window = HASBIT(p2, 6); |
|
1608 |
|
1609 switch (vehicle_type) { |
|
1610 case VEH_Train: stop_command = CMD_START_STOP_TRAIN; break; |
|
1611 case VEH_Road: stop_command = CMD_START_STOP_ROADVEH; break; |
|
1612 case VEH_Ship: stop_command = CMD_START_STOP_SHIP; break; |
|
1613 case VEH_Aircraft: stop_command = CMD_START_STOP_AIRCRAFT; break; |
|
1614 default: return CMD_ERROR; |
|
1615 } |
|
1616 |
|
1617 if (vehicle_list_window) { |
|
1618 uint16 id = GB(p1, 0, 16); |
|
1619 uint16 window_type = p2 & VLW_MASK; |
|
1620 |
|
1621 engine_count = GenerateVehicleSortList((const Vehicle***)&vl, &engine_list_length, vehicle_type, _current_player, id, id, id, window_type); |
|
1622 } else { |
|
1623 /* Get the list of vehicles in the depot */ |
|
1624 BuildDepotVehicleList(vehicle_type, tile, &vl, &engine_list_length, &engine_count, NULL, NULL, NULL); |
|
1625 } |
|
1626 |
|
1627 for (i = 0; i < engine_count; i++) { |
|
1628 const Vehicle *v = vl[i]; |
|
1629 int32 ret; |
|
1630 |
|
1631 if (!!(v->vehstatus & VS_STOPPED) != start_stop) continue; |
|
1632 |
|
1633 if (!vehicle_list_window) { |
|
1634 if (vehicle_type == VEH_Train) { |
|
1635 if (CheckTrainInDepot(v, false) == -1) continue; |
|
1636 } else { |
|
1637 if (!(v->vehstatus & VS_HIDDEN)) continue; |
|
1638 } |
|
1639 } |
|
1640 |
|
1641 ret = DoCommand(tile, v->index, 0, flags, stop_command); |
|
1642 |
|
1643 if (!CmdFailed(ret)) { |
|
1644 return_value = 0; |
|
1645 /* We know that the command is valid for at least one vehicle. |
|
1646 * If we haven't set DC_EXEC, then there is no point in continueing because it will be valid */ |
|
1647 if (!(flags & DC_EXEC)) break; |
|
1648 } |
|
1649 } |
|
1650 |
|
1651 free(vl); |
|
1652 return return_value; |
|
1653 } |
|
1654 |
|
1655 /** Sells all vehicles in a depot |
|
1656 * @param tile Tile of the depot where the depot is |
|
1657 * @param p1 Vehicle type |
|
1658 * @param p2 unused |
|
1659 */ |
|
1660 int32 CmdDepotSellAllVehicles(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
1661 { |
|
1662 Vehicle **engines = NULL; |
|
1663 Vehicle **wagons = NULL; |
|
1664 uint16 engine_list_length = 0; |
|
1665 uint16 engine_count = 0; |
|
1666 uint16 wagon_list_length = 0; |
|
1667 uint16 wagon_count = 0; |
|
1668 |
|
1669 int32 cost = 0; |
|
1670 uint i, sell_command, total_number_vehicles; |
|
1671 byte vehicle_type = GB(p1, 0, 8); |
|
1672 |
|
1673 switch (vehicle_type) { |
|
1674 case VEH_Train: sell_command = CMD_SELL_RAIL_WAGON; break; |
|
1675 case VEH_Road: sell_command = CMD_SELL_ROAD_VEH; break; |
|
1676 case VEH_Ship: sell_command = CMD_SELL_SHIP; break; |
|
1677 case VEH_Aircraft: sell_command = CMD_SELL_AIRCRAFT; break; |
|
1678 default: return CMD_ERROR; |
|
1679 } |
|
1680 |
|
1681 /* Get the list of vehicles in the depot */ |
|
1682 BuildDepotVehicleList(vehicle_type, tile, &engines, &engine_list_length, &engine_count, |
|
1683 &wagons, &wagon_list_length, &wagon_count); |
|
1684 |
|
1685 total_number_vehicles = engine_count + wagon_count; |
|
1686 for (i = 0; i < total_number_vehicles; i++) { |
|
1687 const Vehicle *v; |
|
1688 int32 ret; |
|
1689 |
|
1690 if (i < engine_count) { |
|
1691 v = engines[i]; |
|
1692 } else { |
|
1693 v = wagons[i - engine_count]; |
|
1694 } |
|
1695 |
|
1696 ret = DoCommand(tile, v->index, 1, flags, sell_command); |
|
1697 |
|
1698 if (!CmdFailed(ret)) cost += ret; |
|
1699 } |
|
1700 |
|
1701 free(engines); |
|
1702 free(wagons); |
|
1703 if (cost == 0) return CMD_ERROR; // no vehicles to sell |
|
1704 return cost; |
|
1705 } |
|
1706 |
|
1707 /** Autoreplace all vehicles in the depot |
|
1708 * @param tile Tile of the depot where the vehicles are |
|
1709 * @param p1 Type of vehicle |
|
1710 * @param p2 Unused |
|
1711 */ |
|
1712 int32 CmdDepotMassAutoReplace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
1713 { |
|
1714 Vehicle **vl = NULL; |
|
1715 uint16 engine_list_length = 0; |
|
1716 uint16 engine_count = 0; |
|
1717 uint i, x = 0, y = 0, z = 0; |
|
1718 int32 cost = 0; |
|
1719 byte vehicle_type = GB(p1, 0, 8); |
|
1720 |
|
1721 |
|
1722 if (!IsTileOwner(tile, _current_player)) return CMD_ERROR; |
|
1723 |
|
1724 /* Get the list of vehicles in the depot */ |
|
1725 BuildDepotVehicleList(vehicle_type, tile, &vl, &engine_list_length, &engine_count, NULL, NULL, NULL); |
|
1726 |
|
1727 |
|
1728 for (i = 0; i < engine_count; i++) { |
|
1729 Vehicle *v = vl[i]; |
|
1730 bool stopped = !(v->vehstatus & VS_STOPPED); |
|
1731 int32 ret; |
|
1732 |
|
1733 /* Ensure that the vehicle completely in the depot */ |
|
1734 if (!IsVehicleInDepot(v)) continue; |
|
1735 |
|
1736 x = v->x_pos; |
|
1737 y = v->y_pos; |
|
1738 z = v->z_pos; |
|
1739 |
|
1740 if (stopped) { |
|
1741 v->vehstatus |= VS_STOPPED; // Stop the vehicle |
|
1742 v->leave_depot_instantly = true; |
|
1743 } |
|
1744 ret = MaybeReplaceVehicle(v, !(flags & DC_EXEC), false); |
|
1745 |
|
1746 if (!CmdFailed(ret)) { |
|
1747 cost += ret; |
|
1748 if (!(flags & DC_EXEC)) break; |
|
1749 /* There is a problem with autoreplace and newgrf |
|
1750 * It's impossible to tell the length of a train after it's being replaced before it's actually done |
|
1751 * Because of this, we can't estimate costs due to wagon removal and we will have to always return 0 and pay manually |
|
1752 * Since we pay after each vehicle is replaced and MaybeReplaceVehicle() check if the player got enough money |
|
1753 * we should never reach a condition where the player will end up with negative money from doing this */ |
|
1754 SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
1755 SubtractMoneyFromPlayer(ret); |
|
1756 } |
|
1757 } |
|
1758 |
|
1759 if (cost == 0) { |
|
1760 cost = CMD_ERROR; |
|
1761 } else { |
|
1762 if (flags & DC_EXEC) { |
|
1763 /* Display the cost animation now that DoCommandP() can't do it for us (see previous comments) */ |
|
1764 if (IsLocalPlayer()) ShowCostOrIncomeAnimation(x, y, z, cost); |
|
1765 } |
|
1766 cost = 0; |
|
1767 } |
|
1768 |
|
1769 free(vl); |
|
1770 return cost; |
|
1771 } |
|
1772 |
|
1773 /** Clone a vehicle. If it is a train, it will clone all the cars too |
|
1774 * @param tile tile of the depot where the cloned vehicle is build |
|
1775 * @param p1 the original vehicle's index |
|
1776 * @param p2 1 = shared orders, else copied orders |
|
1777 */ |
|
1778 int32 CmdCloneVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
1779 { |
|
1780 Vehicle *v_front, *v; |
|
1781 Vehicle *w_front, *w, *w_rear; |
|
1782 int cost, total_cost = 0; |
|
1783 uint32 build_argument = 2; |
|
1784 |
|
1785 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
|
1786 v = GetVehicle(p1); |
|
1787 v_front = v; |
|
1788 w = NULL; |
|
1789 w_front = NULL; |
|
1790 w_rear = NULL; |
|
1791 |
|
1792 |
|
1793 /* |
|
1794 * v_front is the front engine in the original vehicle |
|
1795 * v is the car/vehicle of the original vehicle, that is currently being copied |
|
1796 * w_front is the front engine of the cloned vehicle |
|
1797 * w is the car/vehicle currently being cloned |
|
1798 * w_rear is the rear end of the cloned train. It's used to add more cars and is only used by trains |
|
1799 */ |
|
1800 |
|
1801 if (!CheckOwnership(v->owner)) return CMD_ERROR; |
|
1802 |
|
1803 if (v->type == VEH_Train && (!IsFrontEngine(v) || v->u.rail.crash_anim_pos >= 4400)) return CMD_ERROR; |
|
1804 |
|
1805 // check that we can allocate enough vehicles |
|
1806 if (!(flags & DC_EXEC)) { |
|
1807 int veh_counter = 0; |
|
1808 do { |
|
1809 veh_counter++; |
|
1810 } while ((v = v->next) != NULL); |
|
1811 |
|
1812 if (!AllocateVehicles(NULL, veh_counter)) { |
|
1813 return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); |
|
1814 } |
|
1815 } |
|
1816 |
|
1817 v = v_front; |
|
1818 |
|
1819 do { |
|
1820 |
|
1821 if (IsMultiheaded(v) && !IsTrainEngine(v)) { |
|
1822 /* we build the rear ends of multiheaded trains with the front ones */ |
|
1823 continue; |
|
1824 } |
|
1825 |
|
1826 cost = DoCommand(tile, v->engine_type, build_argument, flags, CMD_BUILD_VEH(v->type)); |
|
1827 build_argument = 3; // ensure that we only assign a number to the first engine |
|
1828 |
|
1829 if (CmdFailed(cost)) return cost; |
|
1830 |
|
1831 total_cost += cost; |
|
1832 |
|
1833 if (flags & DC_EXEC) { |
|
1834 w = GetVehicle(_new_vehicle_id); |
|
1835 |
|
1836 if (v->cargo_type != w->cargo_type || v->cargo_subtype != w->cargo_subtype) { |
|
1837 // we can't pay for refitting because we can't estimate refitting costs for a vehicle before it's build |
|
1838 // if we pay for it anyway, the cost and the estimated cost will not be the same and we will have an assert |
|
1839 DoCommand(0, w->index, v->cargo_type | (v->cargo_subtype << 8), flags, CMD_REFIT_VEH(v->type)); |
|
1840 } |
|
1841 if (v->type == VEH_Train && HASBIT(v->u.rail.flags, VRF_REVERSE_DIRECTION)) { |
|
1842 SETBIT(w->u.rail.flags, VRF_REVERSE_DIRECTION); |
|
1843 } |
|
1844 |
|
1845 if (v->type == VEH_Train && !IsFrontEngine(v)) { |
|
1846 // this s a train car |
|
1847 // add this unit to the end of the train |
|
1848 DoCommand(0, (w_rear->index << 16) | w->index, 1, flags, CMD_MOVE_RAIL_VEHICLE); |
|
1849 } else { |
|
1850 // this is a front engine or not a train. It need orders |
|
1851 w_front = w; |
|
1852 w->service_interval = v->service_interval; |
|
1853 DoCommand(0, (v->index << 16) | w->index, p2 & 1 ? CO_SHARE : CO_COPY, flags, CMD_CLONE_ORDER); |
|
1854 } |
|
1855 w_rear = w; // trains needs to know the last car in the train, so they can add more in next loop |
|
1856 } |
|
1857 } while (v->type == VEH_Train && (v = GetNextVehicle(v)) != NULL); |
|
1858 |
|
1859 if (flags & DC_EXEC && v_front->type == VEH_Train) { |
|
1860 // for trains this needs to be the front engine due to the callback function |
|
1861 _new_vehicle_id = w_front->index; |
|
1862 } |
|
1863 |
|
1864 /* Set the expense type last as refitting will make the cost go towards |
|
1865 * running costs... */ |
|
1866 SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
1867 return total_cost; |
|
1868 } |
|
1869 |
|
1870 /* |
|
1871 * move the cargo from one engine to another if possible |
|
1872 */ |
|
1873 static void MoveVehicleCargo(Vehicle *dest, Vehicle *source) |
|
1874 { |
|
1875 Vehicle *v = dest; |
|
1876 int units_moved; |
|
1877 |
|
1878 do { |
|
1879 do { |
|
1880 if (source->cargo_type != dest->cargo_type) |
|
1881 continue; // cargo not compatible |
|
1882 |
|
1883 if (dest->cargo_count == dest->cargo_cap) |
|
1884 continue; // the destination vehicle is already full |
|
1885 |
|
1886 units_moved = min(source->cargo_count, dest->cargo_cap - dest->cargo_count); |
|
1887 source->cargo_count -= units_moved; |
|
1888 dest->cargo_count += units_moved; |
|
1889 dest->cargo_source = source->cargo_source; |
|
1890 |
|
1891 // copy the age of the cargo |
|
1892 dest->cargo_days = source->cargo_days; |
|
1893 dest->day_counter = source->day_counter; |
|
1894 dest->tick_counter = source->tick_counter; |
|
1895 |
|
1896 } while (source->cargo_count > 0 && (dest = dest->next) != NULL); |
|
1897 dest = v; |
|
1898 } while ((source = source->next) != NULL); |
|
1899 } |
|
1900 |
|
1901 static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, const EngineID engine_type) |
|
1902 { |
|
1903 const Order *o; |
|
1904 const Vehicle *u; |
|
1905 |
|
1906 if (v->type == VEH_Train) { |
|
1907 u = GetFirstVehicleInChain(v); |
|
1908 } else { |
|
1909 u = v; |
|
1910 } |
|
1911 |
|
1912 FOR_VEHICLE_ORDERS(u, o) { |
|
1913 if (!(o->refit_cargo < NUM_CARGO)) continue; |
|
1914 if (!CanRefitTo(v->engine_type, o->refit_cargo)) continue; |
|
1915 if (!CanRefitTo(engine_type, o->refit_cargo)) return false; |
|
1916 } |
|
1917 |
|
1918 return true; |
|
1919 } |
|
1920 |
|
1921 /** |
|
1922 * Function to find what type of cargo to refit to when autoreplacing |
|
1923 * @param *v Original vehicle, that is being replaced |
|
1924 * @param engine_type The EngineID of the vehicle that is being replaced to |
|
1925 * @return The cargo type to replace to |
|
1926 * CT_NO_REFIT is returned if no refit is needed |
|
1927 * CT_INVALID is returned when both old and new vehicle got cargo capacity and refitting the new one to the old one's cargo type isn't possible |
|
1928 */ |
|
1929 static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type) |
|
1930 { |
|
1931 bool new_cargo_capacity = true; |
|
1932 CargoID new_cargo_type = CT_INVALID; |
|
1933 |
|
1934 switch (v->type) { |
|
1935 case VEH_Train: |
|
1936 new_cargo_capacity = (RailVehInfo(engine_type)->capacity > 0); |
|
1937 new_cargo_type = RailVehInfo(engine_type)->cargo_type; |
|
1938 break; |
|
1939 |
|
1940 case VEH_Road: |
|
1941 new_cargo_capacity = (RoadVehInfo(engine_type)->capacity > 0); |
|
1942 new_cargo_type = RoadVehInfo(engine_type)->cargo_type; |
|
1943 break; |
|
1944 case VEH_Ship: |
|
1945 new_cargo_capacity = (ShipVehInfo(engine_type)->capacity > 0); |
|
1946 new_cargo_type = ShipVehInfo(engine_type)->cargo_type; |
|
1947 break; |
|
1948 |
|
1949 case VEH_Aircraft: |
|
1950 /* all aircraft starts as passenger planes with cargo capacity |
|
1951 * new_cargo_capacity is always true for aircraft, which is the init value. No need to set it here */ |
|
1952 new_cargo_type = CT_PASSENGERS; |
|
1953 break; |
|
1954 |
|
1955 default: NOT_REACHED(); break; |
|
1956 } |
|
1957 |
|
1958 if (!new_cargo_capacity) return CT_NO_REFIT; // Don't try to refit an engine with no cargo capacity |
|
1959 |
|
1960 if (v->cargo_type == new_cargo_type || CanRefitTo(engine_type, v->cargo_type)) { |
|
1961 if (VerifyAutoreplaceRefitForOrders(v, engine_type)) { |
|
1962 return v->cargo_type == new_cargo_type ? CT_NO_REFIT : v->cargo_type; |
|
1963 } else { |
|
1964 return CT_INVALID; |
|
1965 } |
|
1966 } |
|
1967 if (v->type != VEH_Train) return CT_INVALID; // We can't refit the vehicle to carry the cargo we want |
|
1968 |
|
1969 /* Below this line it's safe to assume that the vehicle in question is a train */ |
|
1970 |
|
1971 if (v->cargo_cap != 0) return CT_INVALID; // trying to replace a vehicle with cargo capacity into another one with incompatible cargo type |
|
1972 |
|
1973 /* the old engine didn't have cargo capacity, but the new one does |
|
1974 * now we will figure out what cargo the train is carrying and refit to fit this */ |
|
1975 v = GetFirstVehicleInChain(v); |
|
1976 do { |
|
1977 if (v->cargo_cap == 0) continue; |
|
1978 /* Now we found a cargo type being carried on the train and we will see if it is possible to carry to this one */ |
|
1979 if (v->cargo_type == new_cargo_type) return CT_NO_REFIT; |
|
1980 if (CanRefitTo(engine_type, v->cargo_type)) return v->cargo_type; |
|
1981 } while ((v=v->next) != NULL); |
|
1982 return CT_NO_REFIT; // We failed to find a cargo type on the old vehicle and we will not refit the new one |
|
1983 } |
|
1984 |
|
1985 /* Replaces a vehicle (used to be called autorenew) |
|
1986 * This function is only called from MaybeReplaceVehicle() |
|
1987 * Must be called with _current_player set to the owner of the vehicle |
|
1988 * @param w Vehicle to replace |
|
1989 * @param flags is the flags to use when calling DoCommand(). Mainly DC_EXEC counts |
|
1990 * @return value is cost of the replacement or CMD_ERROR |
|
1991 */ |
|
1992 static int32 ReplaceVehicle(Vehicle **w, byte flags, int32 total_cost) |
|
1993 { |
|
1994 int32 cost; |
|
1995 int32 sell_value; |
|
1996 Vehicle *old_v = *w; |
|
1997 const Player *p = GetPlayer(old_v->owner); |
|
1998 EngineID new_engine_type; |
|
1999 const UnitID cached_unitnumber = old_v->unitnumber; |
|
2000 bool new_front = false; |
|
2001 Vehicle *new_v = NULL; |
|
2002 char vehicle_name[32]; |
|
2003 CargoID replacement_cargo_type; |
|
2004 |
|
2005 new_engine_type = EngineReplacementForPlayer(p, old_v->engine_type); |
|
2006 if (new_engine_type == INVALID_ENGINE) new_engine_type = old_v->engine_type; |
|
2007 |
|
2008 replacement_cargo_type = GetNewCargoTypeForReplace(old_v, new_engine_type); |
|
2009 |
|
2010 /* check if we can't refit to the needed type, so no replace takes place to prevent the vehicle from altering cargo type */ |
|
2011 if (replacement_cargo_type == CT_INVALID) return 0; |
|
2012 |
|
2013 sell_value = DoCommand(0, old_v->index, 0, DC_QUERY_COST, CMD_SELL_VEH(old_v->type)); |
|
2014 |
|
2015 /* We give the player a loan of the same amount as the sell value. |
|
2016 * This is needed in case he needs the income from the sale to build the new vehicle. |
|
2017 * We take it back if building fails or when we really sell the old engine */ |
|
2018 SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
2019 SubtractMoneyFromPlayer(sell_value); |
|
2020 |
|
2021 cost = DoCommand(old_v->tile, new_engine_type, 3, flags, CMD_BUILD_VEH(old_v->type)); |
|
2022 if (CmdFailed(cost)) { |
|
2023 SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
2024 SubtractMoneyFromPlayer(-sell_value); // Take back the money we just gave the player |
|
2025 return cost; |
|
2026 } |
|
2027 |
|
2028 if (replacement_cargo_type != CT_NO_REFIT) cost += GetRefitCost(new_engine_type); // add refit cost |
|
2029 |
|
2030 if (flags & DC_EXEC) { |
|
2031 new_v = GetVehicle(_new_vehicle_id); |
|
2032 *w = new_v; //we changed the vehicle, so MaybeReplaceVehicle needs to work on the new one. Now we tell it what the new one is |
|
2033 |
|
2034 /* refit if needed */ |
|
2035 if (replacement_cargo_type != CT_NO_REFIT) { |
|
2036 if (CmdFailed(DoCommand(0, new_v->index, replacement_cargo_type, DC_EXEC, CMD_REFIT_VEH(new_v->type)))) { |
|
2037 /* Being here shows a failure, which most likely is in GetNewCargoTypeForReplace() or incorrect estimation costs */ |
|
2038 error("Autoreplace failed to refit. Replace engine %d to %d and refit to cargo %d", old_v->engine_type, new_v->engine_type, replacement_cargo_type); |
|
2039 } |
|
2040 } |
|
2041 |
|
2042 if (new_v->type == VEH_Train && HASBIT(old_v->u.rail.flags, VRF_REVERSE_DIRECTION) && !IsMultiheaded(new_v) && !(new_v->next != NULL && IsArticulatedPart(new_v->next))) { |
|
2043 // we are autorenewing to a single engine, so we will turn it as the old one was turned as well |
|
2044 SETBIT(new_v->u.rail.flags, VRF_REVERSE_DIRECTION); |
|
2045 } |
|
2046 |
|
2047 if (old_v->type == VEH_Train && !IsFrontEngine(old_v)) { |
|
2048 /* this is a railcar. We need to move the car into the train |
|
2049 * We add the new engine after the old one instead of replacing it. It will give the same result anyway when we |
|
2050 * sell the old engine in a moment |
|
2051 */ |
|
2052 DoCommand(0, (GetPrevVehicleInChain(old_v)->index << 16) | new_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
2053 /* Now we move the old one out of the train */ |
|
2054 DoCommand(0, (INVALID_VEHICLE << 16) | old_v->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
2055 } else { |
|
2056 // copy/clone the orders |
|
2057 DoCommand(0, (old_v->index << 16) | new_v->index, IsOrderListShared(old_v) ? CO_SHARE : CO_COPY, DC_EXEC, CMD_CLONE_ORDER); |
|
2058 new_v->cur_order_index = old_v->cur_order_index; |
|
2059 ChangeVehicleViewWindow(old_v, new_v); |
|
2060 new_v->profit_this_year = old_v->profit_this_year; |
|
2061 new_v->profit_last_year = old_v->profit_last_year; |
|
2062 new_v->service_interval = old_v->service_interval; |
|
2063 new_front = true; |
|
2064 new_v->unitnumber = old_v->unitnumber; // use the same unit number |
|
2065 |
|
2066 new_v->current_order = old_v->current_order; |
|
2067 if (old_v->type == VEH_Train && GetNextVehicle(old_v) != NULL){ |
|
2068 Vehicle *temp_v = GetNextVehicle(old_v); |
|
2069 |
|
2070 // move the entire train to the new engine, excluding the old engine |
|
2071 if (IsMultiheaded(old_v) && temp_v == old_v->u.rail.other_multiheaded_part) { |
|
2072 // we got front and rear of a multiheaded engine right after each other. We should work with the next in line instead |
|
2073 temp_v = GetNextVehicle(temp_v); |
|
2074 } |
|
2075 |
|
2076 if (temp_v != NULL) { |
|
2077 DoCommand(0, (new_v->index << 16) | temp_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
2078 } |
|
2079 } |
|
2080 } |
|
2081 /* We are done setting up the new vehicle. Now we move the cargo from the old one to the new one */ |
|
2082 MoveVehicleCargo(new_v->type == VEH_Train ? GetFirstVehicleInChain(new_v) : new_v, old_v); |
|
2083 |
|
2084 // Get the name of the old vehicle if it has a custom name. |
|
2085 if (!IsCustomName(old_v->string_id)) { |
|
2086 vehicle_name[0] = '\0'; |
|
2087 } else { |
|
2088 GetName(vehicle_name, old_v->string_id & 0x7FF, lastof(vehicle_name)); |
|
2089 } |
|
2090 } else { // flags & DC_EXEC not set |
|
2091 /* Ensure that the player will not end up having negative money while autoreplacing |
|
2092 * This is needed because the only other check is done after the income from selling the old vehicle is substracted from the cost */ |
|
2093 if (p->money64 < (cost + total_cost)) { |
|
2094 SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
2095 SubtractMoneyFromPlayer(-sell_value); // Pay back the loan |
|
2096 return CMD_ERROR; |
|
2097 } |
|
2098 } |
|
2099 |
|
2100 /* Take back the money we just gave the player just before building the vehicle |
|
2101 * The player will get the same amount now that the sale actually takes place */ |
|
2102 SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
|
2103 SubtractMoneyFromPlayer(-sell_value); |
|
2104 |
|
2105 /* sell the engine/ find out how much you get for the old engine (income is returned as negative cost) */ |
|
2106 cost += DoCommand(0, old_v->index, 0, flags, CMD_SELL_VEH(old_v->type)); |
|
2107 |
|
2108 if (new_front) { |
|
2109 /* now we assign the old unitnumber to the new vehicle */ |
|
2110 new_v->unitnumber = cached_unitnumber; |
|
2111 } |
|
2112 |
|
2113 /* Transfer the name of the old vehicle */ |
|
2114 if ((flags & DC_EXEC) && vehicle_name[0] != '\0') { |
|
2115 _cmd_text = vehicle_name; |
|
2116 DoCommand(0, new_v->index, 0, DC_EXEC, CMD_NAME_VEHICLE); |
|
2117 } |
|
2118 |
|
2119 return cost; |
|
2120 } |
|
2121 |
|
2122 /** replaces a vehicle if it's set for autoreplace or is too old |
|
2123 * (used to be called autorenew) |
|
2124 * @param v The vehicle to replace |
|
2125 * if the vehicle is a train, v needs to be the front engine |
|
2126 * @param check Checks if the replace is valid. No action is done at all |
|
2127 * @param display_costs If set, a cost animation is shown (only if check is false) |
|
2128 * @return CMD_ERROR if something went wrong. Otherwise the price of the replace |
|
2129 */ |
|
2130 static int32 MaybeReplaceVehicle(Vehicle *v, bool check, bool display_costs) |
|
2131 { |
|
2132 Vehicle *w; |
|
2133 const Player *p = GetPlayer(v->owner); |
|
2134 byte flags = 0; |
|
2135 int32 cost, temp_cost = 0; |
|
2136 bool stopped = false; |
|
2137 |
|
2138 /* Remember the length in case we need to trim train later on |
|
2139 * If it's not a train, the value is unused |
|
2140 * round up to the length of the tiles used for the train instead of the train length instead |
|
2141 * Useful when newGRF uses custom length */ |
|
2142 uint16 old_total_length = (v->type == VEH_Train ? |
|
2143 (v->u.rail.cached_total_length + TILE_SIZE - 1) / TILE_SIZE * TILE_SIZE : |
|
2144 -1 |
|
2145 ); |
|
2146 |
|
2147 |
|
2148 _current_player = v->owner; |
|
2149 |
|
2150 assert(v->type == VEH_Train || v->type == VEH_Road || v->type == VEH_Ship || v->type == VEH_Aircraft); |
|
2151 |
|
2152 assert(v->vehstatus & VS_STOPPED); // the vehicle should have been stopped in VehicleEnteredDepotThisTick() if needed |
|
2153 |
|
2154 if (v->leave_depot_instantly) { |
|
2155 // we stopped the vehicle to do this, so we have to remember to start it again when we are done |
|
2156 // we need to store this info as the engine might be replaced and lose this info |
|
2157 stopped = true; |
|
2158 } |
|
2159 |
|
2160 for (;;) { |
|
2161 cost = 0; |
|
2162 w = v; |
|
2163 do { |
|
2164 if (w->type == VEH_Train && IsMultiheaded(w) && !IsTrainEngine(w)) { |
|
2165 /* we build the rear ends of multiheaded trains with the front ones */ |
|
2166 continue; |
|
2167 } |
|
2168 |
|
2169 // check if the vehicle should be replaced |
|
2170 if (!p->engine_renew || |
|
2171 w->age - w->max_age < (p->engine_renew_months * 30) || // replace if engine is too old |
|
2172 w->max_age == 0) { // rail cars got a max age of 0 |
|
2173 if (!EngineHasReplacementForPlayer(p, w->engine_type)) // updates to a new model |
|
2174 continue; |
|
2175 } |
|
2176 |
|
2177 /* Now replace the vehicle */ |
|
2178 temp_cost = ReplaceVehicle(&w, flags, cost); |
|
2179 |
|
2180 if (flags & DC_EXEC && |
|
2181 (w->type != VEH_Train || w->u.rail.first_engine == INVALID_ENGINE)) { |
|
2182 /* now we bought a new engine and sold the old one. We need to fix the |
|
2183 * pointers in order to avoid pointing to the old one for trains: these |
|
2184 * pointers should point to the front engine and not the cars |
|
2185 */ |
|
2186 v = w; |
|
2187 } |
|
2188 |
|
2189 if (!CmdFailed(temp_cost)) { |
|
2190 cost += temp_cost; |
|
2191 } |
|
2192 } while (w->type == VEH_Train && (w = GetNextVehicle(w)) != NULL); |
|
2193 |
|
2194 if (!(flags & DC_EXEC) && (p->money64 < (int32)(cost + p->engine_renew_money) || cost == 0)) { |
|
2195 if (!check && p->money64 < (int32)(cost + p->engine_renew_money) && ( _local_player == v->owner ) && cost != 0) { |
|
2196 StringID message; |
|
2197 SetDParam(0, v->unitnumber); |
|
2198 switch (v->type) { |
|
2199 case VEH_Train: message = STR_TRAIN_AUTORENEW_FAILED; break; |
|
2200 case VEH_Road: message = STR_ROADVEHICLE_AUTORENEW_FAILED; break; |
|
2201 case VEH_Ship: message = STR_SHIP_AUTORENEW_FAILED; break; |
|
2202 case VEH_Aircraft: message = STR_AIRCRAFT_AUTORENEW_FAILED; break; |
|
2203 // This should never happen |
|
2204 default: NOT_REACHED(); message = 0; break; |
|
2205 } |
|
2206 |
|
2207 AddNewsItem(message, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0); |
|
2208 } |
|
2209 if (stopped) v->vehstatus &= ~VS_STOPPED; |
|
2210 if (display_costs) _current_player = OWNER_NONE; |
|
2211 return CMD_ERROR; |
|
2212 } |
|
2213 |
|
2214 if (flags & DC_EXEC) { |
|
2215 break; // we are done replacing since the loop ran once with DC_EXEC |
|
2216 } else if (check) { |
|
2217 /* It's a test only and we know that we can do this |
|
2218 * NOTE: payment for wagon removal is NOT included in this price */ |
|
2219 return cost; |
|
2220 } |
|
2221 // now we redo the loop, but this time we actually do stuff since we know that we can do it |
|
2222 flags |= DC_EXEC; |
|
2223 } |
|
2224 |
|
2225 /* If setting is on to try not to exceed the old length of the train with the replacement */ |
|
2226 if (v->type == VEH_Train && p->renew_keep_length) { |
|
2227 Vehicle *temp; |
|
2228 w = v; |
|
2229 |
|
2230 while (v->u.rail.cached_total_length > old_total_length) { |
|
2231 // the train is too long. We will remove cars one by one from the start of the train until it's short enough |
|
2232 while (w != NULL && !(RailVehInfo(w->engine_type)->flags&RVI_WAGON) ) { |
|
2233 w = GetNextVehicle(w); |
|
2234 } |
|
2235 if (w == NULL) { |
|
2236 // we failed to make the train short enough |
|
2237 SetDParam(0, v->unitnumber); |
|
2238 AddNewsItem(STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0); |
|
2239 break; |
|
2240 } |
|
2241 temp = w; |
|
2242 w = GetNextVehicle(w); |
|
2243 DoCommand(0, (INVALID_VEHICLE << 16) | temp->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
2244 MoveVehicleCargo(v, temp); |
|
2245 cost += DoCommand(0, temp->index, 0, DC_EXEC, CMD_SELL_RAIL_WAGON); |
|
2246 } |
|
2247 } |
|
2248 |
|
2249 if (stopped) v->vehstatus &= ~VS_STOPPED; |
|
2250 if (display_costs) { |
|
2251 if (IsLocalPlayer()) ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost); |
|
2252 _current_player = OWNER_NONE; |
|
2253 } |
|
2254 return cost; |
|
2255 } |
|
2256 |
|
2257 /* Extend the list size for BuildDepotVehicleList() */ |
|
2258 static inline void ExtendVehicleListSize(const Vehicle ***engine_list, uint16 *engine_list_length, uint16 step_size) |
|
2259 { |
|
2260 *engine_list_length = min(*engine_list_length + step_size, GetMaxVehicleIndex() + 1); |
|
2261 *engine_list = realloc((void*)*engine_list, (*engine_list_length) * sizeof((*engine_list)[0])); |
|
2262 } |
|
2263 |
|
2264 /** Generates a list of vehicles inside a depot |
|
2265 * Will enlarge allocated space for the list if they are too small, so it's ok to call with (pointer to NULL array, pointer to uninitised uint16, pointer to 0) |
|
2266 * If one of the lists is not needed (say wagons when finding ships), all the pointers regarding that list should be set to NULL |
|
2267 * @param Type type of vehicle |
|
2268 * @param tile The tile the depot is located in |
|
2269 * @param ***engine_list Pointer to a pointer to an array of vehicles in the depot (old list is freed and a new one is malloced) |
|
2270 * @param *engine_list_length Allocated size of engine_list. Needs to be set to 0 when engine_list points to a NULL array |
|
2271 * @param *engine_count The number of engines stored in the list |
|
2272 * @param ***wagon_list Pointer to a pointer to an array of free wagons in the depot (old list is freed and a new one is malloced) |
|
2273 * @param *wagon_list_length Allocated size of wagon_list. Needs to be set to 0 when wagon_list points to a NULL array |
|
2274 * @param *wagon_count The number of engines stored in the list |
|
2275 */ |
|
2276 void BuildDepotVehicleList(byte type, TileIndex tile, Vehicle ***engine_list, uint16 *engine_list_length, uint16 *engine_count, Vehicle ***wagon_list, uint16 *wagon_list_length, uint16 *wagon_count) |
|
2277 { |
|
2278 Vehicle *v; |
|
2279 |
|
2280 /* This function should never be called without an array to store results */ |
|
2281 assert(!(engine_list == NULL && type != VEH_Train)); |
|
2282 assert(!(type == VEH_Train && engine_list == NULL && wagon_list == NULL)); |
|
2283 |
|
2284 /* Both array and the length should either be NULL to disable the list or both should not be NULL */ |
|
2285 assert((engine_list == NULL && engine_list_length == NULL) || (engine_list != NULL && engine_list_length != NULL)); |
|
2286 assert((wagon_list == NULL && wagon_list_length == NULL) || (wagon_list != NULL && wagon_list_length != NULL)); |
|
2287 |
|
2288 assert(!(engine_list != NULL && engine_count == NULL)); |
|
2289 assert(!(wagon_list != NULL && wagon_count == NULL)); |
|
2290 |
|
2291 if (engine_count != NULL) *engine_count = 0; |
|
2292 if (wagon_count != NULL) *wagon_count = 0; |
|
2293 |
|
2294 switch (type) { |
|
2295 case VEH_Train: |
|
2296 FOR_ALL_VEHICLES(v) { |
|
2297 if (v->tile == tile && v->type == VEH_Train && v->u.rail.track == 0x80) { |
|
2298 if (IsFrontEngine(v)) { |
|
2299 if (engine_list == NULL) continue; |
|
2300 if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25); |
|
2301 (*engine_list)[(*engine_count)++] = v; |
|
2302 } else if (IsFreeWagon(v)) { |
|
2303 if (wagon_list == NULL) continue; |
|
2304 if (*wagon_count == *wagon_list_length) ExtendVehicleListSize((const Vehicle***)wagon_list, wagon_list_length, 25); |
|
2305 (*wagon_list)[(*wagon_count)++] = v; |
|
2306 } |
|
2307 } |
|
2308 } |
|
2309 break; |
|
2310 |
|
2311 case VEH_Road: |
|
2312 FOR_ALL_VEHICLES(v) { |
|
2313 if (v->tile == tile && v->type == VEH_Road && IsRoadVehInDepot(v)) { |
|
2314 if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25); |
|
2315 (*engine_list)[(*engine_count)++] = v; |
|
2316 } |
|
2317 } |
|
2318 break; |
|
2319 |
|
2320 case VEH_Ship: |
|
2321 FOR_ALL_VEHICLES(v) { |
|
2322 if (v->tile == tile && v->type == VEH_Ship && IsShipInDepot(v)) { |
|
2323 if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25); |
|
2324 (*engine_list)[(*engine_count)++] = v; |
|
2325 } |
|
2326 } |
|
2327 break; |
|
2328 |
|
2329 case VEH_Aircraft: |
|
2330 FOR_ALL_VEHICLES(v) { |
|
2331 if (v->tile == tile && |
|
2332 v->type == VEH_Aircraft && |
|
2333 v->subtype <= 2 && |
|
2334 v->vehstatus & VS_HIDDEN) { |
|
2335 if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25); |
|
2336 (*engine_list)[(*engine_count)++] = v; |
|
2337 } |
|
2338 } |
|
2339 break; |
|
2340 |
|
2341 default: NOT_REACHED(); |
|
2342 } |
|
2343 } |
|
2344 |
|
2345 /** |
|
2346 * @param sort_list list to store the list in. Either NULL or the length length_of_array tells |
|
2347 * @param length_of_array informs the length allocated for sort_list. This is not the same as the number of vehicles in the list. Needs to be 0 when sort_list is NULL |
|
2348 * @param type type of vehicle |
|
2349 * @param owner PlayerID of owner to generate a list for |
|
2350 * @param station index of station to generate a list for. INVALID_STATION when not used |
|
2351 * @param order index of oder to generate a list for. INVALID_ORDER when not used |
|
2352 * @param window_type tells what kind of window the list is for. Use the VLW flags in vehicle_gui.h |
|
2353 * @return the number of vehicles added to the list |
|
2354 */ |
|
2355 uint GenerateVehicleSortList(const Vehicle ***sort_list, uint16 *length_of_array, byte type, PlayerID owner, StationID station, OrderID order, uint16 depot_airport_index, uint16 window_type) |
|
2356 { |
|
2357 const uint subtype = (type != VEH_Aircraft) ? Train_Front : 2; |
|
2358 uint n = 0; |
|
2359 const Vehicle *v; |
|
2360 |
|
2361 switch (window_type) { |
|
2362 case VLW_STATION_LIST: { |
|
2363 FOR_ALL_VEHICLES(v) { |
|
2364 if (v->type == type && ( |
|
2365 (type == VEH_Train && IsFrontEngine(v)) || |
|
2366 (type != VEH_Train && v->subtype <= subtype))) { |
|
2367 const Order *order; |
|
2368 |
|
2369 FOR_VEHICLE_ORDERS(v, order) { |
|
2370 if (order->type == OT_GOTO_STATION && order->dest == station) { |
|
2371 if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, 50); |
|
2372 (*sort_list)[n++] = v; |
|
2373 break; |
|
2374 } |
|
2375 } |
|
2376 } |
|
2377 } |
|
2378 break; |
|
2379 } |
|
2380 |
|
2381 case VLW_SHARED_ORDERS: { |
|
2382 FOR_ALL_VEHICLES(v) { |
|
2383 /* Find a vehicle with the order in question */ |
|
2384 if (v->orders != NULL && v->orders->index == order) break; |
|
2385 } |
|
2386 |
|
2387 if (v != NULL && v->orders != NULL && v->orders->index == order) { |
|
2388 /* Only try to make the list if we found a vehicle using the order in question */ |
|
2389 for (v = GetFirstVehicleFromSharedList(v); v != NULL; v = v->next_shared) { |
|
2390 if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, 25); |
|
2391 (*sort_list)[n++] = v; |
|
2392 } |
|
2393 } |
|
2394 break; |
|
2395 } |
|
2396 |
|
2397 case VLW_STANDARD: { |
|
2398 FOR_ALL_VEHICLES(v) { |
|
2399 if (v->type == type && v->owner == owner && ( |
|
2400 (type == VEH_Train && IsFrontEngine(v)) || |
|
2401 (type != VEH_Train && v->subtype <= subtype))) { |
|
2402 /* TODO find a better estimate on the total number of vehicles for current player */ |
|
2403 if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, GetNumVehicles()/4); |
|
2404 (*sort_list)[n++] = v; |
|
2405 } |
|
2406 } |
|
2407 break; |
|
2408 } |
|
2409 |
|
2410 case VLW_DEPOT_LIST: { |
|
2411 FOR_ALL_VEHICLES(v) { |
|
2412 if (v->type == type && ( |
|
2413 (type == VEH_Train && IsFrontEngine(v)) || |
|
2414 (type != VEH_Train && v->subtype <= subtype))) { |
|
2415 const Order *order; |
|
2416 |
|
2417 FOR_VEHICLE_ORDERS(v, order) { |
|
2418 if (order->type == OT_GOTO_DEPOT && order->dest == depot_airport_index) { |
|
2419 if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, 25); |
|
2420 (*sort_list)[n++] = v; |
|
2421 break; |
|
2422 } |
|
2423 } |
|
2424 } |
|
2425 } |
|
2426 break; |
|
2427 } |
|
2428 |
|
2429 default: NOT_REACHED(); break; |
|
2430 } |
|
2431 |
|
2432 if ((n + 100) < *length_of_array) { |
|
2433 /* We allocated way too much for sort_list. |
|
2434 * Now we will reduce how much we allocated. |
|
2435 * We will still make it have room for 50 extra vehicles to prevent having |
|
2436 * to move the whole array if just one vehicle is added later */ |
|
2437 *length_of_array = n + 50; |
|
2438 *sort_list = realloc((void*)*sort_list, (*length_of_array) * sizeof((*sort_list)[0])); |
|
2439 } |
|
2440 |
|
2441 return n; |
|
2442 } |
|
2443 |
|
2444 /** send all vehicles of type to depots |
|
2445 * @param type type of vehicle |
|
2446 * @param flags the flags used for DoCommand() |
|
2447 * @param service should the vehicles only get service in the depots |
|
2448 * @param owner PlayerID of owner of the vehicles to send |
|
2449 * @param VLW_flag tells what kind of list requested the goto depot |
|
2450 * @return 0 for success and CMD_ERROR if no vehicle is able to go to depot |
|
2451 */ |
|
2452 int32 SendAllVehiclesToDepot(byte type, uint32 flags, bool service, PlayerID owner, uint16 vlw_flag, uint32 id) |
|
2453 { |
|
2454 const Vehicle **sort_list = NULL; |
|
2455 uint n, i; |
|
2456 uint16 array_length = 0; |
|
2457 |
|
2458 n = GenerateVehicleSortList(&sort_list, &array_length, type, owner, id, id, id, vlw_flag); |
|
2459 |
|
2460 /* Send all the vehicles to a depot */ |
|
2461 for (i = 0; i < n; i++) { |
|
2462 const Vehicle *v = sort_list[i]; |
|
2463 int32 ret = DoCommand(v->tile, v->index, service | DEPOT_DONT_CANCEL, flags, CMD_SEND_TO_DEPOT(type)); |
|
2464 |
|
2465 /* Return 0 if DC_EXEC is not set this is a valid goto depot command) |
|
2466 * In this case we know that at least one vehicle can be sent to a depot |
|
2467 * and we will issue the command. We can now safely quit the loop, knowing |
|
2468 * it will succeed at least once. With DC_EXEC we really need to send them to the depot */ |
|
2469 if (!CmdFailed(ret) && !(flags & DC_EXEC)) { |
|
2470 free((void*)sort_list); |
|
2471 return 0; |
|
2472 } |
|
2473 } |
|
2474 |
|
2475 free((void*)sort_list); |
|
2476 return (flags & DC_EXEC) ? 0 : CMD_ERROR; |
|
2477 } |
|
2478 |
|
2479 bool IsVehicleInDepot(const Vehicle *v) |
|
2480 { |
|
2481 switch (v->type) { |
|
2482 case VEH_Train: return CheckTrainInDepot(v, false) != -1; |
|
2483 case VEH_Road: return IsRoadVehInDepot(v); |
|
2484 case VEH_Ship: return IsShipInDepot(v); |
|
2485 case VEH_Aircraft: return IsAircraftInHangar(v); |
|
2486 default: NOT_REACHED(); |
|
2487 } |
|
2488 return false; |
|
2489 } |
|
2490 |
|
2491 void VehicleEnterDepot(Vehicle *v) |
|
2492 { |
|
2493 switch (v->type) { |
|
2494 case VEH_Train: |
|
2495 InvalidateWindowClasses(WC_TRAINS_LIST); |
|
2496 if (!IsFrontEngine(v)) v = GetFirstVehicleInChain(v); |
|
2497 UpdateSignalsOnSegment(v->tile, GetRailDepotDirection(v->tile)); |
|
2498 v->load_unload_time_rem = 0; |
|
2499 break; |
|
2500 |
|
2501 case VEH_Road: |
|
2502 InvalidateWindowClasses(WC_ROADVEH_LIST); |
|
2503 v->u.road.state = 254; |
|
2504 break; |
|
2505 |
|
2506 case VEH_Ship: |
|
2507 InvalidateWindowClasses(WC_SHIPS_LIST); |
|
2508 v->u.ship.state = 0x80; |
|
2509 RecalcShipStuff(v); |
|
2510 break; |
|
2511 |
|
2512 case VEH_Aircraft: |
|
2513 InvalidateWindowClasses(WC_AIRCRAFT_LIST); |
|
2514 HandleAircraftEnterHangar(v); |
|
2515 break; |
|
2516 default: NOT_REACHED(); |
|
2517 } |
|
2518 |
|
2519 if (v->type != VEH_Train) { |
|
2520 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters. |
|
2521 * We only increase the number of vehicles when the first one enters, so we will not need to search for more vehicles in the depot */ |
|
2522 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); |
|
2523 } |
|
2524 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
|
2525 |
|
2526 v->vehstatus |= VS_HIDDEN; |
|
2527 v->cur_speed = 0; |
|
2528 |
|
2529 VehicleServiceInDepot(v); |
|
2530 |
|
2531 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT); |
|
2532 |
|
2533 if (v->current_order.type == OT_GOTO_DEPOT) { |
|
2534 Order t; |
|
2535 |
|
2536 InvalidateWindow(WC_VEHICLE_VIEW, v->index); |
|
2537 |
|
2538 t = v->current_order; |
|
2539 v->current_order.type = OT_DUMMY; |
|
2540 v->current_order.flags = 0; |
|
2541 |
|
2542 if (t.refit_cargo < NUM_CARGO) { |
|
2543 int32 cost; |
|
2544 |
|
2545 _current_player = v->owner; |
|
2546 cost = DoCommand(v->tile, v->index, t.refit_cargo | t.refit_subtype << 8, DC_EXEC, CMD_REFIT_VEH(v->type)); |
|
2547 |
|
2548 if (CmdFailed(cost)) { |
|
2549 v->leave_depot_instantly = false; // We ensure that the vehicle stays in the depot |
|
2550 if (v->owner == _local_player) { |
|
2551 /* Notify the user that we stopped the vehicle */ |
|
2552 SetDParam(0, _vehicle_type_names[v->type - 0x10]); |
|
2553 SetDParam(1, v->unitnumber); |
|
2554 AddNewsItem(STR_ORDER_REFIT_FAILED, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0); |
|
2555 } |
|
2556 } else if (v->owner == _local_player && cost != 0) { |
|
2557 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost); |
|
2558 } |
|
2559 } |
|
2560 |
|
2561 if (HASBIT(t.flags, OFB_PART_OF_ORDERS)) { |
|
2562 /* Part of orders */ |
|
2563 if (v->type == VEH_Train) v->u.rail.days_since_order_progr = 0; |
|
2564 v->cur_order_index++; |
|
2565 } else if (HASBIT(t.flags, OFB_HALT_IN_DEPOT)) { |
|
2566 /* Force depot visit */ |
|
2567 v->vehstatus |= VS_STOPPED; |
|
2568 if (v->owner == _local_player) { |
|
2569 StringID string; |
|
2570 |
|
2571 switch (v->type) { |
|
2572 case VEH_Train: string = STR_8814_TRAIN_IS_WAITING_IN_DEPOT; break; |
|
2573 case VEH_Road: string = STR_9016_ROAD_VEHICLE_IS_WAITING; break; |
|
2574 case VEH_Ship: string = STR_981C_SHIP_IS_WAITING_IN_DEPOT; break; |
|
2575 case VEH_Aircraft: string = STR_A014_AIRCRAFT_IS_WAITING_IN; break; |
|
2576 default: NOT_REACHED(); string = STR_EMPTY; // Set the string to something to avoid a compiler warning |
|
2577 } |
|
2578 |
|
2579 SetDParam(0, v->unitnumber); |
|
2580 AddNewsItem(string, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0); |
|
2581 } |
|
2582 } |
|
2583 } |
|
2584 } |
|
2585 |
|
2586 /** Give a custom name to your vehicle |
|
2587 * @param tile unused |
|
2588 * @param p1 vehicle ID to name |
|
2589 * @param p2 unused |
|
2590 */ |
|
2591 int32 CmdNameVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
2592 { |
|
2593 Vehicle *v; |
|
2594 StringID str; |
|
2595 |
|
2596 if (!IsValidVehicleID(p1) || _cmd_text[0] == '\0') return CMD_ERROR; |
|
2597 |
|
2598 v = GetVehicle(p1); |
|
2599 |
|
2600 if (!CheckOwnership(v->owner)) return CMD_ERROR; |
|
2601 |
|
2602 str = AllocateNameUnique(_cmd_text, 2); |
|
2603 if (str == 0) return CMD_ERROR; |
|
2604 |
|
2605 if (flags & DC_EXEC) { |
|
2606 StringID old_str = v->string_id; |
|
2607 v->string_id = str; |
|
2608 DeleteName(old_str); |
|
2609 ResortVehicleLists(); |
|
2610 MarkWholeScreenDirty(); |
|
2611 } else { |
|
2612 DeleteName(str); |
|
2613 } |
|
2614 |
|
2615 return 0; |
|
2616 } |
|
2617 |
|
2618 |
|
2619 /** Change the service interval of a vehicle |
|
2620 * @param tile unused |
|
2621 * @param p1 vehicle ID that is being service-interval-changed |
|
2622 * @param p2 new service interval |
|
2623 */ |
|
2624 int32 CmdChangeServiceInt(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
|
2625 { |
|
2626 Vehicle* v; |
|
2627 uint16 serv_int = GetServiceIntervalClamped(p2); /* Double check the service interval from the user-input */ |
|
2628 |
|
2629 if (serv_int != p2 || !IsValidVehicleID(p1)) return CMD_ERROR; |
|
2630 |
|
2631 v = GetVehicle(p1); |
|
2632 |
|
2633 if (!CheckOwnership(v->owner)) return CMD_ERROR; |
|
2634 |
|
2635 if (flags & DC_EXEC) { |
|
2636 v->service_interval = serv_int; |
|
2637 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
|
2638 } |
|
2639 |
|
2640 return 0; |
|
2641 } |
|
2642 |
|
2643 |
|
2644 static Rect _old_vehicle_coords; |
|
2645 |
|
2646 void BeginVehicleMove(Vehicle *v) { |
|
2647 _old_vehicle_coords.left = v->left_coord; |
|
2648 _old_vehicle_coords.top = v->top_coord; |
|
2649 _old_vehicle_coords.right = v->right_coord; |
|
2650 _old_vehicle_coords.bottom = v->bottom_coord; |
|
2651 } |
|
2652 |
|
2653 void EndVehicleMove(Vehicle *v) |
|
2654 { |
|
2655 MarkAllViewportsDirty( |
|
2656 min(_old_vehicle_coords.left,v->left_coord), |
|
2657 min(_old_vehicle_coords.top,v->top_coord), |
|
2658 max(_old_vehicle_coords.right,v->right_coord)+1, |
|
2659 max(_old_vehicle_coords.bottom,v->bottom_coord)+1 |
|
2660 ); |
|
2661 } |
|
2662 |
|
2663 /* returns true if staying in the same tile */ |
|
2664 bool GetNewVehiclePos(const Vehicle *v, GetNewVehiclePosResult *gp) |
|
2665 { |
|
2666 static const int8 _delta_coord[16] = { |
|
2667 -1,-1,-1, 0, 1, 1, 1, 0, /* x */ |
|
2668 -1, 0, 1, 1, 1, 0,-1,-1, /* y */ |
|
2669 }; |
|
2670 |
|
2671 int x = v->x_pos + _delta_coord[v->direction]; |
|
2672 int y = v->y_pos + _delta_coord[v->direction + 8]; |
|
2673 |
|
2674 gp->x = x; |
|
2675 gp->y = y; |
|
2676 gp->old_tile = v->tile; |
|
2677 gp->new_tile = TileVirtXY(x, y); |
|
2678 return gp->old_tile == gp->new_tile; |
|
2679 } |
|
2680 |
|
2681 static const Direction _new_direction_table[] = { |
|
2682 DIR_N , DIR_NW, DIR_W , |
|
2683 DIR_NE, DIR_SE, DIR_SW, |
|
2684 DIR_E , DIR_SE, DIR_S |
|
2685 }; |
|
2686 |
|
2687 Direction GetDirectionTowards(const Vehicle* v, int x, int y) |
|
2688 { |
|
2689 Direction dir; |
|
2690 DirDiff dirdiff; |
|
2691 int i = 0; |
|
2692 |
|
2693 if (y >= v->y_pos) { |
|
2694 if (y != v->y_pos) i+=3; |
|
2695 i+=3; |
|
2696 } |
|
2697 |
|
2698 if (x >= v->x_pos) { |
|
2699 if (x != v->x_pos) i++; |
|
2700 i++; |
|
2701 } |
|
2702 |
|
2703 dir = v->direction; |
|
2704 |
|
2705 dirdiff = DirDifference(_new_direction_table[i], dir); |
|
2706 if (dirdiff == DIRDIFF_SAME) return dir; |
|
2707 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT); |
|
2708 } |
|
2709 |
|
2710 Trackdir GetVehicleTrackdir(const Vehicle* v) |
|
2711 { |
|
2712 if (v->vehstatus & VS_CRASHED) return 0xFF; |
|
2713 |
|
2714 switch (v->type) { |
|
2715 case VEH_Train: |
|
2716 if (v->u.rail.track == 0x80) /* We'll assume the train is facing outwards */ |
|
2717 return DiagdirToDiagTrackdir(GetRailDepotDirection(v->tile)); /* Train in depot */ |
|
2718 |
|
2719 if (v->u.rail.track == 0x40) /* train in tunnel, so just use his direction and assume a diagonal track */ |
|
2720 return DiagdirToDiagTrackdir(DirToDiagDir(v->direction)); |
|
2721 |
|
2722 return TrackDirectionToTrackdir(FIND_FIRST_BIT(v->u.rail.track),v->direction); |
|
2723 |
|
2724 case VEH_Ship: |
|
2725 if (IsShipInDepot(v)) |
|
2726 /* We'll assume the ship is facing outwards */ |
|
2727 return DiagdirToDiagTrackdir(GetShipDepotDirection(v->tile)); |
|
2728 |
|
2729 return TrackDirectionToTrackdir(FIND_FIRST_BIT(v->u.ship.state),v->direction); |
|
2730 |
|
2731 case VEH_Road: |
|
2732 if (IsRoadVehInDepot(v)) /* We'll assume the road vehicle is facing outwards */ |
|
2733 return DiagdirToDiagTrackdir(GetRoadDepotDirection(v->tile)); |
|
2734 |
|
2735 if (IsRoadStopTile(v->tile)) /* We'll assume the road vehicle is facing outwards */ |
|
2736 return DiagdirToDiagTrackdir(GetRoadStopDir(v->tile)); /* Road vehicle in a station */ |
|
2737 |
|
2738 /* If vehicle's state is a valid track direction (vehicle is not turning around) return it */ |
|
2739 if ((v->u.road.state & 7) < 6) return v->u.road.state; |
|
2740 |
|
2741 /* Vehicle is turning around, get the direction from vehicle's direction */ |
|
2742 return DiagdirToDiagTrackdir(DirToDiagDir(v->direction)); |
|
2743 |
|
2744 /* case VEH_Aircraft: case VEH_Special: case VEH_Disaster: */ |
|
2745 default: return 0xFF; |
|
2746 } |
|
2747 } |
|
2748 /* Return value has bit 0x2 set, when the vehicle enters a station. Then, |
|
2749 * result << 8 contains the id of the station entered. If the return value has |
|
2750 * bit 0x8 set, the vehicle could not and did not enter the tile. Are there |
|
2751 * other bits that can be set? */ |
|
2752 uint32 VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y) |
|
2753 { |
|
2754 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y); |
|
2755 } |
|
2756 |
|
2757 UnitID GetFreeUnitNumber(byte type) |
|
2758 { |
|
2759 UnitID unit, max = 0; |
|
2760 const Vehicle *u; |
|
2761 static bool *cache = NULL; |
|
2762 static UnitID gmax = 0; |
|
2763 |
|
2764 switch (type) { |
|
2765 case VEH_Train: max = _patches.max_trains; break; |
|
2766 case VEH_Road: max = _patches.max_roadveh; break; |
|
2767 case VEH_Ship: max = _patches.max_ships; break; |
|
2768 case VEH_Aircraft: max = _patches.max_aircraft; break; |
|
2769 default: NOT_REACHED(); |
|
2770 } |
|
2771 |
|
2772 if (max == 0) { |
|
2773 /* we can't build any of this kind of vehicle, so we just return 1 instead of looking for a free number |
|
2774 * a max of 0 will cause the following code to write to a NULL pointer |
|
2775 * We know that 1 is bigger than the max allowed vehicle number, so it's the same as returning something, that is too big |
|
2776 */ |
|
2777 return 1; |
|
2778 } |
|
2779 |
|
2780 if (max > gmax) { |
|
2781 gmax = max; |
|
2782 free(cache); |
|
2783 cache = malloc((max + 1) * sizeof(*cache)); |
|
2784 } |
|
2785 |
|
2786 // Clear the cache |
|
2787 memset(cache, 0, (max + 1) * sizeof(*cache)); |
|
2788 |
|
2789 // Fill the cache |
|
2790 FOR_ALL_VEHICLES(u) { |
|
2791 if (u->type == type && u->owner == _current_player && u->unitnumber != 0 && u->unitnumber <= max) |
|
2792 cache[u->unitnumber] = true; |
|
2793 } |
|
2794 |
|
2795 // Find the first unused unit number |
|
2796 for (unit = 1; unit <= max; unit++) { |
|
2797 if (!cache[unit]) break; |
|
2798 } |
|
2799 |
|
2800 return unit; |
|
2801 } |
|
2802 |
|
2803 static PalSpriteID GetEngineColourMap(EngineID engine_type, PlayerID player, EngineID parent_engine_type, CargoID cargo_type) |
|
2804 { |
|
2805 SpriteID map; |
|
2806 const Player *p = GetPlayer(player); |
|
2807 LiveryScheme scheme = LS_DEFAULT; |
|
2808 |
|
2809 /* The default livery is always available for use, but its in_use flag determines |
|
2810 * whether any _other_ liveries are in use. */ |
|
2811 if (p->livery[LS_DEFAULT].in_use && (_patches.liveries == 2 || (_patches.liveries == 1 && player == _local_player))) { |
|
2812 /* Determine the livery scheme to use */ |
|
2813 switch (GetEngine(engine_type)->type) { |
|
2814 case VEH_Train: { |
|
2815 switch (_engine_info[engine_type].railtype) { |
|
2816 case RAILTYPE_RAIL: |
|
2817 case RAILTYPE_ELECTRIC: |
|
2818 { |
|
2819 const RailVehicleInfo *rvi = RailVehInfo(engine_type); |
|
2820 |
|
2821 if (cargo_type == CT_INVALID) cargo_type = rvi->cargo_type; |
|
2822 if (rvi->flags & RVI_WAGON) { |
|
2823 if (cargo_type == CT_PASSENGERS || cargo_type == CT_MAIL || cargo_type == CT_VALUABLES) { |
|
2824 if (parent_engine_type == INVALID_ENGINE) { |
|
2825 scheme = LS_PASSENGER_WAGON_STEAM; |
|
2826 } else { |
|
2827 switch (RailVehInfo(parent_engine_type)->engclass) { |
|
2828 case 0: scheme = LS_PASSENGER_WAGON_STEAM; break; |
|
2829 case 1: scheme = LS_PASSENGER_WAGON_DIESEL; break; |
|
2830 case 2: scheme = LS_PASSENGER_WAGON_ELECTRIC; break; |
|
2831 } |
|
2832 } |
|
2833 } else { |
|
2834 scheme = LS_FREIGHT_WAGON; |
|
2835 } |
|
2836 } else { |
|
2837 bool is_mu = HASBIT(_engine_info[engine_type].misc_flags, EF_RAIL_IS_MU); |
|
2838 |
|
2839 switch (rvi->engclass) { |
|
2840 case 0: scheme = LS_STEAM; break; |
|
2841 case 1: scheme = is_mu ? LS_DMU : LS_DIESEL; break; |
|
2842 case 2: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break; |
|
2843 } |
|
2844 } |
|
2845 break; |
|
2846 } |
|
2847 |
|
2848 case RAILTYPE_MONO: scheme = LS_MONORAIL; break; |
|
2849 case RAILTYPE_MAGLEV: scheme = LS_MAGLEV; break; |
|
2850 } |
|
2851 break; |
|
2852 } |
|
2853 |
|
2854 case VEH_Road: { |
|
2855 const RoadVehicleInfo *rvi = RoadVehInfo(engine_type); |
|
2856 if (cargo_type == CT_INVALID) cargo_type = rvi->cargo_type; |
|
2857 scheme = (cargo_type == CT_PASSENGERS) ? LS_BUS : LS_TRUCK; |
|
2858 break; |
|
2859 } |
|
2860 |
|
2861 case VEH_Ship: { |
|
2862 const ShipVehicleInfo *svi = ShipVehInfo(engine_type); |
|
2863 if (cargo_type == CT_INVALID) cargo_type = svi->cargo_type; |
|
2864 scheme = (cargo_type == CT_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP; |
|
2865 break; |
|
2866 } |
|
2867 |
|
2868 case VEH_Aircraft: { |
|
2869 const AircraftVehicleInfo *avi = AircraftVehInfo(engine_type); |
|
2870 if (cargo_type == CT_INVALID) cargo_type = CT_PASSENGERS; |
|
2871 switch (avi->subtype) { |
|
2872 case 0: scheme = LS_HELICOPTER; break; |
|
2873 case 1: scheme = LS_SMALL_PLANE; break; |
|
2874 case 3: scheme = LS_LARGE_PLANE; break; |
|
2875 } |
|
2876 break; |
|
2877 } |
|
2878 } |
|
2879 |
|
2880 /* Switch back to the default scheme if the resolved scheme is not in use */ |
|
2881 if (!p->livery[scheme].in_use) scheme = LS_DEFAULT; |
|
2882 } |
|
2883 |
|
2884 map = HASBIT(EngInfo(engine_type)->misc_flags, EF_USES_2CC) ? |
|
2885 (SPR_2CCMAP_BASE + p->livery[scheme].colour1 + p->livery[scheme].colour2 * 16) : |
|
2886 (PALETTE_RECOLOR_START + p->livery[scheme].colour1); |
|
2887 |
|
2888 return SPRITE_PALETTE(map << PALETTE_SPRITE_START); |
|
2889 } |
|
2890 |
|
2891 PalSpriteID GetEnginePalette(EngineID engine_type, PlayerID player) |
|
2892 { |
|
2893 return GetEngineColourMap(engine_type, player, INVALID_ENGINE, CT_INVALID); |
|
2894 } |
|
2895 |
|
2896 PalSpriteID GetVehiclePalette(const Vehicle *v) |
|
2897 { |
|
2898 if (v->type == VEH_Train) { |
|
2899 return GetEngineColourMap( |
|
2900 (v->u.rail.first_engine != INVALID_ENGINE && (IsArticulatedPart(v) || UsesWagonOverride(v))) ? |
|
2901 v->u.rail.first_engine : v->engine_type, |
|
2902 v->owner, |
|
2903 v->u.rail.first_engine, |
|
2904 v->cargo_type); |
|
2905 } |
|
2906 |
|
2907 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v->cargo_type); |
|
2908 } |
|
2909 |
|
2910 // Save and load of vehicles |
|
2911 const SaveLoad _common_veh_desc[] = { |
|
2912 SLE_VAR(Vehicle, subtype, SLE_UINT8), |
|
2913 |
|
2914 SLE_REF(Vehicle, next, REF_VEHICLE_OLD), |
|
2915 SLE_VAR(Vehicle, string_id, SLE_STRINGID), |
|
2916 SLE_CONDVAR(Vehicle, unitnumber, SLE_FILE_U8 | SLE_VAR_U16, 0, 7), |
|
2917 SLE_CONDVAR(Vehicle, unitnumber, SLE_UINT16, 8, SL_MAX_VERSION), |
|
2918 SLE_VAR(Vehicle, owner, SLE_UINT8), |
|
2919 SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), |
|
2920 SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION), |
|
2921 SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), |
|
2922 SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, 6, SL_MAX_VERSION), |
|
2923 |
|
2924 SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), |
|
2925 SLE_CONDVAR(Vehicle, x_pos, SLE_UINT32, 6, SL_MAX_VERSION), |
|
2926 SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), |
|
2927 SLE_CONDVAR(Vehicle, y_pos, SLE_UINT32, 6, SL_MAX_VERSION), |
|
2928 SLE_VAR(Vehicle, z_pos, SLE_UINT8), |
|
2929 SLE_VAR(Vehicle, direction, SLE_UINT8), |
|
2930 |
|
2931 SLE_VAR(Vehicle, cur_image, SLE_UINT16), |
|
2932 SLE_VAR(Vehicle, spritenum, SLE_UINT8), |
|
2933 SLE_VAR(Vehicle, sprite_width, SLE_UINT8), |
|
2934 SLE_VAR(Vehicle, sprite_height, SLE_UINT8), |
|
2935 SLE_VAR(Vehicle, z_height, SLE_UINT8), |
|
2936 SLE_VAR(Vehicle, x_offs, SLE_INT8), |
|
2937 SLE_VAR(Vehicle, y_offs, SLE_INT8), |
|
2938 SLE_VAR(Vehicle, engine_type, SLE_UINT16), |
|
2939 |
|
2940 SLE_VAR(Vehicle, max_speed, SLE_UINT16), |
|
2941 SLE_VAR(Vehicle, cur_speed, SLE_UINT16), |
|
2942 SLE_VAR(Vehicle, subspeed, SLE_UINT8), |
|
2943 SLE_VAR(Vehicle, acceleration, SLE_UINT8), |
|
2944 SLE_VAR(Vehicle, progress, SLE_UINT8), |
|
2945 |
|
2946 SLE_VAR(Vehicle, vehstatus, SLE_UINT8), |
|
2947 SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), |
|
2948 SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, 5, SL_MAX_VERSION), |
|
2949 |
|
2950 SLE_VAR(Vehicle, cargo_type, SLE_UINT8), |
|
2951 SLE_CONDVAR(Vehicle, cargo_subtype, SLE_UINT8, 35, SL_MAX_VERSION), |
|
2952 SLE_VAR(Vehicle, cargo_days, SLE_UINT8), |
|
2953 SLE_CONDVAR(Vehicle, cargo_source, SLE_FILE_U8 | SLE_VAR_U16, 0, 6), |
|
2954 SLE_CONDVAR(Vehicle, cargo_source, SLE_UINT16, 7, SL_MAX_VERSION), |
|
2955 SLE_VAR(Vehicle, cargo_cap, SLE_UINT16), |
|
2956 SLE_VAR(Vehicle, cargo_count, SLE_UINT16), |
|
2957 |
|
2958 SLE_VAR(Vehicle, day_counter, SLE_UINT8), |
|
2959 SLE_VAR(Vehicle, tick_counter, SLE_UINT8), |
|
2960 |
|
2961 SLE_VAR(Vehicle, cur_order_index, SLE_UINT8), |
|
2962 SLE_VAR(Vehicle, num_orders, SLE_UINT8), |
|
2963 |
|
2964 /* This next line is for version 4 and prior compatibility.. it temporarily reads |
|
2965 type and flags (which were both 4 bits) into type. Later on this is |
|
2966 converted correctly */ |
|
2967 SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, type), SLE_UINT8, 0, 4), |
|
2968 SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4), |
|
2969 |
|
2970 /* Orders for version 5 and on */ |
|
2971 SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, type), SLE_UINT8, 5, SL_MAX_VERSION), |
|
2972 SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, flags), SLE_UINT8, 5, SL_MAX_VERSION), |
|
2973 SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, dest), SLE_UINT16, 5, SL_MAX_VERSION), |
|
2974 |
|
2975 /* Refit in current order */ |
|
2976 SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, refit_cargo), SLE_UINT8, 36, SL_MAX_VERSION), |
|
2977 SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, refit_subtype), SLE_UINT8, 36, SL_MAX_VERSION), |
|
2978 |
|
2979 SLE_REF(Vehicle, orders, REF_ORDER), |
|
2980 |
|
2981 SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), |
|
2982 SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION), |
|
2983 SLE_CONDVAR(Vehicle, max_age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), |
|
2984 SLE_CONDVAR(Vehicle, max_age, SLE_INT32, 31, SL_MAX_VERSION), |
|
2985 SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), |
|
2986 SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32, 31, SL_MAX_VERSION), |
|
2987 SLE_CONDVAR(Vehicle, service_interval, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), |
|
2988 SLE_CONDVAR(Vehicle, service_interval, SLE_INT32, 31, SL_MAX_VERSION), |
|
2989 SLE_VAR(Vehicle, reliability, SLE_UINT16), |
|
2990 SLE_VAR(Vehicle, reliability_spd_dec, SLE_UINT16), |
|
2991 SLE_VAR(Vehicle, breakdown_ctr, SLE_UINT8), |
|
2992 SLE_VAR(Vehicle, breakdown_delay, SLE_UINT8), |
|
2993 SLE_VAR(Vehicle, breakdowns_since_last_service, SLE_UINT8), |
|
2994 SLE_VAR(Vehicle, breakdown_chance, SLE_UINT8), |
|
2995 SLE_CONDVAR(Vehicle, build_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), |
|
2996 SLE_CONDVAR(Vehicle, build_year, SLE_INT32, 31, SL_MAX_VERSION), |
|
2997 |
|
2998 SLE_VAR(Vehicle, load_unload_time_rem, SLE_UINT16), |
|
2999 SLE_CONDVAR(Vehicle, load_status, SLE_UINT8, 40, SL_MAX_VERSION), |
|
3000 |
|
3001 SLE_VAR(Vehicle, profit_this_year, SLE_INT32), |
|
3002 SLE_VAR(Vehicle, profit_last_year, SLE_INT32), |
|
3003 SLE_VAR(Vehicle, value, SLE_UINT32), |
|
3004 |
|
3005 SLE_VAR(Vehicle, random_bits, SLE_UINT8), |
|
3006 SLE_VAR(Vehicle, waiting_triggers, SLE_UINT8), |
|
3007 |
|
3008 SLE_REF(Vehicle, next_shared, REF_VEHICLE), |
|
3009 SLE_REF(Vehicle, prev_shared, REF_VEHICLE), |
|
3010 |
|
3011 // reserve extra space in savegame here. (currently 10 bytes) |
|
3012 SLE_CONDNULL(10, 2, SL_MAX_VERSION), |
|
3013 |
|
3014 SLE_END() |
|
3015 }; |
|
3016 |
|
3017 |
|
3018 static const SaveLoad _train_desc[] = { |
|
3019 SLE_WRITEBYTE(Vehicle, type, VEH_Train, 0), // Train type. VEH_Train in mem, 0 in file. |
|
3020 SLE_INCLUDEX(0, INC_VEHICLE_COMMON), |
|
3021 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRail, crash_anim_pos), SLE_UINT16), |
|
3022 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRail, force_proceed), SLE_UINT8), |
|
3023 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRail, railtype), SLE_UINT8), |
|
3024 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRail, track), SLE_UINT8), |
|
3025 |
|
3026 SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleRail, flags), SLE_UINT8, 2, SL_MAX_VERSION), |
|
3027 SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleRail, days_since_order_progr), SLE_UINT16, 2, SL_MAX_VERSION), |
|
3028 |
|
3029 SLE_CONDNULL(2, 2, 19), |
|
3030 // reserve extra space in savegame here. (currently 11 bytes) |
|
3031 SLE_CONDNULL(11, 2, SL_MAX_VERSION), |
|
3032 |
|
3033 SLE_END() |
|
3034 }; |
|
3035 |
|
3036 static const SaveLoad _roadveh_desc[] = { |
|
3037 SLE_WRITEBYTE(Vehicle, type, VEH_Road, 1), // Road type. VEH_Road in mem, 1 in file. |
|
3038 SLE_INCLUDEX(0, INC_VEHICLE_COMMON), |
|
3039 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, state), SLE_UINT8), |
|
3040 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, frame), SLE_UINT8), |
|
3041 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, blocked_ctr), SLE_UINT16), |
|
3042 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, overtaking), SLE_UINT8), |
|
3043 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, overtaking_ctr), SLE_UINT8), |
|
3044 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, crashed_ctr), SLE_UINT16), |
|
3045 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, reverse_ctr), SLE_UINT8), |
|
3046 |
|
3047 SLE_CONDREFX(offsetof(Vehicle, u) + offsetof(VehicleRoad, slot), REF_ROADSTOPS, 6, SL_MAX_VERSION), |
|
3048 SLE_CONDNULL(1, 6, SL_MAX_VERSION), |
|
3049 SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, slot_age), SLE_UINT8, 6, SL_MAX_VERSION), |
|
3050 // reserve extra space in savegame here. (currently 16 bytes) |
|
3051 SLE_CONDNULL(16, 2, SL_MAX_VERSION), |
|
3052 |
|
3053 SLE_END() |
|
3054 }; |
|
3055 |
|
3056 static const SaveLoad _ship_desc[] = { |
|
3057 SLE_WRITEBYTE(Vehicle, type, VEH_Ship, 2), // Ship type. VEH_Ship in mem, 2 in file. |
|
3058 SLE_INCLUDEX(0, INC_VEHICLE_COMMON), |
|
3059 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleShip, state), SLE_UINT8), |
|
3060 |
|
3061 // reserve extra space in savegame here. (currently 16 bytes) |
|
3062 SLE_CONDNULL(16, 2, SL_MAX_VERSION), |
|
3063 |
|
3064 SLE_END() |
|
3065 }; |
|
3066 |
|
3067 static const SaveLoad _aircraft_desc[] = { |
|
3068 SLE_WRITEBYTE(Vehicle, type, VEH_Aircraft, 3), // Aircraft type. VEH_Aircraft in mem, 3 in file. |
|
3069 SLE_INCLUDEX(0, INC_VEHICLE_COMMON), |
|
3070 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleAir, crashed_counter), SLE_UINT16), |
|
3071 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleAir, pos), SLE_UINT8), |
|
3072 |
|
3073 SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleAir, targetairport), SLE_FILE_U8 | SLE_VAR_U16, 0, 4), |
|
3074 SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleAir, targetairport), SLE_UINT16, 5, SL_MAX_VERSION), |
|
3075 |
|
3076 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleAir, state), SLE_UINT8), |
|
3077 |
|
3078 SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleAir, previous_pos), SLE_UINT8, 2, SL_MAX_VERSION), |
|
3079 |
|
3080 // reserve extra space in savegame here. (currently 15 bytes) |
|
3081 SLE_CONDNULL(15, 2, SL_MAX_VERSION), |
|
3082 |
|
3083 SLE_END() |
|
3084 }; |
|
3085 |
|
3086 static const SaveLoad _special_desc[] = { |
|
3087 SLE_WRITEBYTE(Vehicle,type,VEH_Special, 4), |
|
3088 |
|
3089 SLE_VAR(Vehicle, subtype, SLE_UINT8), |
|
3090 |
|
3091 SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), |
|
3092 SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION), |
|
3093 |
|
3094 SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), |
|
3095 SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, 6, SL_MAX_VERSION), |
|
3096 SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), |
|
3097 SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, 6, SL_MAX_VERSION), |
|
3098 SLE_VAR(Vehicle, z_pos, SLE_UINT8), |
|
3099 |
|
3100 SLE_VAR(Vehicle, cur_image, SLE_UINT16), |
|
3101 SLE_VAR(Vehicle, sprite_width, SLE_UINT8), |
|
3102 SLE_VAR(Vehicle, sprite_height, SLE_UINT8), |
|
3103 SLE_VAR(Vehicle, z_height, SLE_UINT8), |
|
3104 SLE_VAR(Vehicle, x_offs, SLE_INT8), |
|
3105 SLE_VAR(Vehicle, y_offs, SLE_INT8), |
|
3106 SLE_VAR(Vehicle, progress, SLE_UINT8), |
|
3107 SLE_VAR(Vehicle, vehstatus, SLE_UINT8), |
|
3108 |
|
3109 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleSpecial, unk0), SLE_UINT16), |
|
3110 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleSpecial, unk2), SLE_UINT8), |
|
3111 |
|
3112 // reserve extra space in savegame here. (currently 16 bytes) |
|
3113 SLE_CONDNULL(16, 2, SL_MAX_VERSION), |
|
3114 |
|
3115 SLE_END() |
|
3116 }; |
|
3117 |
|
3118 static const SaveLoad _disaster_desc[] = { |
|
3119 SLE_WRITEBYTE(Vehicle, type, VEH_Disaster, 5), |
|
3120 |
|
3121 SLE_REF(Vehicle, next, REF_VEHICLE_OLD), |
|
3122 |
|
3123 SLE_VAR(Vehicle, subtype, SLE_UINT8), |
|
3124 SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), |
|
3125 SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION), |
|
3126 SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), |
|
3127 SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, 6, SL_MAX_VERSION), |
|
3128 |
|
3129 SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), |
|
3130 SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, 6, SL_MAX_VERSION), |
|
3131 SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5), |
|
3132 SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, 6, SL_MAX_VERSION), |
|
3133 SLE_VAR(Vehicle, z_pos, SLE_UINT8), |
|
3134 SLE_VAR(Vehicle, direction, SLE_UINT8), |
|
3135 |
|
3136 SLE_VAR(Vehicle, x_offs, SLE_INT8), |
|
3137 SLE_VAR(Vehicle, y_offs, SLE_INT8), |
|
3138 SLE_VAR(Vehicle, sprite_width, SLE_UINT8), |
|
3139 SLE_VAR(Vehicle, sprite_height, SLE_UINT8), |
|
3140 SLE_VAR(Vehicle, z_height, SLE_UINT8), |
|
3141 SLE_VAR(Vehicle, owner, SLE_UINT8), |
|
3142 SLE_VAR(Vehicle, vehstatus, SLE_UINT8), |
|
3143 SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4), |
|
3144 SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, dest), SLE_UINT16, 5, SL_MAX_VERSION), |
|
3145 |
|
3146 SLE_VAR(Vehicle, cur_image, SLE_UINT16), |
|
3147 SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), |
|
3148 SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION), |
|
3149 SLE_VAR(Vehicle, tick_counter, SLE_UINT8), |
|
3150 |
|
3151 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleDisaster, image_override), SLE_UINT16), |
|
3152 SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleDisaster, unk2), SLE_UINT16), |
|
3153 |
|
3154 // reserve extra space in savegame here. (currently 16 bytes) |
|
3155 SLE_CONDNULL(16, 2, SL_MAX_VERSION), |
|
3156 |
|
3157 SLE_END() |
|
3158 }; |
|
3159 |
|
3160 |
|
3161 static const void *_veh_descs[] = { |
|
3162 _train_desc, |
|
3163 _roadveh_desc, |
|
3164 _ship_desc, |
|
3165 _aircraft_desc, |
|
3166 _special_desc, |
|
3167 _disaster_desc, |
|
3168 }; |
|
3169 |
|
3170 // Will be called when the vehicles need to be saved. |
|
3171 static void Save_VEHS(void) |
|
3172 { |
|
3173 Vehicle *v; |
|
3174 // Write the vehicles |
|
3175 FOR_ALL_VEHICLES(v) { |
|
3176 SlSetArrayIndex(v->index); |
|
3177 SlObject(v, _veh_descs[v->type - 0x10]); |
|
3178 } |
|
3179 } |
|
3180 |
|
3181 // Will be called when vehicles need to be loaded. |
|
3182 static void Load_VEHS(void) |
|
3183 { |
|
3184 int index; |
|
3185 Vehicle *v; |
|
3186 |
|
3187 while ((index = SlIterateArray()) != -1) { |
|
3188 Vehicle *v; |
|
3189 |
|
3190 if (!AddBlockIfNeeded(&_Vehicle_pool, index)) |
|
3191 error("Vehicles: failed loading savegame: too many vehicles"); |
|
3192 |
|
3193 v = GetVehicle(index); |
|
3194 SlObject(v, _veh_descs[SlReadByte()]); |
|
3195 |
|
3196 /* Old savegames used 'last_station_visited = 0xFF' */ |
|
3197 if (CheckSavegameVersion(5) && v->last_station_visited == 0xFF) |
|
3198 v->last_station_visited = INVALID_STATION; |
|
3199 |
|
3200 if (CheckSavegameVersion(5)) { |
|
3201 /* Convert the current_order.type (which is a mix of type and flags, because |
|
3202 * in those versions, they both were 4 bits big) to type and flags */ |
|
3203 v->current_order.flags = (v->current_order.type & 0xF0) >> 4; |
|
3204 v->current_order.type = v->current_order.type & 0x0F; |
|
3205 } |
|
3206 } |
|
3207 |
|
3208 /* Check for shared order-lists (we now use pointers for that) */ |
|
3209 if (CheckSavegameVersionOldStyle(5, 2)) { |
|
3210 FOR_ALL_VEHICLES(v) { |
|
3211 Vehicle *u; |
|
3212 |
|
3213 FOR_ALL_VEHICLES_FROM(u, v->index + 1) { |
|
3214 /* If a vehicle has the same orders, add the link to eachother |
|
3215 * in both vehicles */ |
|
3216 if (v->orders == u->orders) { |
|
3217 v->next_shared = u; |
|
3218 u->prev_shared = v; |
|
3219 break; |
|
3220 } |
|
3221 } |
|
3222 } |
|
3223 } |
|
3224 } |
|
3225 |
|
3226 const ChunkHandler _veh_chunk_handlers[] = { |
|
3227 { 'VEHS', Save_VEHS, Load_VEHS, CH_SPARSE_ARRAY | CH_LAST}, |
|
3228 }; |