00001
00002
00003 #include "stdafx.h"
00004 #include "openttd.h"
00005 #include "roadveh.h"
00006 #include "ship.h"
00007 #include "news.h"
00008 #include "player_func.h"
00009 #include "engine.h"
00010 #include "debug.h"
00011 #include "vehicle_gui.h"
00012 #include "depot.h"
00013 #include "train.h"
00014 #include "aircraft.h"
00015 #include "cargotype.h"
00016 #include "group.h"
00017 #include "order.h"
00018 #include "strings_func.h"
00019 #include "command_func.h"
00020 #include "vehicle_func.h"
00021 #include "functions.h"
00022 #include "variables.h"
00023 #include "autoreplace_func.h"
00024 #include "articulated_vehicles.h"
00025
00026 #include "table/strings.h"
00027
00028
00029
00030
00031
00032 static void MoveVehicleCargo(Vehicle *dest, Vehicle *source)
00033 {
00034 Vehicle *v = dest;
00035
00036 do {
00037 do {
00038 if (source->cargo_type != dest->cargo_type)
00039 continue;
00040
00041 if (dest->cargo.Count() == dest->cargo_cap)
00042 continue;
00043
00044 uint units_moved = min(source->cargo.Count(), dest->cargo_cap - dest->cargo.Count());
00045 source->cargo.MoveTo(&dest->cargo, units_moved);
00046
00047
00048 dest->day_counter = source->day_counter;
00049 dest->tick_counter = source->tick_counter;
00050
00051 } while (source->cargo.Count() > 0 && (dest = dest->Next()) != NULL);
00052 dest = v;
00053 } while ((source = source->Next()) != NULL);
00054
00055
00056
00057
00058
00059
00060
00061 if (dest->type == VEH_TRAIN) TrainConsistChanged(dest->First(), true);
00062 }
00063
00064 static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, const EngineID engine_type)
00065 {
00066 const Order *o;
00067 const Vehicle *u;
00068
00069 if (v->type == VEH_TRAIN) {
00070 u = v->First();
00071 } else {
00072 u = v;
00073 }
00074
00075 FOR_VEHICLE_ORDERS(u, o) {
00076 if (!(o->refit_cargo < NUM_CARGO)) continue;
00077 if (!CanRefitTo(v->engine_type, o->refit_cargo)) continue;
00078 if (!CanRefitTo(engine_type, o->refit_cargo)) return false;
00079 }
00080
00081 return true;
00082 }
00083
00092 static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type)
00093 {
00094 CargoID new_cargo_type = GetEngineCargoType(engine_type);
00095
00096 if (new_cargo_type == CT_INVALID) return CT_NO_REFIT;
00097
00098 if (v->cargo_cap != 0 && (v->cargo_type == new_cargo_type || CanRefitTo(engine_type, v->cargo_type))) {
00099 if (VerifyAutoreplaceRefitForOrders(v, engine_type)) {
00100 return v->cargo_type == new_cargo_type ? (CargoID)CT_NO_REFIT : v->cargo_type;
00101 } else {
00102 return CT_INVALID;
00103 }
00104 }
00105 if (v->type != VEH_TRAIN) return CT_INVALID;
00106
00107
00108
00109 if (v->cargo_cap != 0) return CT_INVALID;
00110
00111
00112
00113 v = v->First();
00114 do {
00115 if (v->cargo_cap == 0) continue;
00116
00117 if (v->cargo_type == new_cargo_type) return CT_NO_REFIT;
00118 if (CanRefitTo(engine_type, v->cargo_type)) return v->cargo_type;
00119 } while ((v = v->Next()) != NULL);
00120 return CT_NO_REFIT;
00121 }
00122
00123
00124
00125
00126
00127
00128
00129
00130 static CommandCost ReplaceVehicle(Vehicle **w, uint32 flags, Money total_cost)
00131 {
00132 CommandCost cost;
00133 CommandCost sell_value;
00134 Vehicle *old_v = *w;
00135 const Player *p = GetPlayer(old_v->owner);
00136 EngineID new_engine_type;
00137 const UnitID cached_unitnumber = old_v->unitnumber;
00138 bool new_front = false;
00139 Vehicle *new_v = NULL;
00140 char *vehicle_name = NULL;
00141 CargoID replacement_cargo_type;
00142
00143
00144 new_engine_type = EngineReplacementForPlayer(p, old_v->engine_type, old_v->group_id);
00145
00146 if (new_engine_type == INVALID_ENGINE) new_engine_type = old_v->engine_type;
00147
00148 replacement_cargo_type = GetNewCargoTypeForReplace(old_v, new_engine_type);
00149
00150
00151 if (replacement_cargo_type == CT_INVALID) return CommandCost();
00152
00153 sell_value = DoCommand(0, old_v->index, 0, DC_QUERY_COST, GetCmdSellVeh(old_v));
00154
00155
00156
00157
00158 SubtractMoneyFromPlayer(sell_value);
00159
00160 cost = DoCommand(old_v->tile, new_engine_type, 0, flags | DC_AUTOREPLACE, GetCmdBuildVeh(old_v));
00161 if (CmdFailed(cost)) {
00162
00163 sell_value.MultiplyCost(-1);
00164 SubtractMoneyFromPlayer(sell_value);
00165 return cost;
00166 }
00167
00168 if (replacement_cargo_type != CT_NO_REFIT) {
00169
00170 CommandCost refit_cost = GetRefitCost(new_engine_type);
00171 if (old_v->type == VEH_TRAIN && RailVehInfo(new_engine_type)->railveh_type == RAILVEH_MULTIHEAD) {
00172
00173 refit_cost.AddCost(refit_cost);
00174 }
00175 cost.AddCost(refit_cost);
00176 }
00177
00178 if (flags & DC_EXEC) {
00179 new_v = GetVehicle(_new_vehicle_id);
00180 *w = new_v;
00181
00182
00183 if (replacement_cargo_type != CT_NO_REFIT) {
00184 if (CmdFailed(DoCommand(0, new_v->index, replacement_cargo_type, DC_EXEC, GetCmdRefitVeh(new_v)))) {
00185
00186 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);
00187 }
00188 }
00189
00190 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()))) {
00191
00192 SetBit(new_v->u.rail.flags, VRF_REVERSE_DIRECTION);
00193 }
00194
00195 if (old_v->type == VEH_TRAIN && !IsFrontEngine(old_v)) {
00196
00197
00198
00199
00200
00201 Vehicle *front = old_v->Previous();
00202
00203 if (IsRearDualheaded(front)) front = front->Previous();
00204
00205 DoCommand(0, (INVALID_VEHICLE << 16) | old_v->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
00206
00207 DoCommand(0, (front->index << 16) | new_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
00208 } else {
00209
00210 DoCommand(0, (old_v->index << 16) | new_v->index, old_v->IsOrderListShared() ? CO_SHARE : CO_COPY, DC_EXEC, CMD_CLONE_ORDER);
00211 new_v->cur_order_index = old_v->cur_order_index;
00212 ChangeVehicleViewWindow(old_v, new_v);
00213 new_v->profit_this_year = old_v->profit_this_year;
00214 new_v->profit_last_year = old_v->profit_last_year;
00215 new_v->service_interval = old_v->service_interval;
00216 DoCommand(0, old_v->group_id, new_v->index, flags, CMD_ADD_VEHICLE_GROUP);
00217 new_front = true;
00218 new_v->unitnumber = old_v->unitnumber;
00219 new_v->dest_tile = old_v->dest_tile;
00220
00221 new_v->current_order = old_v->current_order;
00222 if (old_v->type == VEH_TRAIN && GetNextVehicle(old_v) != NULL){
00223 Vehicle *temp_v = GetNextVehicle(old_v);
00224
00225
00226 if (IsMultiheaded(old_v) && temp_v == old_v->u.rail.other_multiheaded_part) {
00227
00228 temp_v = GetNextVehicle(temp_v);
00229 }
00230
00231 if (temp_v != NULL) {
00232 DoCommand(0, (new_v->index << 16) | temp_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
00233 }
00234 }
00235 }
00236
00237 MoveVehicleCargo(new_v->type == VEH_TRAIN ? new_v->First() : new_v, old_v);
00238
00239
00240 if (old_v->name != NULL) vehicle_name = strdup(old_v->name);
00241 } else {
00242 CommandCost tmp_move;
00243
00244 if (old_v->type == VEH_TRAIN && IsFrontEngine(old_v)) {
00245 Vehicle *next_veh = GetNextUnit(old_v);
00246 if (next_veh != NULL) {
00247
00248
00249 DoCommand(old_v->tile, new_engine_type, 0, DC_EXEC | DC_AUTOREPLACE, GetCmdBuildVeh(old_v));
00250 Vehicle *temp = GetVehicle(_new_vehicle_id);
00251 tmp_move = DoCommand(0, (temp->index << 16) | next_veh->index, 1, 0, CMD_MOVE_RAIL_VEHICLE);
00252 DoCommand(0, temp->index, 0, DC_EXEC, GetCmdSellVeh(old_v));
00253 }
00254 }
00255
00256
00257
00258 if (CmdFailed(tmp_move) || p->player_money < (cost.GetCost() + total_cost)) {
00259
00260 sell_value.MultiplyCost(-1);
00261 SubtractMoneyFromPlayer(sell_value);
00262 return CMD_ERROR;
00263 }
00264 }
00265
00266
00267
00268 sell_value.MultiplyCost(-1);
00269 SubtractMoneyFromPlayer(sell_value);
00270
00271
00272 cost.AddCost(DoCommand(0, old_v->index, 0, flags, GetCmdSellVeh(old_v)));
00273
00274 if (new_front) {
00275
00276 new_v->unitnumber = cached_unitnumber;
00277 }
00278
00279
00280 if ((flags & DC_EXEC) && vehicle_name != NULL) {
00281 _cmd_text = vehicle_name;
00282 DoCommand(0, new_v->index, 0, DC_EXEC, CMD_NAME_VEHICLE);
00283 free(vehicle_name);
00284 }
00285
00286 return cost;
00287 }
00288
00297 CommandCost MaybeReplaceVehicle(Vehicle *v, bool check, bool display_costs)
00298 {
00299 Vehicle *w;
00300 const Player *p = GetPlayer(v->owner);
00301 byte flags = 0;
00302 CommandCost cost, temp_cost;
00303 bool stopped;
00304
00305
00306
00307
00308
00309 uint16 old_total_length = (v->type == VEH_TRAIN ?
00310 (v->u.rail.cached_total_length + TILE_SIZE - 1) / TILE_SIZE * TILE_SIZE :
00311 -1
00312 );
00313
00314
00315 _current_player = v->owner;
00316
00317 assert(IsPlayerBuildableVehicleType(v));
00318
00319 assert(v->vehstatus & VS_STOPPED);
00320
00321
00322
00323
00324 stopped = v->leave_depot_instantly;
00325 v->leave_depot_instantly = false;
00326
00327 for (;;) {
00328 cost = CommandCost(EXPENSES_NEW_VEHICLES);
00329 w = v;
00330 do {
00331 if (w->type == VEH_TRAIN && IsRearDualheaded(w)) {
00332
00333 continue;
00334 }
00335
00336
00337 if (!w->NeedsAutorenewing(p) ||
00338 w->max_age == 0) {
00339 if (!EngineHasReplacementForPlayer(p, w->engine_type, w->group_id)) continue;
00340 }
00341
00342
00343 temp_cost = ReplaceVehicle(&w, flags, cost.GetCost());
00344
00345 if (CmdFailed(temp_cost)) break;
00346
00347 if (flags & DC_EXEC &&
00348 (w->type != VEH_TRAIN || w->u.rail.first_engine == INVALID_ENGINE)) {
00349
00350
00351
00352
00353 v = w;
00354 }
00355 cost.AddCost(temp_cost);
00356 } while (w->type == VEH_TRAIN && (w = GetNextVehicle(w)) != NULL);
00357
00358 if (!(flags & DC_EXEC) && (p->player_money < (cost.GetCost() + p->engine_renew_money) || cost.GetCost() == 0)) {
00359 if (!check && p->player_money < (cost.GetCost() + p->engine_renew_money) && ( _local_player == v->owner ) && cost.GetCost() != 0) {
00360 StringID message;
00361 SetDParam(0, v->unitnumber);
00362 switch (v->type) {
00363 case VEH_TRAIN: message = STR_TRAIN_AUTORENEW_FAILED; break;
00364 case VEH_ROAD: message = STR_ROADVEHICLE_AUTORENEW_FAILED; break;
00365 case VEH_SHIP: message = STR_SHIP_AUTORENEW_FAILED; break;
00366 case VEH_AIRCRAFT: message = STR_AIRCRAFT_AUTORENEW_FAILED; break;
00367
00368 default: NOT_REACHED(); message = 0; break;
00369 }
00370
00371 AddNewsItem(message, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
00372 }
00373 if (stopped) v->vehstatus &= ~VS_STOPPED;
00374 if (display_costs) _current_player = OWNER_NONE;
00375 return CMD_ERROR;
00376 }
00377
00378 if (flags & DC_EXEC) {
00379 break;
00380 } else if (check) {
00381
00382
00383 return cost;
00384 }
00385
00386 flags |= DC_EXEC;
00387 }
00388
00389
00390 if (v->type == VEH_TRAIN && p->renew_keep_length) {
00391 Vehicle *temp;
00392 w = v;
00393
00394 while (v->u.rail.cached_total_length > old_total_length) {
00395
00396 while (w != NULL && RailVehInfo(w->engine_type)->railveh_type != RAILVEH_WAGON) {
00397 w = GetNextVehicle(w);
00398 }
00399 if (w == NULL) {
00400
00401 SetDParam(0, v->unitnumber);
00402 AddNewsItem(STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
00403 break;
00404 }
00405 temp = w;
00406 w = GetNextVehicle(w);
00407 DoCommand(0, (INVALID_VEHICLE << 16) | temp->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
00408 MoveVehicleCargo(v, temp);
00409 cost.AddCost(DoCommand(0, temp->index, 0, DC_EXEC, CMD_SELL_RAIL_WAGON));
00410 }
00411 }
00412
00413 if (stopped) v->vehstatus &= ~VS_STOPPED;
00414 if (display_costs) {
00415 if (IsLocalPlayer()) ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
00416 _current_player = OWNER_NONE;
00417 }
00418 return cost;
00419 }