# 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 };