order_cmd.cpp

Go to the documentation of this file.
00001 /* $Id: order_cmd.cpp 14347 2008-09-16 20:29:09Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "order.h"
00008 #include "airport.h"
00009 #include "depot.h"
00010 #include "waypoint.h"
00011 #include "command_func.h"
00012 #include "station.h"
00013 #include "player_func.h"
00014 #include "news.h"
00015 #include "saveload.h"
00016 #include "vehicle_gui.h"
00017 #include "cargotype.h"
00018 #include "aircraft.h"
00019 #include "strings_func.h"
00020 #include "core/alloc_func.hpp"
00021 #include "functions.h"
00022 #include "window_func.h"
00023 #include "settings_type.h"
00024 #include "string_func.h"
00025 
00026 #include "table/strings.h"
00027 
00028 TileIndex _backup_orders_tile;
00029 BackuppedOrders _backup_orders_data;
00030 
00031 DEFINE_OLD_POOL_GENERIC(Order, Order)
00032 
00033 
00038 Order UnpackOldOrder(uint16 packed)
00039 {
00040   Order order;
00041   order.type    = (OrderType)GB(packed, 0, 4);
00042   order.flags   = GB(packed, 4, 4);
00043   order.dest    = GB(packed, 8, 8);
00044   order.next    = NULL;
00045 
00046   order.refit_cargo   = CT_NO_REFIT;
00047   order.refit_subtype = 0;
00048   order.wait_time     = 0;
00049   order.travel_time   = 0;
00050   order.index = 0; // avoid compiler warning
00051 
00052   // Sanity check
00053   // TTD stores invalid orders as OT_NOTHING with non-zero flags/station
00054   if (!order.IsValid() && (order.flags != 0 || order.dest != 0)) {
00055     order.type = OT_DUMMY;
00056     order.flags = 0;
00057   }
00058 
00059   return order;
00060 }
00061 
00067 static Order UnpackVersion4Order(uint16 packed)
00068 {
00069   Order order;
00070   order.type  = (OrderType)GB(packed, 0, 4);
00071   order.flags = GB(packed, 4, 4);
00072   order.dest  = GB(packed, 8, 8);
00073   order.next  = NULL;
00074   order.index = 0; // avoid compiler warning
00075   order.refit_cargo   = CT_NO_REFIT;
00076   order.refit_subtype = 0;
00077   order.wait_time     = 0;
00078   order.travel_time   = 0;
00079   return order;
00080 }
00081 
00087 void InvalidateVehicleOrder(const Vehicle *v)
00088 {
00089   InvalidateWindow(WC_VEHICLE_VIEW,      v->index);
00090   InvalidateWindow(WC_VEHICLE_ORDERS,    v->index);
00091   InvalidateWindow(WC_VEHICLE_TIMETABLE, v->index);
00092 }
00093 
00099 static void SwapOrders(Order *order1, Order *order2)
00100 {
00101   Order temp_order;
00102 
00103   temp_order = *order1;
00104   AssignOrder(order1, *order2);
00105   order1->next = order2->next;
00106   AssignOrder(order2, temp_order);
00107   order2->next = temp_order.next;
00108 }
00109 
00116 void AssignOrder(Order *order, Order data)
00117 {
00118   order->type  = data.type;
00119   order->flags = data.flags;
00120   order->dest  = data.dest;
00121 
00122   order->refit_cargo   = data.refit_cargo;
00123   order->refit_subtype = data.refit_subtype;
00124 
00125   order->wait_time   = data.wait_time;
00126   order->travel_time = data.travel_time;
00127 }
00128 
00129 
00136 static void DeleteOrderWarnings(const Vehicle* v)
00137 {
00138   DeleteVehicleNews(v->index, STR_TRAIN_HAS_TOO_FEW_ORDERS  + v->type * 4);
00139   DeleteVehicleNews(v->index, STR_TRAIN_HAS_VOID_ORDER      + v->type * 4);
00140   DeleteVehicleNews(v->index, STR_TRAIN_HAS_DUPLICATE_ENTRY + v->type * 4);
00141   DeleteVehicleNews(v->index, STR_TRAIN_HAS_INVALID_ENTRY   + v->type * 4);
00142 }
00143 
00144 
00145 static TileIndex GetOrderLocation(const Order& o)
00146 {
00147   switch (o.type) {
00148     default: NOT_REACHED();
00149     case OT_GOTO_STATION: return GetStation(o.dest)->xy;
00150     case OT_GOTO_DEPOT:   return GetDepot(o.dest)->xy;
00151   }
00152 }
00153 
00154 
00165 CommandCost CmdInsertOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00166 {
00167   Vehicle *v;
00168   VehicleID veh   = GB(p1,  0, 16);
00169   VehicleOrderID sel_ord = GB(p1, 16, 16);
00170   Order new_order = UnpackOrder(p2);
00171 
00172   if (!IsValidVehicleID(veh)) return CMD_ERROR;
00173 
00174   v = GetVehicle(veh);
00175 
00176   if (!CheckOwnership(v->owner)) return CMD_ERROR;
00177 
00178   /* Check if the inserted order is to the correct destination (owner, type),
00179    * and has the correct flags if any */
00180   switch (new_order.type) {
00181     case OT_GOTO_STATION: {
00182       const Station *st;
00183 
00184       if (!IsValidStationID(new_order.dest)) return CMD_ERROR;
00185       st = GetStation(new_order.dest);
00186 
00187       if (st->owner != OWNER_NONE && !CheckOwnership(st->owner)) {
00188         return CMD_ERROR;
00189       }
00190 
00191       switch (v->type) {
00192         case VEH_TRAIN:
00193           if (!(st->facilities & FACIL_TRAIN)) return CMD_ERROR;
00194           break;
00195 
00196         case VEH_ROAD:
00197           if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
00198             if (!(st->facilities & FACIL_BUS_STOP)) return CMD_ERROR;
00199           } else {
00200             if (!(st->facilities & FACIL_TRUCK_STOP)) return CMD_ERROR;
00201           }
00202           break;
00203 
00204         case VEH_SHIP:
00205           if (!(st->facilities & FACIL_DOCK)) return CMD_ERROR;
00206           break;
00207 
00208         case VEH_AIRCRAFT:
00209           if (!(st->facilities & FACIL_AIRPORT) || !CanAircraftUseStation(v->engine_type, st)) {
00210             return CMD_ERROR;
00211           }
00212           break;
00213 
00214         default: return CMD_ERROR;
00215       }
00216 
00217       /* Order flags can be any of the following for stations:
00218        * [full-load | unload] [+ transfer] [+ non-stop]
00219        * non-stop orders (if any) are only valid for trains */
00220       switch (new_order.flags) {
00221         case 0:
00222         case OFB_FULL_LOAD:
00223         case OFB_FULL_LOAD | OFB_TRANSFER:
00224         case OFB_UNLOAD:
00225         case OFB_UNLOAD | OFB_TRANSFER:
00226         case OFB_TRANSFER:
00227           break;
00228 
00229         case OFB_NON_STOP:
00230         case OFB_NON_STOP | OFB_FULL_LOAD:
00231         case OFB_NON_STOP | OFB_FULL_LOAD | OFB_TRANSFER:
00232         case OFB_NON_STOP | OFB_UNLOAD:
00233         case OFB_NON_STOP | OFB_UNLOAD | OFB_TRANSFER:
00234         case OFB_NON_STOP | OFB_TRANSFER:
00235           if (v->type != VEH_TRAIN) return CMD_ERROR;
00236           break;
00237 
00238         default: return CMD_ERROR;
00239       }
00240       break;
00241     }
00242 
00243     case OT_GOTO_DEPOT: {
00244       if (v->type == VEH_AIRCRAFT) {
00245         const Station* st;
00246 
00247         if (!IsValidStationID(new_order.dest)) return CMD_ERROR;
00248         st = GetStation(new_order.dest);
00249 
00250         if (!CheckOwnership(st->owner) ||
00251             !(st->facilities & FACIL_AIRPORT) ||
00252             st->Airport()->nof_depots == 0 ||
00253             !CanAircraftUseStation(v->engine_type, st)) {
00254           return CMD_ERROR;
00255         }
00256       } else {
00257         const Depot* dp;
00258 
00259         if (!IsValidDepotID(new_order.dest)) return CMD_ERROR;
00260         dp = GetDepot(new_order.dest);
00261 
00262         if (!CheckOwnership(GetTileOwner(dp->xy))) return CMD_ERROR;
00263 
00264         switch (v->type) {
00265           case VEH_TRAIN:
00266             if (!IsTileDepotType(dp->xy, TRANSPORT_RAIL)) return CMD_ERROR;
00267             break;
00268 
00269           case VEH_ROAD:
00270             if (!IsTileDepotType(dp->xy, TRANSPORT_ROAD)) return CMD_ERROR;
00271             break;
00272 
00273           case VEH_SHIP:
00274             if (!IsTileDepotType(dp->xy, TRANSPORT_WATER)) return CMD_ERROR;
00275             break;
00276 
00277           default: return CMD_ERROR;
00278         }
00279       }
00280 
00281       /* Order flags can be any of the following for depots:
00282        * order [+ halt] [+ non-stop]
00283        * non-stop orders (if any) are only valid for trains */
00284       switch (new_order.flags) {
00285         case OFB_PART_OF_ORDERS:
00286         case OFB_PART_OF_ORDERS | OFB_HALT_IN_DEPOT:
00287           break;
00288 
00289         case OFB_NON_STOP | OFB_PART_OF_ORDERS:
00290         case OFB_NON_STOP | OFB_PART_OF_ORDERS | OFB_HALT_IN_DEPOT:
00291           if (v->type != VEH_TRAIN) return CMD_ERROR;
00292           break;
00293 
00294         default: return CMD_ERROR;
00295       }
00296       break;
00297     }
00298 
00299     case OT_GOTO_WAYPOINT: {
00300       const Waypoint* wp;
00301 
00302       if (v->type != VEH_TRAIN) return CMD_ERROR;
00303 
00304       if (!IsValidWaypointID(new_order.dest)) return CMD_ERROR;
00305       wp = GetWaypoint(new_order.dest);
00306 
00307       if (!CheckOwnership(GetTileOwner(wp->xy))) return CMD_ERROR;
00308 
00309       /* Order flags can be any of the following for waypoints:
00310        * [non-stop]
00311        * non-stop orders (if any) are only valid for trains */
00312       switch (new_order.flags) {
00313         case 0: break;
00314 
00315         case OFB_NON_STOP:
00316           if (v->type != VEH_TRAIN) return CMD_ERROR;
00317           break;
00318 
00319         default: return CMD_ERROR;
00320       }
00321       break;
00322     }
00323 
00324     default: return CMD_ERROR;
00325   }
00326 
00327   if (sel_ord > v->num_orders) return CMD_ERROR;
00328 
00329   if (!HasOrderPoolFree(1)) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS);
00330 
00331   if (v->type == VEH_SHIP && IsHumanPlayer(v->owner) && _patches.pathfinder_for_ships != VPF_NPF) {
00332     /* Make sure the new destination is not too far away from the previous */
00333     const Order *prev = NULL;
00334     uint n = 0;
00335 
00336     /* Find the last goto station or depot order before the insert location.
00337      * If the order is to be inserted at the beginning of the order list this
00338      * finds the last order in the list. */
00339     for (const Order *o = v->orders; o != NULL; o = o->next) {
00340       if (o->type == OT_GOTO_STATION || o->type == OT_GOTO_DEPOT) prev = o;
00341       if (++n == sel_ord && prev != NULL) break;
00342     }
00343     if (prev != NULL) {
00344       uint dist = DistanceManhattan(
00345         GetOrderLocation(*prev),
00346         GetOrderLocation(new_order)
00347       );
00348       if (dist >= 130) {
00349         return_cmd_error(STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO);
00350       }
00351     }
00352   }
00353 
00354   if (flags & DC_EXEC) {
00355     Vehicle *u;
00356     Order *new_o = new Order();
00357     AssignOrder(new_o, new_order);
00358 
00359     /* Create new order and link in list */
00360     if (v->orders == NULL) {
00361       v->orders = new_o;
00362     } else {
00363       /* Try to get the previous item (we are inserting above the
00364           selected) */
00365       Order *order = GetVehicleOrder(v, sel_ord - 1);
00366 
00367       if (order == NULL && GetVehicleOrder(v, sel_ord) != NULL) {
00368         /* There is no previous item, so we are altering v->orders itself
00369             But because the orders can be shared, we copy the info over
00370             the v->orders, so we don't have to change the pointers of
00371             all vehicles */
00372         SwapOrders(v->orders, new_o);
00373         /* Now update the next pointers */
00374         v->orders->next = new_o;
00375       } else if (order == NULL) {
00376         /* 'sel' is a non-existing order, add him to the end */
00377         order = GetLastVehicleOrder(v);
00378         order->next = new_o;
00379       } else {
00380         /* Put the new order in between */
00381         new_o->next = order->next;
00382         order->next = new_o;
00383       }
00384     }
00385 
00386     u = GetFirstVehicleFromSharedList(v);
00387     DeleteOrderWarnings(u);
00388     for (; u != NULL; u = u->next_shared) {
00389       /* Increase amount of orders */
00390       u->num_orders++;
00391 
00392       /* If the orderlist was empty, assign it */
00393       if (u->orders == NULL) u->orders = v->orders;
00394 
00395       assert(v->orders == u->orders);
00396 
00397       /* If there is added an order before the current one, we need
00398       to update the selected order */
00399       if (sel_ord <= u->cur_order_index) {
00400         uint cur = u->cur_order_index + 1;
00401         /* Check if we don't go out of bound */
00402         if (cur < u->num_orders)
00403           u->cur_order_index = cur;
00404       }
00405       /* Update any possible open window of the vehicle */
00406       InvalidateVehicleOrder(u);
00407     }
00408 
00409     /* Make sure to rebuild the whole list */
00410     RebuildVehicleLists();
00411   }
00412 
00413   return CommandCost();
00414 }
00415 
00420 static CommandCost DecloneOrder(Vehicle *dst, uint32 flags)
00421 {
00422   if (flags & DC_EXEC) {
00423     DeleteVehicleOrders(dst);
00424     InvalidateVehicleOrder(dst);
00425     RebuildVehicleLists();
00426   }
00427   return CommandCost();
00428 }
00429 
00434 static void RemoveSharedOrderVehicleList(Vehicle *v)
00435 {
00436   assert(v->orders != NULL);
00437   WindowClass window_class = WC_NONE;
00438 
00439   switch (v->type) {
00440     default: NOT_REACHED();
00441     case VEH_TRAIN:    window_class = WC_TRAINS_LIST;   break;
00442     case VEH_ROAD:     window_class = WC_ROADVEH_LIST;  break;
00443     case VEH_SHIP:     window_class = WC_SHIPS_LIST;    break;
00444     case VEH_AIRCRAFT: window_class = WC_AIRCRAFT_LIST; break;
00445   }
00446   DeleteWindowById(window_class, (v->orders->index << 16) | (v->type << 11) | VLW_SHARED_ORDERS | v->owner);
00447 }
00448 
00455 CommandCost CmdDeleteOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00456 {
00457   Vehicle *v, *u;
00458   VehicleID veh_id = p1;
00459   VehicleOrderID sel_ord = p2;
00460   Order *order;
00461 
00462   if (!IsValidVehicleID(veh_id)) return CMD_ERROR;
00463 
00464   v = GetVehicle(veh_id);
00465 
00466   if (!CheckOwnership(v->owner)) return CMD_ERROR;
00467 
00468   /* If we did not select an order, we maybe want to de-clone the orders */
00469   if (sel_ord >= v->num_orders)
00470     return DecloneOrder(v, flags);
00471 
00472   order = GetVehicleOrder(v, sel_ord);
00473   if (order == NULL) return CMD_ERROR;
00474 
00475   if (flags & DC_EXEC) {
00476     if (GetVehicleOrder(v, sel_ord - 1) == NULL) {
00477       if (GetVehicleOrder(v, sel_ord + 1) != NULL) {
00478         /* First item, but not the last, so we need to alter v->orders
00479             Because we can have shared order, we copy the data
00480             from the next item over the deleted */
00481         order = GetVehicleOrder(v, sel_ord + 1);
00482         SwapOrders(v->orders, order);
00483       } else {
00484         /* XXX -- The system currently can't handle a shared-order vehicle list
00485          *  open when there aren't any orders in the list, so close the window
00486          *  in this case. Of course it needs a better fix later */
00487         RemoveSharedOrderVehicleList(v);
00488         /* Last item, so clean the list */
00489         v->orders = NULL;
00490       }
00491     } else {
00492       GetVehicleOrder(v, sel_ord - 1)->next = order->next;
00493     }
00494 
00495     /* Give the item free */
00496     delete order;
00497 
00498     u = GetFirstVehicleFromSharedList(v);
00499     DeleteOrderWarnings(u);
00500     for (; u != NULL; u = u->next_shared) {
00501       u->num_orders--;
00502 
00503       if (sel_ord < u->cur_order_index)
00504         u->cur_order_index--;
00505 
00506       /* If we removed the last order, make sure the shared vehicles
00507        * also set their orders to NULL */
00508       if (v->orders == NULL) u->orders = NULL;
00509 
00510       assert(v->orders == u->orders);
00511 
00512       /* NON-stop flag is misused to see if a train is in a station that is
00513        * on his order list or not */
00514       if (sel_ord == u->cur_order_index && u->current_order.type == OT_LOADING &&
00515           HasBit(u->current_order.flags, OF_NON_STOP)) {
00516         u->current_order.flags = 0;
00517       }
00518 
00519       /* Update any possible open window of the vehicle */
00520       InvalidateVehicleOrder(u);
00521     }
00522 
00523     RebuildVehicleLists();
00524   }
00525 
00526   return CommandCost();
00527 }
00528 
00535 CommandCost CmdSkipToOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00536 {
00537   Vehicle *v;
00538   VehicleID veh_id = p1;
00539   VehicleOrderID sel_ord = p2;
00540 
00541   if (!IsValidVehicleID(veh_id)) return CMD_ERROR;
00542 
00543   v = GetVehicle(veh_id);
00544 
00545   if (!CheckOwnership(v->owner) || sel_ord == v->cur_order_index ||
00546       sel_ord >= v->num_orders || v->num_orders < 2) return CMD_ERROR;
00547 
00548   if (flags & DC_EXEC) {
00549     v->cur_order_index = sel_ord;
00550 
00551     if (v->type == VEH_ROAD) ClearSlot(v);
00552 
00553     if (v->current_order.type == OT_LOADING) {
00554       v->LeaveStation();
00555       /* NON-stop flag is misused to see if a train is in a station that is
00556        * on his order list or not */
00557       if (HasBit(v->current_order.flags, OF_NON_STOP)) v->current_order.flags = 0;
00558     }
00559 
00560     InvalidateVehicleOrder(v);
00561   }
00562 
00563   /* We have an aircraft/ship, they have a mini-schedule, so update them all */
00564   if (v->type == VEH_AIRCRAFT) InvalidateWindowClasses(WC_AIRCRAFT_LIST);
00565   if (v->type == VEH_SHIP) InvalidateWindowClasses(WC_SHIPS_LIST);
00566 
00567   return CommandCost();
00568 }
00569 
00580 CommandCost CmdMoveOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00581 {
00582   VehicleID veh = p1;
00583   VehicleOrderID moving_order = GB(p2,  0, 16);
00584   VehicleOrderID target_order = GB(p2, 16, 16);
00585 
00586   if (!IsValidVehicleID(veh)) return CMD_ERROR;
00587 
00588   Vehicle *v = GetVehicle(veh);
00589   if (!CheckOwnership(v->owner)) return CMD_ERROR;
00590 
00591   /* Don't make senseless movements */
00592   if (moving_order >= v->num_orders || target_order >= v->num_orders ||
00593       moving_order == target_order || v->num_orders <= 1)
00594     return CMD_ERROR;
00595 
00596   Order *moving_one = GetVehicleOrder(v, moving_order);
00597   /* Don't move an empty order */
00598   if (moving_one == NULL) return CMD_ERROR;
00599 
00600   if (flags & DC_EXEC) {
00601     /* Take the moving order out of the pointer-chain */
00602     Order *one_before = GetVehicleOrder(v, moving_order - 1);
00603     Order *one_past = GetVehicleOrder(v, moving_order + 1);
00604 
00605     if (one_before == NULL) {
00606       /* First order edit */
00607       v->orders = moving_one->next;
00608     } else {
00609       one_before->next = moving_one->next;
00610     }
00611 
00612     /* Insert the moving_order again in the pointer-chain */
00613     one_before = GetVehicleOrder(v, target_order - 1);
00614     one_past = GetVehicleOrder(v, target_order);
00615 
00616     moving_one->next = one_past;
00617 
00618     if (one_before == NULL) {
00619       /* first order edit */
00620       SwapOrders(v->orders, moving_one);
00621       v->orders->next = moving_one;
00622     } else {
00623       one_before->next = moving_one;
00624     }
00625 
00626     /* Update shared list */
00627     Vehicle *u = GetFirstVehicleFromSharedList(v);
00628 
00629     DeleteOrderWarnings(u);
00630 
00631     for (; u != NULL; u = u->next_shared) {
00632       /* Update the first order */
00633       if (u->orders != v->orders) u->orders = v->orders;
00634 
00635       /* Update the current order */
00636       if (u->cur_order_index == moving_order) {
00637         u->cur_order_index = target_order;
00638       } else if(u->cur_order_index > moving_order && u->cur_order_index <= target_order) {
00639         u->cur_order_index--;
00640       } else if(u->cur_order_index < moving_order && u->cur_order_index >= target_order) {
00641         u->cur_order_index++;
00642       }
00643 
00644       assert(v->orders == u->orders);
00645       /* Update any possible open window of the vehicle */
00646       InvalidateVehicleOrder(u);
00647     }
00648 
00649     /* Make sure to rebuild the whole list */
00650     RebuildVehicleLists();
00651   }
00652 
00653   return CommandCost();
00654 }
00655 
00666 CommandCost CmdModifyOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00667 {
00668   Vehicle *v;
00669   Order *order;
00670   VehicleOrderID sel_ord = GB(p1, 16, 16); // XXX - automatically truncated to 8 bits.
00671   VehicleID veh   = GB(p1,  0, 16);
00672 
00673   if (!IsValidVehicleID(veh)) return CMD_ERROR;
00674   if (p2 != OF_FULL_LOAD && p2 != OF_UNLOAD && p2 != OF_NON_STOP && p2 != OF_TRANSFER) return CMD_ERROR;
00675 
00676   v = GetVehicle(veh);
00677 
00678   if (!CheckOwnership(v->owner)) return CMD_ERROR;
00679 
00680   /* Is it a valid order? */
00681   if (sel_ord >= v->num_orders) return CMD_ERROR;
00682 
00683   order = GetVehicleOrder(v, sel_ord);
00684   if ((order->type != OT_GOTO_STATION  || GetStation(order->dest)->IsBuoy()) &&
00685       (order->type != OT_GOTO_DEPOT    || p2 == OF_UNLOAD) &&
00686       (order->type != OT_GOTO_WAYPOINT || p2 != OF_NON_STOP)) {
00687     return CMD_ERROR;
00688   }
00689 
00690   if (flags & DC_EXEC) {
00691     switch (p2) {
00692     case OF_FULL_LOAD:
00693       ToggleBit(order->flags, OF_FULL_LOAD);
00694       if (order->type != OT_GOTO_DEPOT) ClrBit(order->flags, OF_UNLOAD);
00695       break;
00696     case OF_UNLOAD:
00697       ToggleBit(order->flags, OF_UNLOAD);
00698       ClrBit(order->flags, OF_FULL_LOAD);
00699       break;
00700     case OF_NON_STOP:
00701       ToggleBit(order->flags, OF_NON_STOP);
00702       break;
00703     case OF_TRANSFER:
00704       ToggleBit(order->flags, OF_TRANSFER);
00705       break;
00706     default: NOT_REACHED();
00707     }
00708 
00709     /* Update the windows and full load flags, also for vehicles that share the same order list */
00710     {
00711       Vehicle* u;
00712 
00713       u = GetFirstVehicleFromSharedList(v);
00714       DeleteOrderWarnings(u);
00715       for (; u != NULL; u = u->next_shared) {
00716         /* Toggle u->current_order "Full load" flag if it changed.
00717          * However, as the same flag is used for depot orders, check
00718          * whether we are not going to a depot as there are three
00719          * cases where the full load flag can be active and only
00720          * one case where the flag is used for depot orders. In the
00721          * other cases for the OrderTypeByte the flags are not used,
00722          * so do not care and those orders should not be active
00723          * when this function is called.
00724          */
00725         if (sel_ord == u->cur_order_index &&
00726             u->current_order.type != OT_GOTO_DEPOT &&
00727             HasBit(u->current_order.flags, OF_FULL_LOAD) != HasBit(order->flags, OF_FULL_LOAD)) {
00728           ToggleBit(u->current_order.flags, OF_FULL_LOAD);
00729         }
00730         InvalidateVehicleOrder(u);
00731       }
00732     }
00733   }
00734 
00735   return CommandCost();
00736 }
00737 
00746 CommandCost CmdCloneOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00747 {
00748   Vehicle *dst;
00749   VehicleID veh_src = GB(p1, 16, 16);
00750   VehicleID veh_dst = GB(p1,  0, 16);
00751 
00752   if (!IsValidVehicleID(veh_dst)) return CMD_ERROR;
00753 
00754   dst = GetVehicle(veh_dst);
00755 
00756   if (!CheckOwnership(dst->owner)) return CMD_ERROR;
00757 
00758   switch (p2) {
00759     case CO_SHARE: {
00760       Vehicle *src;
00761 
00762       if (!IsValidVehicleID(veh_src)) return CMD_ERROR;
00763 
00764       src = GetVehicle(veh_src);
00765 
00766       /* Sanity checks */
00767       if (!CheckOwnership(src->owner) || dst->type != src->type || dst == src)
00768         return CMD_ERROR;
00769 
00770       /* Trucks can't share orders with busses (and visa versa) */
00771       if (src->type == VEH_ROAD) {
00772         if (src->cargo_type != dst->cargo_type && (IsCargoInClass(src->cargo_type, CC_PASSENGERS) || IsCargoInClass(dst->cargo_type, CC_PASSENGERS)))
00773           return CMD_ERROR;
00774       }
00775 
00776       /* Is the vehicle already in the shared list? */
00777       {
00778         const Vehicle* u;
00779 
00780         for (u = GetFirstVehicleFromSharedList(src); u != NULL; u = u->next_shared) {
00781           if (u == dst) return CMD_ERROR;
00782         }
00783       }
00784 
00785       if (flags & DC_EXEC) {
00786         /* If the destination vehicle had a OrderList, destroy it */
00787         DeleteVehicleOrders(dst);
00788 
00789         dst->orders = src->orders;
00790         dst->num_orders = src->num_orders;
00791 
00792         /* Link this vehicle in the shared-list */
00793         dst->next_shared = src->next_shared;
00794         dst->prev_shared = src;
00795         if (src->next_shared != NULL) src->next_shared->prev_shared = dst;
00796         src->next_shared = dst;
00797 
00798         InvalidateVehicleOrder(dst);
00799         InvalidateVehicleOrder(src);
00800 
00801         RebuildVehicleLists();
00802       }
00803     } break;
00804 
00805     case CO_COPY: {
00806       Vehicle *src;
00807       int delta;
00808 
00809       if (!IsValidVehicleID(veh_src)) return CMD_ERROR;
00810 
00811       src = GetVehicle(veh_src);
00812 
00813       /* Sanity checks */
00814       if (!CheckOwnership(src->owner) || dst->type != src->type || dst == src)
00815         return CMD_ERROR;
00816 
00817       /* Trucks can't copy all the orders from busses (and visa versa) */
00818       if (src->type == VEH_ROAD) {
00819         const Order *order;
00820         TileIndex required_dst = INVALID_TILE;
00821 
00822         FOR_VEHICLE_ORDERS(src, order) {
00823           if (order->type == OT_GOTO_STATION) {
00824             const Station *st = GetStation(order->dest);
00825             if (IsCargoInClass(dst->cargo_type, CC_PASSENGERS)) {
00826               if (st->bus_stops != NULL) required_dst = st->bus_stops->xy;
00827             } else {
00828               if (st->truck_stops != NULL) required_dst = st->truck_stops->xy;
00829             }
00830             /* This station has not the correct road-bay, so we can't copy! */
00831             if (required_dst == INVALID_TILE)
00832               return CMD_ERROR;
00833           }
00834         }
00835       }
00836 
00837       /* make sure there are orders available */
00838       delta = dst->IsOrderListShared() ? src->num_orders + 1 : src->num_orders - dst->num_orders;
00839       if (!HasOrderPoolFree(delta))
00840         return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS);
00841 
00842       if (flags & DC_EXEC) {
00843         const Order *order;
00844         Order **order_dst;
00845 
00846         /* If the destination vehicle had a OrderList, destroy it */
00847         DeleteVehicleOrders(dst);
00848 
00849         order_dst = &dst->orders;
00850         FOR_VEHICLE_ORDERS(src, order) {
00851           *order_dst = new Order();
00852           AssignOrder(*order_dst, *order);
00853           order_dst = &(*order_dst)->next;
00854         }
00855 
00856         dst->num_orders = src->num_orders;
00857 
00858         InvalidateVehicleOrder(dst);
00859 
00860         RebuildVehicleLists();
00861       }
00862     } break;
00863 
00864     case CO_UNSHARE: return DecloneOrder(dst, flags);
00865     default: return CMD_ERROR;
00866   }
00867 
00868   return CommandCost();
00869 }
00870 
00880 CommandCost CmdOrderRefit(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00881 {
00882   const Vehicle *v;
00883   Order *order;
00884   VehicleID veh = GB(p1, 0, 16);
00885   VehicleOrderID order_number  = GB(p2, 16, 8);
00886   CargoID cargo = GB(p2, 0, 8);
00887   byte subtype  = GB(p2, 8, 8);
00888 
00889   if (!IsValidVehicleID(veh)) return CMD_ERROR;
00890 
00891   v = GetVehicle(veh);
00892 
00893   if (!CheckOwnership(v->owner)) return CMD_ERROR;
00894 
00895   order = GetVehicleOrder(v, order_number);
00896   if (order == NULL) return CMD_ERROR;
00897 
00898   if (flags & DC_EXEC) {
00899     Vehicle *u;
00900 
00901     order->refit_cargo = cargo;
00902     order->refit_subtype = subtype;
00903 
00904     u = GetFirstVehicleFromSharedList(v);
00905     for (; u != NULL; u = u->next_shared) {
00906       /* Update any possible open window of the vehicle */
00907       InvalidateVehicleOrder(u);
00908 
00909       /* If the vehicle already got the current depot set as current order, then update current order as well */
00910       if (u->cur_order_index == order_number && HasBit(u->current_order.flags, OF_PART_OF_ORDERS)) {
00911         u->current_order.refit_cargo = cargo;
00912         u->current_order.refit_subtype = subtype;
00913       }
00914     }
00915   }
00916 
00917   return CommandCost();
00918 }
00919 
00926 void BackupVehicleOrders(const Vehicle *v, BackuppedOrders *bak)
00927 {
00928   /* Make sure we always have freed the stuff */
00929   free(bak->order);
00930   bak->order = NULL;
00931   free(bak->name);
00932   bak->name = NULL;
00933 
00934   /* Save general info */
00935   bak->orderindex       = v->cur_order_index;
00936   bak->group            = v->group_id;
00937   bak->service_interval = v->service_interval;
00938   if (v->name != NULL) bak->name = strdup(v->name);
00939 
00940   /* If we have shared orders, store it on a special way */
00941   if (v->IsOrderListShared()) {
00942     const Vehicle *u = (v->next_shared) ? v->next_shared : v->prev_shared;
00943 
00944     bak->clone = u->index;
00945   } else {
00946     /* Else copy the orders */
00947 
00948     /* We do not have shared orders */
00949     bak->clone = INVALID_VEHICLE;
00950 
00951 
00952     /* Count the number of orders */
00953     uint cnt = 0;
00954     const Order *order;
00955     FOR_VEHICLE_ORDERS(v, order) cnt++;
00956 
00957     /* Allocate memory for the orders plus an end-of-orders marker */
00958     bak->order = MallocT<Order>(cnt + 1);
00959 
00960     Order *dest = bak->order;
00961 
00962     /* Copy the orders */
00963     FOR_VEHICLE_ORDERS(v, order) {
00964       *dest = *order;
00965       dest++;
00966     }
00967     /* End the list with an empty order */
00968     dest->Free();
00969   }
00970 }
00971 
00977 void RestoreVehicleOrders(const Vehicle *v, const BackuppedOrders *bak)
00978 {
00979   /* If we have a custom name, process that */
00980   if (bak->name != NULL) {
00981     _cmd_text = bak->name;
00982     DoCommandP(0, v->index, 0, NULL, CMD_NAME_VEHICLE);
00983   }
00984 
00985   /* If we had shared orders, recover that */
00986   if (bak->clone != INVALID_VEHICLE) {
00987     DoCommandP(0, v->index | (bak->clone << 16), CO_SHARE, NULL, CMD_CLONE_ORDER);
00988   } else {
00989 
00990     /* CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the
00991      *  order number is one more than the current amount of orders, and because
00992      *  in network the commands are queued before send, the second insert always
00993      *  fails in test mode. By bypassing the test-mode, that no longer is a problem. */
00994     for (uint i = 0; bak->order[i].IsValid(); i++) {
00995       if (!DoCommandP(0, v->index + (i << 16), PackOrder(&bak->order[i]), NULL,
00996           CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) {
00997         break;
00998       }
00999 
01000       /* Copy timetable if enabled */
01001       if (_patches.timetabling && !DoCommandP(0, v->index | (i << 16) | (1 << 25),
01002           bak->order[i].wait_time << 16 | bak->order[i].travel_time, NULL,
01003           CMD_CHANGE_TIMETABLE | CMD_NO_TEST_IF_IN_NETWORK)) {
01004         break;
01005       }
01006     }
01007   }
01008 
01009   /* Restore vehicle order-index and service interval */
01010   DoCommandP(0, v->index, bak->orderindex | (bak->service_interval << 16) , NULL, CMD_RESTORE_ORDER_INDEX);
01011 
01012   /* Restore vehicle group */
01013   DoCommandP(0, bak->group, v->index, NULL, CMD_ADD_VEHICLE_GROUP);
01014 }
01015 
01030 CommandCost CmdRestoreOrderIndex(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
01031 {
01032   Vehicle *v;
01033   VehicleOrderID cur_ord = GB(p2,  0, 16);
01034   uint16 serv_int = GB(p2, 16, 16);
01035 
01036   if (!IsValidVehicleID(p1)) return CMD_ERROR;
01037 
01038   v = GetVehicle(p1);
01039 
01040   /* Check the vehicle type and ownership, and if the service interval and order are in range */
01041   if (!CheckOwnership(v->owner)) return CMD_ERROR;
01042   if (serv_int != GetServiceIntervalClamped(serv_int) || cur_ord >= v->num_orders) return CMD_ERROR;
01043 
01044   if (flags & DC_EXEC) {
01045     v->cur_order_index = cur_ord;
01046     v->service_interval = serv_int;
01047   }
01048 
01049   return CommandCost();
01050 }
01051 
01052 
01053 static TileIndex GetStationTileForVehicle(const Vehicle* v, const Station* st)
01054 {
01055   switch (v->type) {
01056     default: NOT_REACHED();
01057     case VEH_TRAIN:     return st->train_tile;
01058     case VEH_AIRCRAFT:  return CanAircraftUseStation(v->engine_type, st) ? st->airport_tile : 0;
01059     case VEH_SHIP:      return st->dock_tile;
01060     case VEH_ROAD:
01061       if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01062         return (st->bus_stops != NULL) ? st->bus_stops->xy : 0;
01063       } else {
01064         return (st->truck_stops != NULL) ? st->truck_stops->xy : 0;
01065       }
01066   }
01067 }
01068 
01069 
01075 void CheckOrders(const Vehicle* v)
01076 {
01077   /* Does the user wants us to check things? */
01078   if (_patches.order_review_system == 0) return;
01079 
01080   /* Do nothing for crashed vehicles */
01081   if (v->vehstatus & VS_CRASHED) return;
01082 
01083   /* Do nothing for stopped vehicles if setting is '1' */
01084   if (_patches.order_review_system == 1 && v->vehstatus & VS_STOPPED)
01085     return;
01086 
01087   /* do nothing we we're not the first vehicle in a share-chain */
01088   if (v->next_shared != NULL) return;
01089 
01090   /* Only check every 20 days, so that we don't flood the message log */
01091   if (v->owner == _local_player && v->day_counter % 20 == 0) {
01092     int n_st, problem_type = -1;
01093     const Order *order;
01094     int message = 0;
01095 
01096     /* Check the order list */
01097     n_st = 0;
01098 
01099     FOR_VEHICLE_ORDERS(v, order) {
01100       /* Dummy order? */
01101       if (order->type == OT_DUMMY) {
01102         problem_type = 1;
01103         break;
01104       }
01105       /* Does station have a load-bay for this vehicle? */
01106       if (order->type == OT_GOTO_STATION) {
01107         const Station* st = GetStation(order->dest);
01108         TileIndex required_tile = GetStationTileForVehicle(v, st);
01109 
01110         n_st++;
01111         if (required_tile == 0) problem_type = 3;
01112       }
01113     }
01114 
01115     /* Check if the last and the first order are the same */
01116     if (v->num_orders > 1) {
01117       const Order* last = GetLastVehicleOrder(v);
01118 
01119       if (v->orders->type  == last->type &&
01120           v->orders->flags == last->flags &&
01121           v->orders->dest  == last->dest) {
01122         problem_type = 2;
01123       }
01124     }
01125 
01126     /* Do we only have 1 station in our order list? */
01127     if (n_st < 2 && problem_type == -1) problem_type = 0;
01128 
01129     /* We don't have a problem */
01130     if (problem_type < 0) return;
01131 
01132     message = STR_TRAIN_HAS_TOO_FEW_ORDERS + (v->type << 2) + problem_type;
01133     //DEBUG(misc, 3, "Triggered News Item for vehicle %d", v->index);
01134 
01135     SetDParam(0, v->unitnumber);
01136     AddNewsItem(
01137       message,
01138       NEWS_FLAGS(NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, 0),
01139       v->index,
01140       0
01141     );
01142   }
01143 }
01144 
01150 void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination)
01151 {
01152   Vehicle *v;
01153 
01154   /* Aircraft have StationIDs for depot orders and never use DepotIDs
01155    * This fact is handled specially below
01156    */
01157 
01158   /* Go through all vehicles */
01159   FOR_ALL_VEHICLES(v) {
01160     Order *order;
01161     bool invalidate;
01162 
01163     /* Forget about this station if this station is removed */
01164     if (v->last_station_visited == destination && type == OT_GOTO_STATION) {
01165       v->last_station_visited = INVALID_STATION;
01166     }
01167 
01168     order = &v->current_order;
01169     if ((v->type == VEH_AIRCRAFT && order->type == OT_GOTO_DEPOT ? OT_GOTO_STATION : order->type) == type &&
01170         v->current_order.dest == destination) {
01171       order->type = OT_DUMMY;
01172       order->flags = 0;
01173       InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01174     }
01175 
01176     /* Clear the order from the order-list */
01177     invalidate = false;
01178     FOR_VEHICLE_ORDERS(v, order) {
01179       if ((v->type == VEH_AIRCRAFT && order->type == OT_GOTO_DEPOT ? OT_GOTO_STATION : order->type) == type &&
01180           order->dest == destination) {
01181         order->type = OT_DUMMY;
01182         order->flags = 0;
01183         invalidate = true;
01184       }
01185     }
01186 
01187     /* Only invalidate once, and if needed */
01188     if (invalidate) {
01189       for (const Vehicle *w = GetFirstVehicleFromSharedList(v); w != NULL; w = w->next_shared) {
01190         InvalidateVehicleOrder(w);
01191       }
01192     }
01193   }
01194 }
01195 
01203 bool VehicleHasDepotOrders(const Vehicle *v)
01204 {
01205   const Order *order;
01206 
01207   FOR_VEHICLE_ORDERS(v, order) {
01208     if (order->type == OT_GOTO_DEPOT)
01209       return true;
01210   }
01211 
01212   return false;
01213 }
01214 
01220 void DeleteVehicleOrders(Vehicle *v)
01221 {
01222   DeleteOrderWarnings(v);
01223 
01224   /* If we have a shared order-list, don't delete the list, but just
01225       remove our pointer */
01226   if (v->IsOrderListShared()) {
01227     Vehicle *u = v;
01228 
01229     v->orders = NULL;
01230     v->num_orders = 0;
01231 
01232     /* Unlink ourself */
01233     if (v->prev_shared != NULL) {
01234       v->prev_shared->next_shared = v->next_shared;
01235       u = v->prev_shared;
01236     }
01237     if (v->next_shared != NULL) {
01238       v->next_shared->prev_shared = v->prev_shared;
01239       u = v->next_shared;
01240     }
01241     v->prev_shared = NULL;
01242     v->next_shared = NULL;
01243 
01244     /* If we are the only one left in the Shared Order Vehicle List,
01245      *  remove it, as we are no longer a Shared Order Vehicle */
01246     if (u->prev_shared == NULL && u->next_shared == NULL && u->orders != NULL) RemoveSharedOrderVehicleList(u);
01247 
01248     /* We only need to update this-one, because if there is a third
01249      *  vehicle which shares the same order-list, nothing will change. If
01250      *  this is the last vehicle, the last line of the order-window
01251      *  will change from Shared order list, to Order list, so it needs
01252      *  an update */
01253     InvalidateVehicleOrder(u);
01254     return;
01255   }
01256 
01257   /* Remove the orders */
01258   Order *cur = v->orders;
01259   /* Delete the vehicle list of shared orders, if any */
01260   if (cur != NULL) RemoveSharedOrderVehicleList(v);
01261   v->orders = NULL;
01262   v->num_orders = 0;
01263 
01264   if (cur != NULL) {
01265     cur->FreeChain(); // Free the orders.
01266   }
01267 }
01268 
01269 Date GetServiceIntervalClamped(uint index)
01270 {
01271   return (_patches.servint_ispercent) ? Clamp(index, MIN_SERVINT_PERCENT, MAX_SERVINT_PERCENT) : Clamp(index, MIN_SERVINT_DAYS, MAX_SERVINT_DAYS);
01272 }
01273 
01274 
01282 bool CheckForValidOrders(const Vehicle* v)
01283 {
01284   const Order *order;
01285 
01286   FOR_VEHICLE_ORDERS(v, order) if (order->type != OT_DUMMY) return true;
01287 
01288   return false;
01289 }
01290 
01291 void InitializeOrders()
01292 {
01293   _Order_pool.CleanPool();
01294   _Order_pool.AddBlockToPool();
01295 
01296   _backup_orders_tile = 0;
01297 }
01298 
01299 static const SaveLoad _order_desc[] = {
01300   SLE_VAR(Order, type,  SLE_UINT8),
01301   SLE_VAR(Order, flags, SLE_UINT8),
01302   SLE_VAR(Order, dest,  SLE_UINT16),
01303   SLE_REF(Order, next,  REF_ORDER),
01304   SLE_CONDVAR(Order, refit_cargo,    SLE_UINT8,  36, SL_MAX_VERSION),
01305   SLE_CONDVAR(Order, refit_subtype,  SLE_UINT8,  36, SL_MAX_VERSION),
01306   SLE_CONDVAR(Order, wait_time,      SLE_UINT16, 67, SL_MAX_VERSION),
01307   SLE_CONDVAR(Order, travel_time,    SLE_UINT16, 67, SL_MAX_VERSION),
01308 
01309   /* Leftover from the minor savegame version stuff
01310    * We will never use those free bytes, but we have to keep this line to allow loading of old savegames */
01311   SLE_CONDNULL(10, 5, 35),
01312   SLE_END()
01313 };
01314 
01315 static void Save_ORDR()
01316 {
01317   Order *order;
01318 
01319   FOR_ALL_ORDERS(order) {
01320     SlSetArrayIndex(order->index);
01321     SlObject(order, _order_desc);
01322   }
01323 }
01324 
01325 static void Load_ORDR()
01326 {
01327   if (CheckSavegameVersionOldStyle(5, 2)) {
01328     /* Version older than 5.2 did not have a ->next pointer. Convert them
01329         (in the old days, the orderlist was 5000 items big) */
01330     uint len = SlGetFieldLength();
01331     uint i;
01332 
01333     if (CheckSavegameVersion(5)) {
01334       /* Pre-version 5 had an other layout for orders
01335           (uint16 instead of uint32) */
01336       len /= sizeof(uint16);
01337       uint16 *orders = MallocT<uint16>(len + 1);
01338 
01339       SlArray(orders, len, SLE_UINT16);
01340 
01341       for (i = 0; i < len; ++i) {
01342         Order *order = new (i) Order();
01343         AssignOrder(order, UnpackVersion4Order(orders[i]));
01344       }
01345 
01346       free(orders);
01347     } else if (CheckSavegameVersionOldStyle(5, 2)) {
01348       len /= sizeof(uint16);
01349       uint16 *orders = MallocT<uint16>(len + 1);
01350 
01351       SlArray(orders, len, SLE_UINT32);
01352 
01353       for (i = 0; i < len; ++i) {
01354         Order *order = new (i) Order();
01355         AssignOrder(order, UnpackOrder(orders[i]));
01356       }
01357 
01358       free(orders);
01359     }
01360 
01361     /* Update all the next pointer */
01362     for (i = 1; i < len; ++i) {
01363       /* The orders were built like this:
01364        *   While the order is valid, set the previous will get it's next pointer set
01365        *   We start with index 1 because no order will have the first in it's next pointer */
01366       if (GetOrder(i)->IsValid())
01367         GetOrder(i - 1)->next = GetOrder(i);
01368     }
01369   } else {
01370     int index;
01371 
01372     while ((index = SlIterateArray()) != -1) {
01373       Order *order = new (index) Order();
01374       SlObject(order, _order_desc);
01375     }
01376   }
01377 }
01378 
01379 extern const ChunkHandler _order_chunk_handlers[] = {
01380   { 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY | CH_LAST},
01381 };

Generated on Mon Sep 22 20:34:17 2008 for openttd by  doxygen 1.5.6