204 /* If the vehicle in front is the rear end of a dualheaded engine, then we need to use the one in front of that one */ |
204 /* If the vehicle in front is the rear end of a dualheaded engine, then we need to use the one in front of that one */ |
205 if (IsRearDualheaded(front)) front = front->Previous(); |
205 if (IsRearDualheaded(front)) front = front->Previous(); |
206 /* Now we move the old one out of the train */ |
206 /* Now we move the old one out of the train */ |
207 DoCommand(0, (INVALID_VEHICLE << 16) | old_v->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
207 DoCommand(0, (INVALID_VEHICLE << 16) | old_v->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
208 /* Add the new vehicle */ |
208 /* Add the new vehicle */ |
209 DoCommand(0, (front->index << 16) | new_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
209 CommandCost tmp_move = DoCommand(0, (front->index << 16) | new_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
210 if (CmdFailed(tmp_move)) { |
|
211 cost.AddCost(tmp_move); |
|
212 DoCommand(0, new_v->index, 1, DC_EXEC, GetCmdSellVeh(VEH_TRAIN)); |
|
213 } |
210 } |
214 } |
211 } else { |
215 } else { |
212 // copy/clone the orders |
216 // copy/clone the orders |
213 DoCommand(0, (old_v->index << 16) | new_v->index, old_v->IsOrderListShared() ? CO_SHARE : CO_COPY, DC_EXEC, CMD_CLONE_ORDER); |
217 DoCommand(0, (old_v->index << 16) | new_v->index, old_v->IsOrderListShared() ? CO_SHARE : CO_COPY, DC_EXEC, CMD_CLONE_ORDER); |
214 new_v->cur_order_index = old_v->cur_order_index; |
218 new_v->cur_order_index = old_v->cur_order_index; |
230 // we got front and rear of a multiheaded engine right after each other. We should work with the next in line instead |
234 // we got front and rear of a multiheaded engine right after each other. We should work with the next in line instead |
231 temp_v = GetNextVehicle(temp_v); |
235 temp_v = GetNextVehicle(temp_v); |
232 } |
236 } |
233 |
237 |
234 if (temp_v != NULL) { |
238 if (temp_v != NULL) { |
235 DoCommand(0, (new_v->index << 16) | temp_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
239 CommandCost tmp_move = DoCommand(0, (new_v->index << 16) | temp_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
240 if (CmdFailed(tmp_move)) { |
|
241 cost.AddCost(tmp_move); |
|
242 DoCommand(0, temp_v->index, 1, DC_EXEC, GetCmdSellVeh(VEH_TRAIN)); |
|
243 } |
236 } |
244 } |
237 } |
245 } |
238 } |
246 } |
239 /* We are done setting up the new vehicle. Now we move the cargo from the old one to the new one */ |
247 /* We are done setting up the new vehicle. Now we move the cargo from the old one to the new one */ |
240 MoveVehicleCargo(new_v->type == VEH_TRAIN ? new_v->First() : new_v, old_v); |
248 MoveVehicleCargo(new_v->type == VEH_TRAIN ? new_v->First() : new_v, old_v); |
361 * @return the costs, the success bool and sometimes an error message |
369 * @return the costs, the success bool and sometimes an error message |
362 */ |
370 */ |
363 CommandCost MaybeReplaceVehicle(Vehicle *v, uint32 flags, bool display_costs) |
371 CommandCost MaybeReplaceVehicle(Vehicle *v, uint32 flags, bool display_costs) |
364 { |
372 { |
365 Vehicle *w; |
373 Vehicle *w; |
366 const Player *p = GetPlayer(v->owner); |
374 Player *p = GetPlayer(v->owner); |
367 CommandCost cost; |
375 CommandCost cost; |
368 bool stopped = false; |
376 bool stopped = false; |
|
377 BackuppedVehicle backup(true); |
369 |
378 |
370 /* We only want "real" vehicle types. */ |
379 /* We only want "real" vehicle types. */ |
371 assert(IsPlayerBuildableVehicleType(v)); |
380 assert(IsPlayerBuildableVehicleType(v)); |
372 |
381 |
373 /* Ensure that this bool is cleared. */ |
382 /* Ensure that this bool is cleared. */ |
408 w = v; |
417 w = v; |
409 do { |
418 do { |
410 EngineID new_engine = GetNewEngineType(w, p); |
419 EngineID new_engine = GetNewEngineType(w, p); |
411 if (new_engine == INVALID_ENGINE) continue; |
420 if (new_engine == INVALID_ENGINE) continue; |
412 |
421 |
|
422 if (!backup.ContainsBackup()) { |
|
423 /* We are going to try to replace a vehicle but we don't have any backup so we will make one. */ |
|
424 backup.Backup(v, p); |
|
425 } |
413 /* Now replace the vehicle */ |
426 /* Now replace the vehicle */ |
414 cost.AddCost(ReplaceVehicle(&w, flags, cost.GetCost(), p, new_engine)); |
427 cost.AddCost(ReplaceVehicle(&w, DC_EXEC, cost.GetCost(), p, new_engine)); |
415 |
428 |
416 if (flags & DC_EXEC && |
429 if (w->type != VEH_TRAIN || w->u.rail.first_engine == INVALID_ENGINE) { |
417 (w->type != VEH_TRAIN || w->u.rail.first_engine == INVALID_ENGINE)) { |
|
418 /* now we bought a new engine and sold the old one. We need to fix the |
430 /* now we bought a new engine and sold the old one. We need to fix the |
419 * pointers in order to avoid pointing to the old one for trains: these |
431 * pointers in order to avoid pointing to the old one for trains: these |
420 * pointers should point to the front engine and not the cars |
432 * pointers should point to the front engine and not the cars |
421 */ |
433 */ |
422 v = w; |
434 v = w; |
423 } |
435 } |
424 } while (w->type == VEH_TRAIN && (w = GetNextVehicle(w)) != NULL); |
436 } while (w->type == VEH_TRAIN && (w = GetNextVehicle(w)) != NULL); |
425 |
437 |
|
438 if (v->type == VEH_TRAIN && p->renew_keep_length) { |
|
439 /* Remove wagons until the wanted length is reached */ |
|
440 cost.AddCost(WagonRemoval(v, old_total_length)); |
|
441 } |
|
442 |
426 if (flags & DC_QUERY_COST || cost.GetCost() == 0) { |
443 if (flags & DC_QUERY_COST || cost.GetCost() == 0) { |
427 /* We didn't do anything during the replace so we will just exit here */ |
444 /* We didn't do anything during the replace so we will just exit here */ |
|
445 v = backup.Restore(v); |
428 if (stopped) v->vehstatus &= ~VS_STOPPED; |
446 if (stopped) v->vehstatus &= ~VS_STOPPED; |
429 return cost; |
447 return cost; |
430 } |
448 } |
431 |
449 |
432 if (display_costs && !(flags & DC_EXEC)) { |
450 if (display_costs) { |
433 /* We want to ensure that we will not get below p->engine_renew_money. |
451 /* We want to ensure that we will not get below p->engine_renew_money. |
434 * We will not actually pay this amount. It's for display and checks only. */ |
452 * We will not actually pay this amount. It's for display and checks only. */ |
435 cost.AddCost((Money)p->engine_renew_money); |
453 CommandCost tmp = cost; |
436 if (CmdSucceeded(cost) && GetAvailableMoneyForCommand() < cost.GetCost()) { |
454 tmp.AddCost((Money)p->engine_renew_money); |
|
455 if (CmdSucceeded(tmp) && GetAvailableMoneyForCommand() < tmp.GetCost()) { |
437 /* We don't have enough money so we will set cost to failed */ |
456 /* We don't have enough money so we will set cost to failed */ |
|
457 cost.AddCost((Money)p->engine_renew_money); |
438 cost.AddCost(CMD_ERROR); |
458 cost.AddCost(CMD_ERROR); |
439 } |
459 } |
440 } |
460 } |
441 |
461 |
442 if (display_costs && CmdFailed(cost)) { |
462 if (display_costs && CmdFailed(cost)) { |
455 AddNewsItem(message, NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0); |
475 AddNewsItem(message, NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0); |
456 } |
476 } |
457 } |
477 } |
458 } |
478 } |
459 |
479 |
460 if (flags & DC_EXEC && CmdSucceeded(cost)) { |
480 if (display_costs && IsLocalPlayer() && (flags & DC_EXEC) && CmdSucceeded(cost)) { |
461 if (v->type == VEH_TRAIN && p->renew_keep_length) { |
481 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost()); |
462 /* Remove wagons until the wanted length is reached */ |
482 } |
463 cost.AddCost(WagonRemoval(v, old_total_length)); |
483 |
464 } |
484 if (!(flags & DC_EXEC) || CmdFailed(cost)) { |
465 |
485 v = backup.Restore(v); |
466 if (display_costs && IsLocalPlayer()) { |
|
467 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost()); |
|
468 } |
|
469 } |
486 } |
470 |
487 |
471 /* Start the vehicle if we stopped it earlier */ |
488 /* Start the vehicle if we stopped it earlier */ |
472 if (stopped) v->vehstatus &= ~VS_STOPPED; |
489 if (stopped) v->vehstatus &= ~VS_STOPPED; |
473 |
490 |