232 |
196 |
233 return CT_NO_REFIT; |
197 return CT_NO_REFIT; |
234 } |
198 } |
235 } |
199 } |
236 |
200 |
237 /** Replaces a vehicle (used to be called autorenew) |
|
238 * This function is only called from MaybeReplaceVehicle() |
|
239 * Must be called with _current_player set to the owner of the vehicle |
|
240 * @param w Vehicle to replace |
|
241 * @param flags is the flags to use when calling DoCommand(). Mainly DC_EXEC counts |
|
242 * @param p The vehicle owner (faster than refinding the pointer) |
|
243 * @param new_engine_type The EngineID to replace to |
|
244 * @return value is cost of the replacement or CMD_ERROR |
|
245 */ |
|
246 static CommandCost ReplaceVehicle(Vehicle **w, uint32 flags, Money total_cost, const Player *p, EngineID new_engine_type) |
|
247 { |
|
248 CommandCost cost; |
|
249 CommandCost sell_value; |
|
250 Vehicle *old_v = *w; |
|
251 const UnitID cached_unitnumber = old_v->unitnumber; |
|
252 bool new_front = false; |
|
253 Vehicle *new_v = NULL; |
|
254 char *vehicle_name = NULL; |
|
255 CargoID replacement_cargo_type; |
|
256 |
|
257 replacement_cargo_type = GetNewCargoTypeForReplace(old_v, new_engine_type); |
|
258 |
|
259 /* check if we can't refit to the needed type, so no replace takes place to prevent the vehicle from altering cargo type */ |
|
260 if (replacement_cargo_type == CT_INVALID) return CommandCost(); |
|
261 |
|
262 sell_value = DoCommand(0, old_v->index, 0, DC_QUERY_COST, GetCmdSellVeh(old_v)); |
|
263 |
|
264 /* We give the player a loan of the same amount as the sell value. |
|
265 * This is needed in case he needs the income from the sale to build the new vehicle. |
|
266 * We take it back if building fails or when we really sell the old engine */ |
|
267 SubtractMoneyFromPlayer(sell_value); |
|
268 |
|
269 cost = DoCommand(old_v->tile, new_engine_type, 0, flags | DC_AUTOREPLACE, GetCmdBuildVeh(old_v)); |
|
270 if (CmdFailed(cost)) { |
|
271 /* Take back the money we just gave the player */ |
|
272 sell_value.MultiplyCost(-1); |
|
273 SubtractMoneyFromPlayer(sell_value); |
|
274 return cost; |
|
275 } |
|
276 |
|
277 if (replacement_cargo_type != CT_NO_REFIT) { |
|
278 /* add refit cost */ |
|
279 CommandCost refit_cost = GetRefitCost(new_engine_type); |
|
280 if (old_v->type == VEH_TRAIN && RailVehInfo(new_engine_type)->railveh_type == RAILVEH_MULTIHEAD) { |
|
281 /* Since it's a dualheaded engine we have to pay once more because the rear end is being refitted too. */ |
|
282 refit_cost.AddCost(refit_cost); |
|
283 } |
|
284 cost.AddCost(refit_cost); |
|
285 } |
|
286 |
|
287 if (flags & DC_EXEC) { |
|
288 new_v = GetVehicle(_new_vehicle_id); |
|
289 *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 |
|
290 |
|
291 /* refit if needed */ |
|
292 if (replacement_cargo_type != CT_NO_REFIT) { |
|
293 if (CmdFailed(DoCommand(0, new_v->index, replacement_cargo_type, DC_EXEC, GetCmdRefitVeh(new_v)))) { |
|
294 /* Being here shows a failure, which most likely is in GetNewCargoTypeForReplace() or incorrect estimation costs */ |
|
295 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); |
|
296 } |
|
297 } |
|
298 |
|
299 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()))) { |
|
300 // we are autorenewing to a single engine, so we will turn it as the old one was turned as well |
|
301 SetBit(new_v->u.rail.flags, VRF_REVERSE_DIRECTION); |
|
302 } |
|
303 |
|
304 if (old_v->type == VEH_TRAIN && !IsFrontEngine(old_v)) { |
|
305 /* this is a railcar. We need to move the car into the train |
|
306 * We add the new engine after the old one instead of replacing it. It will give the same result anyway when we |
|
307 * sell the old engine in a moment |
|
308 */ |
|
309 /* Get the vehicle in front of the one we move out */ |
|
310 Vehicle *front = old_v->Previous(); |
|
311 if (front == NULL) { |
|
312 /* It would appear that we have the front wagon of a row of wagons without engines */ |
|
313 Vehicle *next = old_v->Next(); |
|
314 if (next != NULL) { |
|
315 /* Move the chain to the new front wagon */ |
|
316 DoCommand(0, (new_v->index << 16) | next->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
317 } |
|
318 } else { |
|
319 /* 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 */ |
|
320 if (IsRearDualheaded(front)) front = front->Previous(); |
|
321 /* Now we move the old one out of the train */ |
|
322 DoCommand(0, (INVALID_VEHICLE << 16) | old_v->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
323 /* Add the new vehicle */ |
|
324 CommandCost tmp_move = DoCommand(0, (front->index << 16) | new_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
325 if (CmdFailed(tmp_move)) { |
|
326 cost.AddCost(tmp_move); |
|
327 DoCommand(0, new_v->index, 1, DC_EXEC, GetCmdSellVeh(VEH_TRAIN)); |
|
328 } |
|
329 } |
|
330 } else { |
|
331 // copy/clone the orders |
|
332 DoCommand(0, (old_v->index << 16) | new_v->index, old_v->IsOrderListShared() ? CO_SHARE : CO_COPY, DC_EXEC, CMD_CLONE_ORDER); |
|
333 new_v->cur_order_index = old_v->cur_order_index; |
|
334 ChangeVehicleViewWindow(old_v->index, new_v->index); |
|
335 new_v->profit_this_year = old_v->profit_this_year; |
|
336 new_v->profit_last_year = old_v->profit_last_year; |
|
337 new_v->service_interval = old_v->service_interval; |
|
338 DoCommand(0, old_v->group_id, new_v->index, flags, CMD_ADD_VEHICLE_GROUP); |
|
339 new_front = true; |
|
340 new_v->unitnumber = old_v->unitnumber; // use the same unit number |
|
341 new_v->dest_tile = old_v->dest_tile; |
|
342 |
|
343 new_v->current_order = old_v->current_order; |
|
344 if (old_v->type == VEH_TRAIN && GetNextVehicle(old_v) != NULL){ |
|
345 Vehicle *temp_v = GetNextVehicle(old_v); |
|
346 |
|
347 // move the entire train to the new engine, excluding the old engine |
|
348 if (IsMultiheaded(old_v) && temp_v == old_v->u.rail.other_multiheaded_part) { |
|
349 // we got front and rear of a multiheaded engine right after each other. We should work with the next in line instead |
|
350 temp_v = GetNextVehicle(temp_v); |
|
351 } |
|
352 |
|
353 if (temp_v != NULL) { |
|
354 CommandCost tmp_move = DoCommand(0, (new_v->index << 16) | temp_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
355 if (CmdFailed(tmp_move)) { |
|
356 cost.AddCost(tmp_move); |
|
357 DoCommand(0, temp_v->index, 1, DC_EXEC, GetCmdSellVeh(VEH_TRAIN)); |
|
358 } |
|
359 } |
|
360 } |
|
361 } |
|
362 if (CmdSucceeded(cost)) { |
|
363 /* We are done setting up the new vehicle. Now we move the cargo from the old one to the new one */ |
|
364 MoveVehicleCargo(new_v->type == VEH_TRAIN ? new_v->First() : new_v, old_v); |
|
365 |
|
366 /* Get the name of the old vehicle if it has a custom name. */ |
|
367 if (old_v->name != NULL) vehicle_name = strdup(old_v->name); |
|
368 } |
|
369 } else { // flags & DC_EXEC not set |
|
370 CommandCost tmp_move; |
|
371 |
|
372 if (old_v->type == VEH_TRAIN && IsFrontEngine(old_v)) { |
|
373 Vehicle *next_veh = GetNextUnit(old_v); // don't try to move the rear multiheaded engine or articulated parts |
|
374 if (next_veh != NULL) { |
|
375 /* Verify that the wagons can be placed on the engine in question. |
|
376 * This is done by building an engine, test if the wagons can be added and then sell the test engine. */ |
|
377 DoCommand(old_v->tile, new_engine_type, 0, DC_EXEC | DC_AUTOREPLACE, GetCmdBuildVeh(old_v)); |
|
378 Vehicle *temp = GetVehicle(_new_vehicle_id); |
|
379 tmp_move = DoCommand(0, (temp->index << 16) | next_veh->index, 1, 0, CMD_MOVE_RAIL_VEHICLE); |
|
380 DoCommand(0, temp->index, 0, DC_EXEC, GetCmdSellVeh(old_v)); |
|
381 } |
|
382 } |
|
383 |
|
384 /* Ensure that the player will not end up having negative money while autoreplacing |
|
385 * This is needed because the only other check is done after the income from selling the old vehicle is substracted from the cost */ |
|
386 if (CmdFailed(tmp_move) || p->player_money < (cost.GetCost() + total_cost)) { |
|
387 /* Pay back the loan */ |
|
388 sell_value.MultiplyCost(-1); |
|
389 SubtractMoneyFromPlayer(sell_value); |
|
390 return CMD_ERROR; |
|
391 } |
|
392 } |
|
393 |
|
394 /* Take back the money we just gave the player just before building the vehicle |
|
395 * The player will get the same amount now that the sale actually takes place */ |
|
396 sell_value.MultiplyCost(-1); |
|
397 SubtractMoneyFromPlayer(sell_value); |
|
398 |
|
399 /* sell the engine/ find out how much you get for the old engine (income is returned as negative cost) */ |
|
400 cost.AddCost(DoCommand(0, old_v->index, 0, flags, GetCmdSellVeh(old_v))); |
|
401 |
|
402 if (CmdFailed(cost)) return cost; |
|
403 |
|
404 if (new_front) { |
|
405 /* now we assign the old unitnumber to the new vehicle */ |
|
406 new_v->unitnumber = cached_unitnumber; |
|
407 } |
|
408 |
|
409 /* Transfer the name of the old vehicle */ |
|
410 if ((flags & DC_EXEC) && vehicle_name != NULL) { |
|
411 _cmd_text = vehicle_name; |
|
412 DoCommand(0, new_v->index, 0, DC_EXEC, CMD_NAME_VEHICLE); |
|
413 free(vehicle_name); |
|
414 } |
|
415 |
|
416 return cost; |
|
417 } |
|
418 |
|
419 /** Removes wagons from a train until it get a certain length |
|
420 * @param v The vehicle |
|
421 * @param old_total_length The wanted max length |
|
422 * @return The profit from selling the wagons |
|
423 */ |
|
424 static CommandCost WagonRemoval(Vehicle *v, uint16 old_total_length) |
|
425 { |
|
426 if (v->type != VEH_TRAIN) return CommandCost(); |
|
427 Vehicle *front = v; |
|
428 |
|
429 CommandCost cost = CommandCost(); |
|
430 |
|
431 while (front->u.rail.cached_total_length > old_total_length) { |
|
432 /* the train is too long. We will remove cars one by one from the start of the train until it's short enough */ |
|
433 while (v != NULL && RailVehInfo(v->engine_type)->railveh_type != RAILVEH_WAGON) { |
|
434 /* We move backwards in the train until we find a wagon */ |
|
435 v = GetNextVehicle(v); |
|
436 } |
|
437 |
|
438 if (v == NULL) { |
|
439 /* We sold all the wagons and the train is still not short enough */ |
|
440 SetDParam(0, front->unitnumber); |
|
441 AddNewsItem(STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT, NS_ADVICE, front->index, 0); |
|
442 return cost; |
|
443 } |
|
444 |
|
445 /* We found a wagon we can sell */ |
|
446 Vehicle *temp = v; |
|
447 v = GetNextVehicle(v); |
|
448 DoCommand(0, (INVALID_VEHICLE << 16) | temp->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); // remove the wagon from the train |
|
449 MoveVehicleCargo(front, temp); // move the cargo back on the train |
|
450 cost.AddCost(DoCommand(0, temp->index, 0, DC_EXEC, CMD_SELL_RAIL_WAGON)); // sell the wagon |
|
451 } |
|
452 return cost; |
|
453 } |
|
454 |
|
455 /** Get the EngineID of the replacement for a vehicle |
201 /** Get the EngineID of the replacement for a vehicle |
456 * @param v The vehicle to find a replacement for |
202 * @param v The vehicle to find a replacement for |
457 * @param p The vehicle's owner (it's faster to forward the pointer than refinding it) |
203 * @param p The vehicle's owner (it's faster to forward the pointer than refinding it) |
458 * @return the EngineID of the replacement. INVALID_ENGINE if no buildable replacement is found |
204 * @return the EngineID of the replacement. INVALID_ENGINE if no buildable replacement is found |
459 */ |
205 */ |
476 IsEngineBuildable(v->engine_type, v->type, _current_player)) { // engine can still be build |
222 IsEngineBuildable(v->engine_type, v->type, _current_player)) { // engine can still be build |
477 return v->engine_type; |
223 return v->engine_type; |
478 } |
224 } |
479 |
225 |
480 return INVALID_ENGINE; |
226 return INVALID_ENGINE; |
481 } |
|
482 |
|
483 /** replaces a vehicle if it's set for autoreplace or is too old |
|
484 * (used to be called autorenew) |
|
485 * @param v The vehicle to replace |
|
486 * if the vehicle is a train, v needs to be the front engine |
|
487 * @param flags |
|
488 * @param display_costs If set, a cost animation is shown (only if DC_EXEC is set) |
|
489 * This bool also takes autorenew money into consideration |
|
490 * @return the costs, the success bool and sometimes an error message |
|
491 */ |
|
492 CommandCost MaybeReplaceVehicle(Vehicle *v, uint32 flags, bool display_costs) |
|
493 { |
|
494 Vehicle *w; |
|
495 Player *p = GetPlayer(v->owner); |
|
496 CommandCost cost; |
|
497 bool stopped = false; |
|
498 BackuppedVehicle backup(true); |
|
499 |
|
500 /* We only want "real" vehicle types. */ |
|
501 assert(IsPlayerBuildableVehicleType(v)); |
|
502 |
|
503 /* Ensure that this bool is cleared. */ |
|
504 assert(!v->leave_depot_instantly); |
|
505 |
|
506 /* We can't sell if the current player don't own the vehicle. */ |
|
507 assert(v->owner == _current_player); |
|
508 |
|
509 if (!v->IsInDepot()) { |
|
510 /* The vehicle should be inside the depot */ |
|
511 switch (v->type) { |
|
512 default: NOT_REACHED(); |
|
513 case VEH_TRAIN: return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED); break; |
|
514 case VEH_ROAD: return_cmd_error(STR_9013_MUST_BE_STOPPED_INSIDE); break; |
|
515 case VEH_SHIP: return_cmd_error(STR_980B_SHIP_MUST_BE_STOPPED_IN); break; |
|
516 case VEH_AIRCRAFT: return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED); break; |
|
517 } |
|
518 } |
|
519 |
|
520 /* Remember the length in case we need to trim train later on |
|
521 * If it's not a train, the value is unused |
|
522 * round up to the length of the tiles used for the train instead of the train length instead |
|
523 * Useful when newGRF uses custom length */ |
|
524 uint16 old_total_length = (v->type == VEH_TRAIN ? |
|
525 (v->u.rail.cached_total_length + TILE_SIZE - 1) / TILE_SIZE * TILE_SIZE : |
|
526 -1 |
|
527 ); |
|
528 |
|
529 if (!(v->vehstatus & VS_STOPPED)) { |
|
530 /* The vehicle is moving so we better stop it before we might alter consist or sell it */ |
|
531 v->vehstatus |= VS_STOPPED; |
|
532 /* Remember that we stopped the vehicle */ |
|
533 stopped = true; |
|
534 } |
|
535 |
|
536 { |
|
537 cost = CommandCost(EXPENSES_NEW_VEHICLES); |
|
538 w = v; |
|
539 do { |
|
540 EngineID new_engine = GetNewEngineType(w, p); |
|
541 if (new_engine == INVALID_ENGINE) continue; |
|
542 |
|
543 if (!backup.ContainsBackup()) { |
|
544 /* We are going to try to replace a vehicle but we don't have any backup so we will make one. */ |
|
545 backup.Backup(v, p); |
|
546 } |
|
547 /* Now replace the vehicle. |
|
548 * First we need to cache if it's the front vehicle as we need to update the v pointer if it is. |
|
549 * If the replacement fails then we can't trust the data in the vehicle hence the reason to cache the result. */ |
|
550 bool IsFront = w->type != VEH_TRAIN || w->u.rail.first_engine == INVALID_ENGINE; |
|
551 |
|
552 cost.AddCost(ReplaceVehicle(&w, DC_EXEC, cost.GetCost(), p, new_engine)); |
|
553 |
|
554 if (IsFront) { |
|
555 /* now we bought a new engine and sold the old one. We need to fix the |
|
556 * pointers in order to avoid pointing to the old one for trains: these |
|
557 * pointers should point to the front engine and not the cars |
|
558 */ |
|
559 v = w; |
|
560 } |
|
561 } while (CmdSucceeded(cost) && w->type == VEH_TRAIN && (w = GetNextVehicle(w)) != NULL); |
|
562 |
|
563 if (CmdSucceeded(cost) && v->type == VEH_TRAIN && p->renew_keep_length) { |
|
564 /* Remove wagons until the wanted length is reached */ |
|
565 cost.AddCost(WagonRemoval(v, old_total_length)); |
|
566 } |
|
567 |
|
568 if (flags & DC_QUERY_COST || cost.GetCost() == 0) { |
|
569 /* We didn't do anything during the replace so we will just exit here */ |
|
570 v = backup.Restore(v, p); |
|
571 if (stopped) v->vehstatus &= ~VS_STOPPED; |
|
572 return cost; |
|
573 } |
|
574 |
|
575 if (display_costs) { |
|
576 /* We want to ensure that we will not get below p->engine_renew_money. |
|
577 * We will not actually pay this amount. It's for display and checks only. */ |
|
578 CommandCost tmp = cost; |
|
579 tmp.AddCost((Money)p->engine_renew_money); |
|
580 if (CmdSucceeded(tmp) && GetAvailableMoneyForCommand() < tmp.GetCost()) { |
|
581 /* We don't have enough money so we will set cost to failed */ |
|
582 cost.AddCost((Money)p->engine_renew_money); |
|
583 cost.AddCost(CMD_ERROR); |
|
584 } |
|
585 } |
|
586 |
|
587 if (display_costs && CmdFailed(cost)) { |
|
588 if (GetAvailableMoneyForCommand() < cost.GetCost() && IsLocalPlayer()) { |
|
589 StringID message; |
|
590 SetDParam(0, v->unitnumber); |
|
591 switch (v->type) { |
|
592 case VEH_TRAIN: message = STR_TRAIN_AUTORENEW_FAILED; break; |
|
593 case VEH_ROAD: message = STR_ROADVEHICLE_AUTORENEW_FAILED; break; |
|
594 case VEH_SHIP: message = STR_SHIP_AUTORENEW_FAILED; break; |
|
595 case VEH_AIRCRAFT: message = STR_AIRCRAFT_AUTORENEW_FAILED; break; |
|
596 // This should never happen |
|
597 default: NOT_REACHED(); message = 0; break; |
|
598 } |
|
599 |
|
600 AddNewsItem(message, NS_ADVICE, v->index, 0); |
|
601 } |
|
602 } |
|
603 } |
|
604 |
|
605 if (display_costs && IsLocalPlayer() && (flags & DC_EXEC) && CmdSucceeded(cost)) { |
|
606 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost()); |
|
607 } |
|
608 |
|
609 if (!(flags & DC_EXEC) || CmdFailed(cost)) { |
|
610 v = backup.Restore(v, p); |
|
611 } |
|
612 |
|
613 /* Start the vehicle if we stopped it earlier */ |
|
614 if (stopped) v->vehstatus &= ~VS_STOPPED; |
|
615 |
|
616 return cost; |
|
617 } |
227 } |
618 |
228 |
619 /** Builds and refits a replacement vehicle |
229 /** Builds and refits a replacement vehicle |
620 * Important: The old vehicle is still in the original vehicle chain (used for determining the cargo when the old vehicle did not carry anything, but the new one does) |
230 * Important: The old vehicle is still in the original vehicle chain (used for determining the cargo when the old vehicle did not carry anything, but the new one does) |
621 * @param old_veh A single (articulated/multiheaded) vehicle that shall be replaced. |
231 * @param old_veh A single (articulated/multiheaded) vehicle that shall be replaced. |