115 if (CanRefitTo(engine_type, v->cargo_type)) return v->cargo_type; |
117 if (CanRefitTo(engine_type, v->cargo_type)) return v->cargo_type; |
116 } while ((v = v->Next()) != NULL); |
118 } while ((v = v->Next()) != NULL); |
117 return CT_NO_REFIT; // We failed to find a cargo type on the old vehicle and we will not refit the new one |
119 return CT_NO_REFIT; // We failed to find a cargo type on the old vehicle and we will not refit the new one |
118 } |
120 } |
119 |
121 |
120 /* Replaces a vehicle (used to be called autorenew) |
122 /** Replaces a vehicle (used to be called autorenew) |
121 * This function is only called from MaybeReplaceVehicle() |
123 * This function is only called from MaybeReplaceVehicle() |
122 * Must be called with _current_player set to the owner of the vehicle |
124 * Must be called with _current_player set to the owner of the vehicle |
123 * @param w Vehicle to replace |
125 * @param w Vehicle to replace |
124 * @param flags is the flags to use when calling DoCommand(). Mainly DC_EXEC counts |
126 * @param flags is the flags to use when calling DoCommand(). Mainly DC_EXEC counts |
|
127 * @param p The vehicle owner (faster than refinding the pointer) |
|
128 * @param new_engine_type The EngineID to replace to |
125 * @return value is cost of the replacement or CMD_ERROR |
129 * @return value is cost of the replacement or CMD_ERROR |
126 */ |
130 */ |
127 static CommandCost ReplaceVehicle(Vehicle **w, byte flags, Money total_cost) |
131 static CommandCost ReplaceVehicle(Vehicle **w, byte flags, Money total_cost, const Player *p, EngineID new_engine_type) |
128 { |
132 { |
129 CommandCost cost; |
133 CommandCost cost; |
130 CommandCost sell_value; |
134 CommandCost sell_value; |
131 Vehicle *old_v = *w; |
135 Vehicle *old_v = *w; |
132 const Player *p = GetPlayer(old_v->owner); |
|
133 EngineID new_engine_type; |
|
134 const UnitID cached_unitnumber = old_v->unitnumber; |
136 const UnitID cached_unitnumber = old_v->unitnumber; |
135 bool new_front = false; |
137 bool new_front = false; |
136 Vehicle *new_v = NULL; |
138 Vehicle *new_v = NULL; |
137 char *vehicle_name = NULL; |
139 char *vehicle_name = NULL; |
138 CargoID replacement_cargo_type; |
140 CargoID replacement_cargo_type; |
139 |
|
140 /* Check if there is a autoreplacement set for the vehicle */ |
|
141 new_engine_type = EngineReplacementForPlayer(p, old_v->engine_type, old_v->group_id); |
|
142 /* if not, just renew to the same type */ |
|
143 if (new_engine_type == INVALID_ENGINE) new_engine_type = old_v->engine_type; |
|
144 |
141 |
145 replacement_cargo_type = GetNewCargoTypeForReplace(old_v, new_engine_type); |
142 replacement_cargo_type = GetNewCargoTypeForReplace(old_v, new_engine_type); |
146 |
143 |
147 /* check if we can't refit to the needed type, so no replace takes place to prevent the vehicle from altering cargo type */ |
144 /* check if we can't refit to the needed type, so no replace takes place to prevent the vehicle from altering cargo type */ |
148 if (replacement_cargo_type == CT_INVALID) return CommandCost(); |
145 if (replacement_cargo_type == CT_INVALID) return CommandCost(); |
290 } |
287 } |
291 |
288 |
292 return cost; |
289 return cost; |
293 } |
290 } |
294 |
291 |
|
292 /** Removes wagons from a train until it get a certain length |
|
293 * @param v The vehicle |
|
294 * @param old_total_length The wanted max length |
|
295 * @return The profit from selling the wagons |
|
296 */ |
|
297 static CommandCost WagonRemoval(Vehicle *v, uint16 old_total_length) |
|
298 { |
|
299 if (v->type != VEH_TRAIN) return CommandCost(); |
|
300 Vehicle *front = v; |
|
301 |
|
302 CommandCost cost = CommandCost(); |
|
303 |
|
304 while (front->u.rail.cached_total_length > old_total_length) { |
|
305 /* the train is too long. We will remove cars one by one from the start of the train until it's short enough */ |
|
306 while (v != NULL && RailVehInfo(v->engine_type)->railveh_type != RAILVEH_WAGON) { |
|
307 /* We move backwards in the train until we find a wagon */ |
|
308 v = GetNextVehicle(v); |
|
309 } |
|
310 |
|
311 if (v == NULL) { |
|
312 /* We sold all the wagons and the train is still not short enough */ |
|
313 SetDParam(0, front->unitnumber); |
|
314 AddNewsItem(STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT, NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, front->index, 0); |
|
315 return cost; |
|
316 } |
|
317 |
|
318 /* We found a wagon we can sell */ |
|
319 Vehicle *temp = v; |
|
320 v = GetNextVehicle(v); |
|
321 DoCommand(0, (INVALID_VEHICLE << 16) | temp->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); // remove the wagon from the train |
|
322 MoveVehicleCargo(front, temp); // move the cargo back on the train |
|
323 cost.AddCost(DoCommand(0, temp->index, 0, DC_EXEC, CMD_SELL_RAIL_WAGON)); // sell the wagon |
|
324 } |
|
325 return cost; |
|
326 } |
|
327 |
|
328 /** Get the EngineID of the replacement for a vehicle |
|
329 * @param v The vehicle to find a replacement for |
|
330 * @param p The vehicle's owner (it's faster to forward the pointer than refinding it) |
|
331 * @return the EngineID of the replacement. INVALID_ENGINE if no buildable replacement is found |
|
332 */ |
|
333 static EngineID GetNewEngineType(const Vehicle *v, const Player *p) |
|
334 { |
|
335 if (v->type == VEH_TRAIN && IsRearDualheaded(v)) { |
|
336 /* we build the rear ends of multiheaded trains with the front ones */ |
|
337 return INVALID_ENGINE; |
|
338 } |
|
339 |
|
340 EngineID e = EngineReplacementForPlayer(p, v->engine_type, v->group_id); |
|
341 |
|
342 if (e != INVALID_ENGINE && IsEngineBuildable(e, v->type, _current_player)) { |
|
343 return e; |
|
344 } |
|
345 |
|
346 if (v->NeedsAutorenewing(p) && // replace if engine is too old |
|
347 IsEngineBuildable(v->engine_type, v->type, _current_player)) { // engine can still be build |
|
348 return v->engine_type; |
|
349 } |
|
350 |
|
351 return INVALID_ENGINE; |
|
352 } |
|
353 |
295 /** replaces a vehicle if it's set for autoreplace or is too old |
354 /** replaces a vehicle if it's set for autoreplace or is too old |
296 * (used to be called autorenew) |
355 * (used to be called autorenew) |
297 * @param v The vehicle to replace |
356 * @param v The vehicle to replace |
298 * if the vehicle is a train, v needs to be the front engine |
357 * if the vehicle is a train, v needs to be the front engine |
299 * @param check Checks if the replace is valid. No action is done at all |
358 * @param flags |
300 * @param display_costs If set, a cost animation is shown (only if check is false) |
359 * @param display_costs If set, a cost animation is shown (only if DC_EXEC is set) |
301 * @return CMD_ERROR if something went wrong. Otherwise the price of the replace |
360 * This bool also takes autorenew money into consideration |
302 */ |
361 * @return the costs, the success bool and sometimes an error message |
303 CommandCost MaybeReplaceVehicle(Vehicle *v, bool check, bool display_costs) |
362 */ |
|
363 CommandCost MaybeReplaceVehicle(Vehicle *v, uint32 flags, bool display_costs) |
304 { |
364 { |
305 Vehicle *w; |
365 Vehicle *w; |
306 const Player *p = GetPlayer(v->owner); |
366 const Player *p = GetPlayer(v->owner); |
307 byte flags = 0; |
367 CommandCost cost; |
308 CommandCost cost, temp_cost; |
368 bool stopped = false; |
309 bool stopped; |
369 |
|
370 /* We only want "real" vehicle types. */ |
|
371 assert(IsPlayerBuildableVehicleType(v)); |
|
372 |
|
373 /* Ensure that this bool is cleared. */ |
|
374 assert(!v->leave_depot_instantly); |
|
375 |
|
376 /* We can't sell if the current player don't own the vehicle. */ |
|
377 assert(v->owner == _current_player); |
|
378 |
|
379 if (!v->IsInDepot()) { |
|
380 /* The vehicle should be inside the depot */ |
|
381 switch (v->type) { |
|
382 default: NOT_REACHED(); |
|
383 case VEH_TRAIN: return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED); break; |
|
384 case VEH_ROAD: return_cmd_error(STR_9013_MUST_BE_STOPPED_INSIDE); break; |
|
385 case VEH_SHIP: return_cmd_error(STR_980B_SHIP_MUST_BE_STOPPED_IN); break; |
|
386 case VEH_AIRCRAFT: return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED); break; |
|
387 } |
|
388 } |
310 |
389 |
311 /* Remember the length in case we need to trim train later on |
390 /* Remember the length in case we need to trim train later on |
312 * If it's not a train, the value is unused |
391 * If it's not a train, the value is unused |
313 * round up to the length of the tiles used for the train instead of the train length instead |
392 * round up to the length of the tiles used for the train instead of the train length instead |
314 * Useful when newGRF uses custom length */ |
393 * Useful when newGRF uses custom length */ |
315 uint16 old_total_length = (v->type == VEH_TRAIN ? |
394 uint16 old_total_length = (v->type == VEH_TRAIN ? |
316 (v->u.rail.cached_total_length + TILE_SIZE - 1) / TILE_SIZE * TILE_SIZE : |
395 (v->u.rail.cached_total_length + TILE_SIZE - 1) / TILE_SIZE * TILE_SIZE : |
317 -1 |
396 -1 |
318 ); |
397 ); |
319 |
398 |
320 |
399 if (!(v->vehstatus & VS_STOPPED)) { |
321 _current_player = v->owner; |
400 /* The vehicle is moving so we better stop it before we might alter consist or sell it */ |
322 |
401 v->vehstatus |= VS_STOPPED; |
323 assert(IsPlayerBuildableVehicleType(v)); |
402 /* Remember that we stopped the vehicle */ |
324 |
403 stopped = true; |
325 assert(v->vehstatus & VS_STOPPED); // the vehicle should have been stopped in VehicleEnteredDepotThisTick() if needed |
404 } |
326 |
405 |
327 /* Remember the flag v->leave_depot_instantly because if we replace the vehicle, the vehicle holding this flag will be sold |
406 { |
328 * If it is set, then we only stopped the vehicle to replace it (if needed) and we will need to start it again. |
|
329 * We also need to reset the flag since it should remain false except from when the vehicle enters a depot until autoreplace is handled in the same tick */ |
|
330 stopped = v->leave_depot_instantly; |
|
331 v->leave_depot_instantly = false; |
|
332 |
|
333 for (;;) { |
|
334 cost = CommandCost(EXPENSES_NEW_VEHICLES); |
407 cost = CommandCost(EXPENSES_NEW_VEHICLES); |
335 w = v; |
408 w = v; |
336 do { |
409 do { |
337 if (w->type == VEH_TRAIN && IsRearDualheaded(w)) { |
410 EngineID new_engine = GetNewEngineType(w, p); |
338 /* we build the rear ends of multiheaded trains with the front ones */ |
411 if (new_engine == INVALID_ENGINE) continue; |
339 continue; |
|
340 } |
|
341 |
|
342 // check if the vehicle should be replaced |
|
343 if (!w->NeedsAutorenewing(p) || // replace if engine is too old |
|
344 w->max_age == 0) { // rail cars got a max age of 0 |
|
345 if (!EngineHasReplacementForPlayer(p, w->engine_type, w->group_id)) continue; |
|
346 } |
|
347 |
412 |
348 /* Now replace the vehicle */ |
413 /* Now replace the vehicle */ |
349 temp_cost = ReplaceVehicle(&w, flags, cost.GetCost()); |
414 cost.AddCost(ReplaceVehicle(&w, flags, cost.GetCost(), p, new_engine)); |
350 |
|
351 if (CmdFailed(temp_cost)) break; // replace failed for some reason. Leave the vehicle alone |
|
352 |
415 |
353 if (flags & DC_EXEC && |
416 if (flags & DC_EXEC && |
354 (w->type != VEH_TRAIN || w->u.rail.first_engine == INVALID_ENGINE)) { |
417 (w->type != VEH_TRAIN || w->u.rail.first_engine == INVALID_ENGINE)) { |
355 /* now we bought a new engine and sold the old one. We need to fix the |
418 /* now we bought a new engine and sold the old one. We need to fix the |
356 * pointers in order to avoid pointing to the old one for trains: these |
419 * pointers in order to avoid pointing to the old one for trains: these |
357 * pointers should point to the front engine and not the cars |
420 * pointers should point to the front engine and not the cars |
358 */ |
421 */ |
359 v = w; |
422 v = w; |
360 } |
423 } |
361 cost.AddCost(temp_cost); |
|
362 } while (w->type == VEH_TRAIN && (w = GetNextVehicle(w)) != NULL); |
424 } while (w->type == VEH_TRAIN && (w = GetNextVehicle(w)) != NULL); |
363 |
425 |
364 if (!(flags & DC_EXEC) && (p->player_money < (cost.GetCost() + p->engine_renew_money) || cost.GetCost() == 0)) { |
426 if (flags & DC_QUERY_COST || cost.GetCost() == 0) { |
365 if (!check && p->player_money < (cost.GetCost() + p->engine_renew_money) && ( _local_player == v->owner ) && cost.GetCost() != 0) { |
427 /* We didn't do anything during the replace so we will just exit here */ |
|
428 if (stopped) v->vehstatus &= ~VS_STOPPED; |
|
429 return cost; |
|
430 } |
|
431 |
|
432 if (display_costs && !(flags & DC_EXEC)) { |
|
433 /* 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. */ |
|
435 cost.AddCost((Money)p->engine_renew_money); |
|
436 if (CmdSucceeded(cost) && GetAvailableMoneyForCommand() < cost.GetCost()) { |
|
437 /* We don't have enough money so we will set cost to failed */ |
|
438 cost.AddCost(CMD_ERROR); |
|
439 } |
|
440 } |
|
441 |
|
442 if (display_costs && CmdFailed(cost)) { |
|
443 if (GetAvailableMoneyForCommand() < cost.GetCost() && IsLocalPlayer()) { |
366 StringID message; |
444 StringID message; |
367 SetDParam(0, v->unitnumber); |
445 SetDParam(0, v->unitnumber); |
368 switch (v->type) { |
446 switch (v->type) { |
369 case VEH_TRAIN: message = STR_TRAIN_AUTORENEW_FAILED; break; |
447 case VEH_TRAIN: message = STR_TRAIN_AUTORENEW_FAILED; break; |
370 case VEH_ROAD: message = STR_ROADVEHICLE_AUTORENEW_FAILED; break; |
448 case VEH_ROAD: message = STR_ROADVEHICLE_AUTORENEW_FAILED; break; |
374 default: NOT_REACHED(); message = 0; break; |
452 default: NOT_REACHED(); message = 0; break; |
375 } |
453 } |
376 |
454 |
377 AddNewsItem(message, NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0); |
455 AddNewsItem(message, NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0); |
378 } |
456 } |
379 if (stopped) v->vehstatus &= ~VS_STOPPED; |
457 } |
380 if (display_costs) _current_player = OWNER_NONE; |
458 } |
381 return CMD_ERROR; |
459 |
382 } |
460 if (flags & DC_EXEC && CmdSucceeded(cost)) { |
383 |
461 if (v->type == VEH_TRAIN && p->renew_keep_length) { |
384 if (flags & DC_EXEC) { |
462 /* Remove wagons until the wanted length is reached */ |
385 break; // we are done replacing since the loop ran once with DC_EXEC |
463 cost.AddCost(WagonRemoval(v, old_total_length)); |
386 } else if (check) { |
464 } |
387 /* It's a test only and we know that we can do this |
465 |
388 * NOTE: payment for wagon removal is NOT included in this price */ |
466 if (display_costs && IsLocalPlayer()) { |
389 return cost; |
467 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost()); |
390 } |
468 } |
391 // now we redo the loop, but this time we actually do stuff since we know that we can do it |
469 } |
392 flags |= DC_EXEC; |
470 |
393 } |
471 /* Start the vehicle if we stopped it earlier */ |
394 |
|
395 /* If setting is on to try not to exceed the old length of the train with the replacement */ |
|
396 if (v->type == VEH_TRAIN && p->renew_keep_length) { |
|
397 Vehicle *temp; |
|
398 w = v; |
|
399 |
|
400 while (v->u.rail.cached_total_length > old_total_length) { |
|
401 // the train is too long. We will remove cars one by one from the start of the train until it's short enough |
|
402 while (w != NULL && RailVehInfo(w->engine_type)->railveh_type != RAILVEH_WAGON) { |
|
403 w = GetNextVehicle(w); |
|
404 } |
|
405 if (w == NULL) { |
|
406 // we failed to make the train short enough |
|
407 SetDParam(0, v->unitnumber); |
|
408 AddNewsItem(STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT, NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, DNC_NONE, v->index, 0); |
|
409 break; |
|
410 } |
|
411 temp = w; |
|
412 w = GetNextVehicle(w); |
|
413 DoCommand(0, (INVALID_VEHICLE << 16) | temp->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); |
|
414 MoveVehicleCargo(v, temp); |
|
415 cost.AddCost(DoCommand(0, temp->index, 0, DC_EXEC, CMD_SELL_RAIL_WAGON)); |
|
416 } |
|
417 } |
|
418 |
|
419 if (stopped) v->vehstatus &= ~VS_STOPPED; |
472 if (stopped) v->vehstatus &= ~VS_STOPPED; |
420 if (display_costs) { |
473 |
421 if (IsLocalPlayer()) ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost()); |
|
422 _current_player = OWNER_NONE; |
|
423 } |
|
424 return cost; |
474 return cost; |
425 } |
475 } |