# vehicle_cmd.cpp

Go to the documentation of this file.

    00001 /* $Id: vehicle_cmd.cpp 17598 2009-09-21 15:41:58Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "roadveh.h"
    00007 #include "gfx_func.h"
    00008 #include "news_func.h"
    00009 #include "command_func.h"
    00010 #include "company_func.h"
    00011 #include "vehicle_gui.h"
    00012 #include "train.h"
    00013 #include "newgrf_engine.h"
    00014 #include "newgrf_text.h"
    00015 #include "functions.h"
    00016 #include "window_func.h"
    00017 #include "vehicle_func.h"
    00018 #include "string_func.h"
    00019 #include "depot_map.h"
    00020 #include "vehiclelist.h"
    00021
    00022 #include "table/strings.h"
    00023
    00024 /* Tables used in vehicle.h to find the right command for a certain vehicle type */
    00025 const uint32 _veh_build_proc_table[] = {
    00026   CMD_BUILD_RAIL_VEHICLE,
    00027   CMD_BUILD_ROAD_VEH,
    00028   CMD_BUILD_SHIP,
    00029   CMD_BUILD_AIRCRAFT,
    00030 };
    00031 const uint32 _veh_sell_proc_table[] = {
    00032   CMD_SELL_RAIL_WAGON,
    00033   CMD_SELL_ROAD_VEH,
    00034   CMD_SELL_SHIP,
    00035   CMD_SELL_AIRCRAFT,
    00036 };
    00037
    00038 const uint32 _veh_refit_proc_table[] = {
    00039   CMD_REFIT_RAIL_VEHICLE,
    00040   CMD_REFIT_ROAD_VEH,
    00041   CMD_REFIT_SHIP,
    00042   CMD_REFIT_AIRCRAFT,
    00043 };
    00044
    00045 const uint32 _send_to_depot_proc_table[] = {
    00046   CMD_SEND_TRAIN_TO_DEPOT,
    00047   CMD_SEND_ROADVEH_TO_DEPOT,
    00048   CMD_SEND_SHIP_TO_DEPOT,
    00049   CMD_SEND_AIRCRAFT_TO_HANGAR,
    00050 };
    00051
    00059 CommandCost CmdStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00060 {
    00061   /* Disable the effect of p2 bit 0, when DC_AUTOREPLACE is not set */
    00062   if ((flags & DC_AUTOREPLACE) == 0) SetBit(p2, 0);
    00063
    00064   if (!IsValidVehicleID(p1)) return CMD_ERROR;
    00065
    00066   Vehicle *v = GetVehicle(p1);
    00067
    00068   if (!CheckOwnership(v->owner)) return CMD_ERROR;
    00069   if (!v->IsPrimaryVehicle()) return CMD_ERROR;
    00070
    00071   switch (v->type) {
    00072     case VEH_TRAIN:
    00073       if (v->vehstatus & VS_STOPPED && v->u.rail.cached_power == 0) return_cmd_error(STR_TRAIN_START_NO_CATENARY);
    00074       break;
    00075
    00076     case VEH_SHIP:
    00077     case VEH_ROAD:
    00078       break;
    00079
    00080     case VEH_AIRCRAFT:
    00081       /* cannot stop airplane when in flight, or when taking off / landing */
    00082       if (v->u.air.state >= STARTTAKEOFF && v->u.air.state < TERM7) return_cmd_error(STR_A017_AIRCRAFT_IS_IN_FLIGHT);
    00083       break;
    00084
    00085     default: return CMD_ERROR;
    00086   }
    00087
    00088   /* Check if this vehicle can be started/stopped. The callback will fail or
    00089    * return 0xFF if it can. */
    00090   uint16 callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
    00091   if (callback != CALLBACK_FAILED && GB(callback, 0, 8) != 0xFF && HasBit(p2, 0)) {
    00092     StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
    00093     return_cmd_error(error);
    00094   }
    00095
    00096   if (flags & DC_EXEC) {
    00097     static const StringID vehicle_waiting_in_depot[] = {
    00098       STR_8814_TRAIN_IS_WAITING_IN_DEPOT,
    00099       STR_9016_ROAD_VEHICLE_IS_WAITING,
    00100       STR_981C_SHIP_IS_WAITING_IN_DEPOT,
    00101       STR_A014_AIRCRAFT_IS_WAITING_IN,
    00102     };
    00103
    00104     static const WindowClass vehicle_list[] = {
    00105       WC_TRAINS_LIST,
    00106       WC_ROADVEH_LIST,
    00107       WC_SHIPS_LIST,
    00108       WC_AIRCRAFT_LIST,
    00109     };
    00110
    00111     if (v->IsStoppedInDepot() && (flags & DC_AUTOREPLACE) == 0) DeleteVehicleNews(p1, vehicle_waiting_in_depot[v->type]);
    00112
    00113     v->vehstatus ^= VS_STOPPED;
    00114     if (v->type != VEH_TRAIN) v->cur_speed = 0; // trains can stop 'slowly'
    00115     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00116     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
    00117     InvalidateWindowClasses(vehicle_list[v->type]);
    00118     v->MarkDirty();
    00119   }
    00120   return CommandCost();
    00121 }
    00122
    00133 CommandCost CmdMassStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00134 {
    00135   VehicleList list;
    00136   CommandCost return_value = CMD_ERROR;
    00137   VehicleType vehicle_type = (VehicleType)GB(p2, 0, 5);
    00138   bool start_stop = HasBit(p2, 5);
    00139   bool vehicle_list_window = HasBit(p2, 6);
    00140
    00141   if (vehicle_list_window) {
    00142     uint32 id = p1;
    00143     uint16 window_type = p2 & VLW_MASK;
    00144
    00145     GenerateVehicleSortList(&list, vehicle_type, _current_company, id, window_type);
    00146   } else {
    00147     /* Get the list of vehicles in the depot */
    00148     BuildDepotVehicleList(vehicle_type, tile, &list, NULL);
    00149   }
    00150
    00151   for (uint i = 0; i < list.Length(); i++) {
    00152     const Vehicle *v = list[i];
    00153
    00154     if (!!(v->vehstatus & VS_STOPPED) != start_stop) continue;
    00155
    00156     if (!vehicle_list_window) {
    00157       if (vehicle_type == VEH_TRAIN) {
    00158         if (CheckTrainInDepot(v, false) == -1) continue;
    00159       } else {
    00160         if (!(v->vehstatus & VS_HIDDEN)) continue;
    00161       }
    00162     }
    00163
    00164     CommandCost ret = DoCommand(tile, v->index, 0, flags, CMD_START_STOP_VEHICLE);
    00165
    00166     if (CmdSucceeded(ret)) {
    00167       return_value = CommandCost();
    00168       /* We know that the command is valid for at least one vehicle.
    00169        * If we haven't set DC_EXEC, then there is no point in continueing because it will be valid */
    00170       if (!(flags & DC_EXEC)) break;
    00171     }
    00172   }
    00173
    00174   return return_value;
    00175 }
    00176
    00183 CommandCost CmdDepotSellAllVehicles(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00184 {
    00185   VehicleList list;
    00186
    00187   CommandCost cost(EXPENSES_NEW_VEHICLES);
    00188   uint sell_command;
    00189   VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
    00190
    00191   switch (vehicle_type) {
    00192     case VEH_TRAIN:    sell_command = CMD_SELL_RAIL_WAGON; break;
    00193     case VEH_ROAD:     sell_command = CMD_SELL_ROAD_VEH;   break;
    00194     case VEH_SHIP:     sell_command = CMD_SELL_SHIP;       break;
    00195     case VEH_AIRCRAFT: sell_command = CMD_SELL_AIRCRAFT;   break;
    00196     default: return CMD_ERROR;
    00197   }
    00198
    00199   /* Get the list of vehicles in the depot */
    00200   BuildDepotVehicleList(vehicle_type, tile, &list, &list);
    00201
    00202   for (uint i = 0; i < list.Length(); i++) {
    00203     CommandCost ret = DoCommand(tile, list[i]->index, 1, flags, sell_command);
    00204     if (CmdSucceeded(ret)) cost.AddCost(ret);
    00205   }
    00206
    00207   if (cost.GetCost() == 0) return CMD_ERROR; // no vehicles to sell
    00208   return cost;
    00209 }
    00210
    00220 CommandCost CmdDepotMassAutoReplace(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00221 {
    00222   VehicleList list;
    00223   CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES);
    00224   VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
    00225   bool all_or_nothing = HasBit(p2, 0);
    00226
    00227   if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
    00228
    00229   /* Get the list of vehicles in the depot */
    00230   BuildDepotVehicleList(vehicle_type, tile, &list, &list, true);
    00231
    00232   bool did_something = false;
    00233
    00234   for (uint i = 0; i < list.Length(); i++) {
    00235     Vehicle *v = (Vehicle*)list[i];
    00236
    00237     /* Ensure that the vehicle completely in the depot */
    00238     if (!v->IsInDepot()) continue;
    00239
    00240     CommandCost ret = DoCommand(0, v->index, 0, flags, CMD_AUTOREPLACE_VEHICLE);
    00241
    00242     if (CmdSucceeded(ret)) {
    00243       did_something = true;
    00244       cost.AddCost(ret);
    00245     } else {
    00246       if (ret.GetErrorMessage() != STR_AUTOREPLACE_NOTHING_TO_DO && all_or_nothing) {
    00247         /* We failed to replace a vehicle even though we set all or nothing.
    00248          * We should never reach this if DC_EXEC is set since then it should
    00249          * have failed the estimation guess. */
    00250         assert(!(flags & DC_EXEC));
    00251         /* Now we will have to return an error. */
    00252         return CMD_ERROR;
    00253       }
    00254     }
    00255   }
    00256
    00257   if (!did_something) {
    00258     /* Either we didn't replace anything or something went wrong.
    00259      * Either way we want to return an error and not execute this command. */
    00260     cost = CMD_ERROR;
    00261   }
    00262
    00263   return cost;
    00264 }
    00265
    00270 static bool IsUniqueVehicleName(const char *name)
    00271 {
    00272   const Vehicle *v;
    00273
    00274   FOR_ALL_VEHICLES(v) {
    00275     if (v->name != NULL && strcmp(v->name, name) == 0) return false;
    00276   }
    00277
    00278   return true;
    00279 }
    00280
    00285 static void CloneVehicleName(const Vehicle *src, Vehicle *dst)
    00286 {
    00287   char buf[256];
    00288
    00289   /* Find the position of the first digit in the last group of digits. */
    00290   size_t number_position;
    00291   for (number_position = strlen(src->name); number_position > 0; number_position--) {
    00292     /* The design of UTF-8 lets this work simply without having to check
    00293      * for UTF-8 sequences. */
    00294     if (src->name[number_position - 1] < '0' || src->name[number_position - 1] > '9') break;
    00295   }
    00296
    00297   /* Format buffer and determine starting number. */
    00298   int num;
    00299   if (number_position == strlen(src->name)) {
    00300     /* No digit at the end, so start at number 2. */
    00301     strecpy(buf, src->name, lastof(buf));
    00302     strecat(buf, " ", lastof(buf));
    00303     number_position = strlen(buf);
    00304     num = 2;
    00305   } else {
    00306     /* Found digits, parse them and start at the next number. */
    00307     strecpy(buf, src->name, lastof(buf));
    00308     buf[number_position] = '\0';
    00309     num = strtol(&src->name[number_position], NULL, 10) + 1;
    00310   }
    00311
    00312   /* Check if this name is already taken. */
    00313   for (int max_iterations = 1000; max_iterations > 0; max_iterations--, num++) {
    00314     /* Attach the number to the temporary name. */
    00315     seprintf(&buf[number_position], lastof(buf), "%d", num);
    00316
    00317     /* Check the name is unique. */
    00318     if (IsUniqueVehicleName(buf)) {
    00319       dst->name = strdup(buf);
    00320       break;
    00321     }
    00322   }
    00323
    00324   /* All done. If we didn't find a name, it'll just use its default. */
    00325 }
    00326
    00333 CommandCost CmdCloneVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00334 {
    00335   CommandCost total_cost(EXPENSES_NEW_VEHICLES);
    00336   uint32 build_argument = 2;
    00337
    00338   if (!IsValidVehicleID(p1)) return CMD_ERROR;
    00339
    00340   Vehicle *v = GetVehicle(p1);
    00341   Vehicle *v_front = v;
    00342   Vehicle *w = NULL;
    00343   Vehicle *w_front = NULL;
    00344   Vehicle *w_rear = NULL;
    00345
    00346   /*
    00347    * v_front is the front engine in the original vehicle
    00348    * v is the car/vehicle of the original vehicle, that is currently being copied
    00349    * w_front is the front engine of the cloned vehicle
    00350    * w is the car/vehicle currently being cloned
    00351    * w_rear is the rear end of the cloned train. It's used to add more cars and is only used by trains
    00352    */
    00353
    00354   if (!CheckOwnership(v->owner)) return CMD_ERROR;
    00355
    00356   if (v->type == VEH_TRAIN && (!IsFrontEngine(v) || v->u.rail.crash_anim_pos >= 4400)) return CMD_ERROR;
    00357
    00358   /* check that we can allocate enough vehicles */
    00359   if (!(flags & DC_EXEC)) {
    00360     int veh_counter = 0;
    00361     do {
    00362       veh_counter++;
    00363     } while ((v = v->Next()) != NULL);
    00364
    00365     if (!Vehicle::AllocateList(NULL, veh_counter)) {
    00366       return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
    00367     }
    00368   }
    00369
    00370   v = v_front;
    00371
    00372   do {
    00373     if (v->type == VEH_TRAIN && IsRearDualheaded(v)) {
    00374       /* we build the rear ends of multiheaded trains with the front ones */
    00375       continue;
    00376     }
    00377
    00378     /* In case we're building a multi headed vehicle and the maximum number of
    00379      * vehicles is almost reached (e.g. max trains - 1) not all vehicles would
    00380      * be cloned. When the non-primary engines were build they were seen as
    00381      * 'new' vehicles whereas they would immediately be joined with a primary
    00382      * engine. This caused the vehicle to be not build as 'the limit' had been
    00383      * reached, resulting in partially build vehicles and such. */
    00384     DoCommandFlag build_flags = flags;
    00385     if ((flags & DC_EXEC) && !v->IsPrimaryVehicle()) build_flags |= DC_AUTOREPLACE;
    00386
    00387     CommandCost cost = DoCommand(tile, v->engine_type, build_argument, build_flags, GetCmdBuildVeh(v));
    00388     build_argument = 3; // ensure that we only assign a number to the first engine
    00389
    00390     if (CmdFailed(cost)) {
    00391       /* Can't build a part, then sell the stuff we already made; clear up the mess */
    00392       if (w_front != NULL) DoCommand(w_front->tile, w_front->index, 1, flags, GetCmdSellVeh(w_front));
    00393       return cost;
    00394     }
    00395
    00396     total_cost.AddCost(cost);
    00397
    00398     if (flags & DC_EXEC) {
    00399       w = GetVehicle(_new_vehicle_id);
    00400
    00401       if (v->type == VEH_TRAIN && HasBit(v->u.rail.flags, VRF_REVERSE_DIRECTION)) {
    00402         SetBit(w->u.rail.flags, VRF_REVERSE_DIRECTION);
    00403       }
    00404
    00405       if (v->type == VEH_TRAIN && !IsFrontEngine(v)) {
    00406         /* this s a train car
    00407          * add this unit to the end of the train */
    00408         CommandCost result = DoCommand(0, (w_rear->index << 16) | w->index, 1, flags, CMD_MOVE_RAIL_VEHICLE);
    00409         if (CmdFailed(result)) {
    00410           /* The train can't be joined to make the same consist as the original.
    00411            * Sell what we already made (clean up) and return an error.           */
    00412           DoCommand(w_front->tile, w_front->index, 1, flags, GetCmdSellVeh(w_front));
    00413           DoCommand(w_front->tile, w->index,       1, flags, GetCmdSellVeh(w));
    00414           return result; // return error and the message returned from CMD_MOVE_RAIL_VEHICLE
    00415         }
    00416       } else {
    00417         /* this is a front engine or not a train. */
    00418         w_front = w;
    00419         w->service_interval = v->service_interval;
    00420       }
    00421       w_rear = w; // trains needs to know the last car in the train, so they can add more in next loop
    00422     }
    00423   } while (v->type == VEH_TRAIN && (v = GetNextVehicle(v)) != NULL);
    00424
    00425   if (flags & DC_EXEC && v_front->type == VEH_TRAIN) {
    00426     /* for trains this needs to be the front engine due to the callback function */
    00427     _new_vehicle_id = w_front->index;
    00428   }
    00429
    00430   if (flags & DC_EXEC) {
    00431     /* Cloned vehicles belong to the same group */
    00432     DoCommand(0, v_front->group_id, w_front->index, flags, CMD_ADD_VEHICLE_GROUP);
    00433   }
    00434
    00435
    00436   /* Take care of refitting. */
    00437   w = w_front;
    00438   v = v_front;
    00439
    00440   /* Both building and refitting are influenced by newgrf callbacks, which
    00441    * makes it impossible to accurately estimate the cloning costs. In
    00442    * particular, it is possible for engines of the same type to be built with
    00443    * different numbers of articulated parts, so when refitting we have to
    00444    * loop over real vehicles first, and then the articulated parts of those
    00445    * vehicles in a different loop. */
    00446   do {
    00447     do {
    00448       if (flags & DC_EXEC) {
    00449         assert(w != NULL);
    00450
    00451         if (w->cargo_type != v->cargo_type || w->cargo_subtype != v->cargo_subtype) {
    00452           CommandCost cost = DoCommand(0, w->index, v->cargo_type | (v->cargo_subtype << 8) | 1U << 16 , flags, GetCmdRefitVeh(v));
    00453           if (CmdSucceeded(cost)) total_cost.AddCost(cost);
    00454         }
    00455
    00456         if (w->type == VEH_TRAIN && EngineHasArticPart(w)) {
    00457           w = GetNextArticPart(w);
    00458         } else if (w->type == VEH_ROAD && RoadVehHasArticPart(w)) {
    00459           w = w->Next();
    00460         } else {
    00461           break;
    00462         }
    00463       } else {
    00464         const Engine *e = GetEngine(v->engine_type);
    00465         CargoID initial_cargo = (e->CanCarryCargo() ? e->GetDefaultCargoType() : (CargoID)CT_INVALID);
    00466
    00467         if (v->cargo_type != initial_cargo && initial_cargo != CT_INVALID) {
    00468           total_cost.AddCost(GetRefitCost(v->engine_type));
    00469         }
    00470       }
    00471
    00472       if (v->type == VEH_TRAIN && EngineHasArticPart(v)) {
    00473         v = GetNextArticPart(v);
    00474       } else if (v->type == VEH_ROAD && RoadVehHasArticPart(v)) {
    00475         v = v->Next();
    00476       } else {
    00477         break;
    00478       }
    00479     } while (v != NULL);
    00480
    00481     if ((flags & DC_EXEC) && v->type == VEH_TRAIN) w = GetNextVehicle(w);
    00482   } while (v->type == VEH_TRAIN && (v = GetNextVehicle(v)) != NULL);
    00483
    00484   if (flags & DC_EXEC) {
    00485     /*
    00486      * Set the orders of the vehicle. Cannot do it earlier as we need
    00487      * the vehicle refitted before doing this, otherwise the moved
    00488      * cargo types might not match (passenger vs non-passenger)
    00489      */
    00490     DoCommand(0, (v_front->index << 16) | w_front->index, p2 & 1 ? CO_SHARE : CO_COPY, flags, CMD_CLONE_ORDER);
    00491
    00492     /* Now clone the vehicle's name, if it has one. */
    00493     if (v_front->name != NULL) CloneVehicleName(v_front, w_front);
    00494   }
    00495
    00496   /* Since we can't estimate the cost of cloning a vehicle accurately we must
    00497    * check whether the company has enough money manually. */
    00498   if (!CheckCompanyHasMoney(total_cost)) {
    00499     if (flags & DC_EXEC) {
    00500       /* The vehicle has already been bought, so now it must be sold again. */
    00501       DoCommand(w_front->tile, w_front->index, 1, flags, GetCmdSellVeh(w_front));
    00502     }
    00503     return CMD_ERROR;
    00504   }
    00505
    00506   return total_cost;
    00507 }
    00508
    00518 CommandCost SendAllVehiclesToDepot(VehicleType type, DoCommandFlag flags, bool service, Owner owner, uint16 vlw_flag, uint32 id)
    00519 {
    00520   VehicleList list;
    00521
    00522   GenerateVehicleSortList(&list, type, owner, id, vlw_flag);
    00523
    00524   /* Send all the vehicles to a depot */
    00525   for (uint i = 0; i < list.Length(); i++) {
    00526     const Vehicle *v = list[i];
    00527     CommandCost ret = DoCommand(v->tile, v->index, (service ? 1 : 0) | DEPOT_DONT_CANCEL, flags, GetCmdSendToDepot(type));
    00528
    00529     /* Return 0 if DC_EXEC is not set this is a valid goto depot command)
    00530      * In this case we know that at least one vehicle can be sent to a depot
    00531      * and we will issue the command. We can now safely quit the loop, knowing
    00532      * it will succeed at least once. With DC_EXEC we really need to send them to the depot */
    00533     if (CmdSucceeded(ret) && !(flags & DC_EXEC)) {
    00534       return CommandCost();
    00535     }
    00536   }
    00537
    00538   return (flags & DC_EXEC) ? CommandCost() : CMD_ERROR;
    00539 }
    00540
    00547 CommandCost CmdRenameVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00548 {
    00549   if (!IsValidVehicleID(p1)) return CMD_ERROR;
    00550
    00551   Vehicle *v = GetVehicle(p1);
    00552   if (!CheckOwnership(v->owner)) return CMD_ERROR;
    00553
    00554   bool reset = StrEmpty(text);
    00555
    00556   if (!reset) {
    00557     if (strlen(text) >= MAX_LENGTH_VEHICLE_NAME_BYTES) return CMD_ERROR;
    00558     if (!(flags & DC_AUTOREPLACE) && !IsUniqueVehicleName(text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
    00559   }
    00560
    00561   if (flags & DC_EXEC) {
    00562     free(v->name);
    00563     v->name = reset ? NULL : strdup(text);
    00564     InvalidateWindowClassesData(WC_TRAINS_LIST, 1);
    00565     MarkWholeScreenDirty();
    00566   }
    00567
    00568   return CommandCost();
    00569 }
    00570
    00571
    00578 CommandCost CmdChangeServiceInt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00579 {
    00580   uint16 serv_int = GetServiceIntervalClamped(p2); // Double check the service interval from the user-input
    00581
    00582   if (serv_int != p2 || !IsValidVehicleID(p1)) return CMD_ERROR;
    00583
    00584   Vehicle *v = GetVehicle(p1);
    00585
    00586   if (!CheckOwnership(v->owner)) return CMD_ERROR;
    00587
    00588   if (flags & DC_EXEC) {
    00589     v->service_interval = serv_int;
    00590     InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    00591   }
    00592
    00593   return CommandCost();
    00594 }