order_cmd.cpp

Go to the documentation of this file.
00001 /* $Id: order_cmd.cpp 16804 2009-07-13 10:16:50Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "debug.h"
00007 #include "command_func.h"
00008 #include "company_func.h"
00009 #include "news_func.h"
00010 #include "vehicle_gui.h"
00011 #include "cargotype.h"
00012 #include "station_map.h"
00013 #include "vehicle_base.h"
00014 #include "strings_func.h"
00015 #include "functions.h"
00016 #include "window_func.h"
00017 #include "newgrf_cargo.h"
00018 #include "timetable.h"
00019 #include "vehicle_func.h"
00020 #include "oldpool_func.h"
00021 #include "depot_base.h"
00022 #include "settings_type.h"
00023 
00024 #include "table/strings.h"
00025 
00026 /* DestinationID must be at least as large as every these below, because it can
00027  * be any of them
00028  */
00029 assert_compile(sizeof(DestinationID) >= sizeof(DepotID));
00030 assert_compile(sizeof(DestinationID) >= sizeof(WaypointID));
00031 assert_compile(sizeof(DestinationID) >= sizeof(StationID));
00032 
00033 TileIndex _backup_orders_tile;
00034 BackuppedOrders _backup_orders_data;
00035 
00036 DEFINE_OLD_POOL_GENERIC(Order, Order);
00037 DEFINE_OLD_POOL_GENERIC(OrderList, OrderList);
00038 
00039 void Order::Free()
00040 {
00041   this->type  = OT_NOTHING;
00042   this->flags = 0;
00043   this->dest  = 0;
00044   this->next  = NULL;
00045 }
00046 
00047 void Order::MakeGoToStation(StationID destination)
00048 {
00049   this->type = OT_GOTO_STATION;
00050   this->flags = 0;
00051   this->dest = destination;
00052 }
00053 
00054 void Order::MakeGoToDepot(DepotID destination, OrderDepotTypeFlags order, OrderNonStopFlags non_stop_type, OrderDepotActionFlags action, CargoID cargo, byte subtype)
00055 {
00056   this->type = OT_GOTO_DEPOT;
00057   this->SetDepotOrderType(order);
00058   this->SetDepotActionType(action);
00059   this->SetNonStopType(non_stop_type);
00060   this->dest = destination;
00061   this->SetRefit(cargo, subtype);
00062 }
00063 
00064 void Order::MakeGoToWaypoint(WaypointID destination)
00065 {
00066   this->type = OT_GOTO_WAYPOINT;
00067   this->flags = 0;
00068   this->dest = destination;
00069 }
00070 
00071 void Order::MakeLoading(bool ordered)
00072 {
00073   this->type = OT_LOADING;
00074   if (!ordered) this->flags = 0;
00075 }
00076 
00077 void Order::MakeLeaveStation()
00078 {
00079   this->type = OT_LEAVESTATION;
00080   this->flags = 0;
00081 }
00082 
00083 void Order::MakeDummy()
00084 {
00085   this->type = OT_DUMMY;
00086   this->flags = 0;
00087 }
00088 
00089 void Order::MakeConditional(VehicleOrderID order)
00090 {
00091   this->type = OT_CONDITIONAL;
00092   this->flags = order;
00093   this->dest = 0;
00094 }
00095 
00096 void Order::SetRefit(CargoID cargo, byte subtype)
00097 {
00098   this->refit_cargo = cargo;
00099   this->refit_subtype = subtype;
00100 }
00101 
00102 bool Order::Equals(const Order &other) const
00103 {
00104   /* In case of go to nearest depot orders we need "only" compare the flags
00105    * with the other and not the nearest depot order bit or the actual
00106    * destination because those get clear/filled in during the order
00107    * evaluation. If we do not do this the order will continuously be seen as
00108    * a different order and it will try to find a "nearest depot" every tick. */
00109   if ((this->type == OT_GOTO_DEPOT && this->type == other.type) &&
00110       ((this->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0 ||
00111        (other.GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0)) {
00112     return
00113       this->GetDepotOrderType() == other.GetDepotOrderType() &&
00114       (this->GetDepotActionType() & ~ODATFB_NEAREST_DEPOT) == (other.GetDepotActionType() & ~ODATFB_NEAREST_DEPOT);
00115   }
00116 
00117   return
00118       this->type  == other.type &&
00119       this->flags == other.flags &&
00120       this->dest  == other.dest;
00121 }
00122 
00123 uint32 Order::Pack() const
00124 {
00125   return this->dest << 16 | this->flags << 8 | this->type;
00126 }
00127 
00128 uint16 Order::MapOldOrder() const
00129 {
00130   uint16 order = this->GetType();
00131   switch (this->type) {
00132     case OT_GOTO_STATION:
00133       if (this->GetUnloadType() & OUFB_UNLOAD) SetBit(order, 5);
00134       if (this->GetLoadType() & OLFB_FULL_LOAD) SetBit(order, 6);
00135       if (this->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) SetBit(order, 7);
00136       order |= GB(this->GetDestination(), 0, 8) << 8;
00137       break;
00138     case OT_GOTO_DEPOT:
00139       if (!(this->GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) SetBit(order, 6);
00140       SetBit(order, 7);
00141       order |= GB(this->GetDestination(), 0, 8) << 8;
00142       break;
00143     case OT_LOADING:
00144       if (this->GetLoadType() & OLFB_FULL_LOAD) SetBit(order, 6);
00145       break;
00146   }
00147   return order;
00148 }
00149 
00150 Order::Order(uint32 packed)
00151 {
00152   this->type    = (OrderType)GB(packed,  0,  8);
00153   this->flags   = GB(packed,  8,  8);
00154   this->dest    = GB(packed, 16, 16);
00155   this->next    = NULL;
00156   this->refit_cargo   = CT_NO_REFIT;
00157   this->refit_subtype = 0;
00158   this->wait_time     = 0;
00159   this->travel_time   = 0;
00160 }
00161 
00167 void InvalidateVehicleOrder(const Vehicle *v, int data)
00168 {
00169   InvalidateWindow(WC_VEHICLE_VIEW, v->index);
00170 
00171   if (data != 0) {
00172     /* Calls SetDirty() too */
00173     InvalidateWindowData(WC_VEHICLE_ORDERS,    v->index, data);
00174     InvalidateWindowData(WC_VEHICLE_TIMETABLE, v->index, data);
00175     return;
00176   }
00177 
00178   InvalidateWindow(WC_VEHICLE_ORDERS,    v->index);
00179   InvalidateWindow(WC_VEHICLE_TIMETABLE, v->index);
00180 }
00181 
00188 void Order::AssignOrder(const Order &other)
00189 {
00190   this->type  = other.type;
00191   this->flags = other.flags;
00192   this->dest  = other.dest;
00193 
00194   this->refit_cargo   = other.refit_cargo;
00195   this->refit_subtype = other.refit_subtype;
00196 
00197   this->wait_time   = other.wait_time;
00198   this->travel_time = other.travel_time;
00199 }
00200 
00201 
00202 OrderList::OrderList(Order *chain, Vehicle *v) :
00203     first(chain), num_orders(0), num_vehicles(1), first_shared(v),
00204     timetable_duration(0)
00205 {
00206   for (Order *o = this->first; o != NULL; o = o->next) {
00207     ++this->num_orders;
00208     this->timetable_duration += o->wait_time + o->travel_time;
00209   }
00210 
00211   for (Vehicle *u = v->PreviousShared(); u != NULL; u = u->PreviousShared()) {
00212     ++this->num_vehicles;
00213     this->first_shared = u;
00214   }
00215 
00216   for (const Vehicle *u = v->NextShared(); u != NULL; u = u->NextShared()) ++this->num_vehicles;
00217 }
00218 
00219 void OrderList::FreeChain(bool keep_orderlist)
00220 {
00221   Order *next;
00222   for(Order *o = this->first; o != NULL; o = next) {
00223     next = o->next;
00224     delete o;
00225   }
00226 
00227   if (keep_orderlist) {
00228     this->first = NULL;
00229     this->num_orders = 0;
00230     this->timetable_duration = 0;
00231   } else {
00232     delete this;
00233   }
00234 }
00235 
00236 Order *OrderList::GetOrderAt(int index) const
00237 {
00238   if (index < 0) return NULL;
00239 
00240   Order *order = this->first;
00241 
00242   while (order != NULL && index-- > 0)
00243     order = order->next;
00244 
00245   return order;
00246 }
00247 
00248 void OrderList::InsertOrderAt(Order *new_order, int index)
00249 {
00250   if (this->first == NULL) {
00251     this->first = new_order;
00252   } else {
00253     if (index == 0) {
00254       /* Insert as first or only order */
00255       new_order->next = this->first;
00256       this->first = new_order;
00257     } else if (index >= this->num_orders) {
00258       /* index is after the last order, add it to the end */
00259       this->GetLastOrder()->next = new_order;
00260     } else {
00261       /* Put the new order in between */
00262       Order *order = this->GetOrderAt(index - 1);
00263       new_order->next = order->next;
00264       order->next = new_order;
00265     }
00266   }
00267   ++this->num_orders;
00268   this->timetable_duration += new_order->wait_time + new_order->travel_time;
00269 }
00270 
00271 
00272 void OrderList::DeleteOrderAt(int index)
00273 {
00274   if (index >= this->num_orders) return;
00275 
00276   Order *to_remove;
00277 
00278   if (index == 0) {
00279     to_remove = this->first;
00280     this->first = to_remove->next;
00281   } else {
00282     Order *prev = GetOrderAt(index - 1);
00283     to_remove = prev->next;
00284     prev->next = to_remove->next;
00285   }
00286   --this->num_orders;
00287   this->timetable_duration -= (to_remove->wait_time + to_remove->travel_time);
00288   delete to_remove;
00289 }
00290 
00291 void OrderList::MoveOrder(int from, int to)
00292 {
00293   if (from >= this->num_orders || to >= this->num_orders || from == to) return;
00294 
00295   Order *moving_one;
00296 
00297   /* Take the moving order out of the pointer-chain */
00298   if (from == 0) {
00299     moving_one = this->first;
00300     this->first = moving_one->next;
00301   } else {
00302     Order *one_before = GetOrderAt(from - 1);
00303     moving_one = one_before->next;
00304     one_before->next = moving_one->next;
00305   }
00306 
00307   /* Insert the moving_order again in the pointer-chain */
00308   if (to == 0) {
00309     moving_one->next = this->first;
00310     this->first = moving_one;
00311   } else {
00312     Order *one_before = GetOrderAt(to - 1);
00313     moving_one->next = one_before->next;
00314     one_before->next = moving_one;
00315   }
00316 }
00317 
00318 void OrderList::RemoveVehicle(Vehicle *v)
00319 {
00320   --this->num_vehicles;
00321   if (v == this->first_shared) this->first_shared = v->NextShared();
00322 }
00323 
00324 bool OrderList::IsVehicleInSharedOrdersList(const Vehicle *v) const
00325 {
00326   for (const Vehicle *v_shared = this->first_shared; v_shared != NULL; v_shared = v_shared->NextShared()) {
00327     if (v_shared == v) return true;
00328   }
00329 
00330   return false;
00331 }
00332 
00333 int OrderList::GetPositionInSharedOrderList(const Vehicle *v) const
00334 {
00335   int count = 0;
00336   for (const Vehicle *v_shared = v->PreviousShared(); v_shared != NULL; v_shared = v_shared->PreviousShared()) count++;
00337   return count;
00338 }
00339 
00340 bool OrderList::IsCompleteTimetable() const
00341 {
00342   for (Order *o = this->first; o != NULL; o = o->next) {
00343     if (!o->IsCompletelyTimetabled()) return false;
00344   }
00345   return true;
00346 }
00347 
00348 void OrderList::DebugCheckSanity() const
00349 {
00350   VehicleOrderID check_num_orders = 0;
00351   uint check_num_vehicles = 0;
00352   uint check_timetable_duration = 0;
00353 
00354   DEBUG(misc, 6, "Checking OrderList %hu for sanity...", this->index);
00355 
00356   for (const Order *o = this->first; o != NULL; o = o->next) {
00357     ++check_num_orders;
00358     check_timetable_duration += o->wait_time + o->travel_time;
00359   }
00360   assert(this->num_orders == check_num_orders);
00361   assert(this->timetable_duration == check_timetable_duration);
00362 
00363   for (const Vehicle *v = this->first_shared; v != NULL; v = v->NextShared()) {
00364     ++check_num_vehicles;
00365     assert(v->orders.list == this);
00366   }
00367   assert(this->num_vehicles == check_num_vehicles);
00368   DEBUG(misc, 6, "... detected %u orders, %u vehicles, %u ticks", (uint)this->num_orders,
00369         this->num_vehicles, this->timetable_duration);
00370 }
00371 
00379 static inline bool OrderGoesToStation(const Vehicle *v, const Order *o)
00380 {
00381   return o->IsType(OT_GOTO_STATION) ||
00382       (v->type == VEH_AIRCRAFT && o->IsType(OT_GOTO_DEPOT) && !(o->GetDepotActionType() & ODATFB_NEAREST_DEPOT));
00383 }
00384 
00391 static void DeleteOrderWarnings(const Vehicle *v)
00392 {
00393   DeleteVehicleNews(v->index, STR_VEHICLE_HAS_TOO_FEW_ORDERS);
00394   DeleteVehicleNews(v->index, STR_VEHICLE_HAS_VOID_ORDER);
00395   DeleteVehicleNews(v->index, STR_VEHICLE_HAS_DUPLICATE_ENTRY);
00396   DeleteVehicleNews(v->index, STR_VEHICLE_HAS_INVALID_ENTRY);
00397 }
00398 
00399 
00400 static TileIndex GetOrderLocation(const Order& o)
00401 {
00402   switch (o.GetType()) {
00403     default: NOT_REACHED();
00404     case OT_GOTO_STATION: return GetStation(o.GetDestination())->xy;
00405     case OT_GOTO_DEPOT:   return GetDepot(o.GetDestination())->xy;
00406   }
00407 }
00408 
00409 static uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int conditional_depth = 0)
00410 {
00411   if (cur->IsType(OT_CONDITIONAL)) {
00412     if (conditional_depth > v->GetNumOrders()) return 0;
00413 
00414     conditional_depth++;
00415 
00416     int dist1 = GetOrderDistance(prev, GetVehicleOrder(v, cur->GetConditionSkipToOrder()), v, conditional_depth);
00417     int dist2 = GetOrderDistance(prev, cur->next == NULL ? v->orders.list->GetFirstOrder() : cur->next, v, conditional_depth);
00418     return max(dist1, dist2);
00419   }
00420 
00421   return DistanceManhattan(GetOrderLocation(*prev), GetOrderLocation(*cur));
00422 }
00423 
00434 CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00435 {
00436   Vehicle *v;
00437   VehicleID veh   = GB(p1,  0, 16);
00438   VehicleOrderID sel_ord = GB(p1, 16, 16);
00439   Order new_order(p2);
00440 
00441   if (!IsValidVehicleID(veh)) return CMD_ERROR;
00442 
00443   v = GetVehicle(veh);
00444 
00445   if (!CheckOwnership(v->owner)) return CMD_ERROR;
00446 
00447   /* Check if the inserted order is to the correct destination (owner, type),
00448    * and has the correct flags if any */
00449   switch (new_order.GetType()) {
00450     case OT_GOTO_STATION: {
00451       if (!IsValidStationID(new_order.GetDestination())) return CMD_ERROR;
00452 
00453       const Station *st = GetStation(new_order.GetDestination());
00454 
00455       if (st->owner != OWNER_NONE && !CheckOwnership(st->owner)) return CMD_ERROR;
00456 
00457       if (!CanVehicleUseStation(v, st)) return_cmd_error(STR_CAN_T_ADD_ORDER);
00458       for (Vehicle *u = v->FirstShared(); u != NULL; u = u->NextShared()) {
00459         if (!CanVehicleUseStation(u, st)) return_cmd_error(STR_CAN_T_ADD_ORDER_SHARED);
00460       }
00461 
00462       /* Non stop not allowed for non-trains. */
00463       if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN && v->type != VEH_ROAD) return CMD_ERROR;
00464 
00465       /* No load and no unload are mutual exclusive. */
00466       if ((new_order.GetLoadType() & OLFB_NO_LOAD) && (new_order.GetUnloadType() & OUFB_NO_UNLOAD)) return CMD_ERROR;
00467 
00468       /* Filter invalid load/unload types. */
00469       switch (new_order.GetLoadType()) {
00470         case OLF_LOAD_IF_POSSIBLE: case OLFB_FULL_LOAD: case OLF_FULL_LOAD_ANY: case OLFB_NO_LOAD: break;
00471         default: return CMD_ERROR;
00472       }
00473       switch (new_order.GetUnloadType()) {
00474         case OUF_UNLOAD_IF_POSSIBLE: case OUFB_UNLOAD: case OUFB_TRANSFER: case OUFB_NO_UNLOAD: break;
00475         default: return CMD_ERROR;
00476       }
00477       break;
00478     }
00479 
00480     case OT_GOTO_DEPOT: {
00481       if (new_order.GetDepotActionType() != ODATFB_NEAREST_DEPOT) {
00482         if (v->type == VEH_AIRCRAFT) {
00483           if (!IsValidStationID(new_order.GetDestination())) return CMD_ERROR;
00484 
00485           const Station *st = GetStation(new_order.GetDestination());
00486 
00487           if (!CheckOwnership(st->owner) ||
00488               !CanVehicleUseStation(v, st) ||
00489               st->Airport()->nof_depots == 0) {
00490             return CMD_ERROR;
00491           }
00492         } else {
00493           if (!IsValidDepotID(new_order.GetDestination())) return CMD_ERROR;
00494 
00495           const Depot *dp = GetDepot(new_order.GetDestination());
00496 
00497           if (!CheckOwnership(GetTileOwner(dp->xy))) return CMD_ERROR;
00498 
00499           switch (v->type) {
00500             case VEH_TRAIN:
00501               if (!IsRailDepotTile(dp->xy)) return CMD_ERROR;
00502               break;
00503 
00504             case VEH_ROAD:
00505               if (!IsRoadDepotTile(dp->xy)) return CMD_ERROR;
00506               break;
00507 
00508             case VEH_SHIP:
00509               if (!IsShipDepotTile(dp->xy)) return CMD_ERROR;
00510               break;
00511 
00512             default: return CMD_ERROR;
00513           }
00514         }
00515       } else {
00516         if (!IsCompanyBuildableVehicleType(v)) return CMD_ERROR;
00517       }
00518 
00519       if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN && v->type != VEH_ROAD) return CMD_ERROR;
00520       if (new_order.GetDepotOrderType() & ~(ODTFB_PART_OF_ORDERS | ((new_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0 ? ODTFB_SERVICE : 0))) return CMD_ERROR;
00521       if (new_order.GetDepotActionType() & ~(ODATFB_HALT | ODATFB_NEAREST_DEPOT)) return CMD_ERROR;
00522       if ((new_order.GetDepotOrderType() & ODTFB_SERVICE) && (new_order.GetDepotActionType() & ODATFB_HALT)) return CMD_ERROR;
00523       break;
00524     }
00525 
00526     case OT_GOTO_WAYPOINT: {
00527       if (v->type != VEH_TRAIN) return CMD_ERROR;
00528 
00529       if (!IsValidWaypointID(new_order.GetDestination())) return CMD_ERROR;
00530       const Waypoint *wp = GetWaypoint(new_order.GetDestination());
00531 
00532       if (!CheckOwnership(wp->owner)) return CMD_ERROR;
00533 
00534       /* Order flags can be any of the following for waypoints:
00535        * [non-stop]
00536        * non-stop orders (if any) are only valid for trains */
00537       if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN) return CMD_ERROR;
00538       break;
00539     }
00540 
00541     case OT_CONDITIONAL: {
00542       VehicleOrderID skip_to = new_order.GetConditionSkipToOrder();
00543       if (skip_to != 0 && skip_to >= v->GetNumOrders()) return CMD_ERROR; // Always allow jumping to the first (even when there is no order).
00544       if (new_order.GetConditionVariable() > OCV_END) return CMD_ERROR;
00545 
00546       OrderConditionComparator occ = new_order.GetConditionComparator();
00547       if (occ > OCC_END) return CMD_ERROR;
00548       switch (new_order.GetConditionVariable()) {
00549         case OCV_REQUIRES_SERVICE:
00550           if (occ != OCC_IS_TRUE && occ != OCC_IS_FALSE) return CMD_ERROR;
00551           break;
00552 
00553         case OCV_UNCONDITIONALLY:
00554           if (occ != OCC_EQUALS) return CMD_ERROR;
00555           if (new_order.GetConditionValue() != 0) return CMD_ERROR;
00556           break;
00557 
00558         case OCV_LOAD_PERCENTAGE:
00559         case OCV_RELIABILITY:
00560           if (new_order.GetConditionValue() > 100) return CMD_ERROR;
00561           /* FALL THROUGH */
00562         default:
00563           if (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) return CMD_ERROR;
00564           break;
00565       }
00566     } break;
00567 
00568     default: return CMD_ERROR;
00569   }
00570 
00571   if (sel_ord > v->GetNumOrders()) return CMD_ERROR;
00572 
00573   if (v->GetNumOrders() >= MAX_VEH_ORDER_ID) return_cmd_error(STR_8832_TOO_MANY_ORDERS);
00574   if (!Order::CanAllocateItem()) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS);
00575   if (v->orders.list == NULL && !OrderList::CanAllocateItem()) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS);
00576 
00577   if (v->type == VEH_SHIP && _settings_game.pf.pathfinder_for_ships != VPF_NPF) {
00578     /* Make sure the new destination is not too far away from the previous */
00579     const Order *prev = NULL;
00580     uint n = 0;
00581 
00582     /* Find the last goto station or depot order before the insert location.
00583      * If the order is to be inserted at the beginning of the order list this
00584      * finds the last order in the list. */
00585     const Order *o;
00586     FOR_VEHICLE_ORDERS(v, o) {
00587       if (o->IsType(OT_GOTO_STATION) || o->IsType(OT_GOTO_DEPOT)) prev = o;
00588       if (++n == sel_ord && prev != NULL) break;
00589     }
00590     if (prev != NULL) {
00591       uint dist = GetOrderDistance(prev, &new_order, v);
00592       if (dist >= 130) {
00593         return_cmd_error(STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO);
00594       }
00595     }
00596   }
00597 
00598   if (flags & DC_EXEC) {
00599     Order *new_o = new Order();
00600     new_o->AssignOrder(new_order);
00601 
00602     /* Create new order and link in list */
00603     if (v->orders.list == NULL) {
00604       v->orders.list = new OrderList(new_o, v);
00605     } else {
00606       v->orders.list->InsertOrderAt(new_o, sel_ord);
00607     }
00608 
00609     Vehicle *u = v->FirstShared();
00610     DeleteOrderWarnings(u);
00611     for (; u != NULL; u = u->NextShared()) {
00612       assert(v->orders.list == u->orders.list);
00613 
00614       /* If there is added an order before the current one, we need
00615       to update the selected order */
00616       if (sel_ord <= u->cur_order_index) {
00617         uint cur = u->cur_order_index + 1;
00618         /* Check if we don't go out of bound */
00619         if (cur < u->GetNumOrders())
00620           u->cur_order_index = cur;
00621       }
00622       /* Update any possible open window of the vehicle */
00623       InvalidateVehicleOrder(u, INVALID_VEH_ORDER_ID | (sel_ord << 8));
00624     }
00625 
00626     /* As we insert an order, the order to skip to will be 'wrong'. */
00627     VehicleOrderID cur_order_id = 0;
00628     Order *order;
00629     FOR_VEHICLE_ORDERS(v, order) {
00630       if (order->IsType(OT_CONDITIONAL)) {
00631         VehicleOrderID order_id = order->GetConditionSkipToOrder();
00632         if (order_id >= sel_ord) {
00633           order->SetConditionSkipToOrder(order_id + 1);
00634         }
00635         if (order_id == cur_order_id) {
00636           order->SetConditionSkipToOrder((order_id + 1) % v->GetNumOrders());
00637         }
00638       }
00639       cur_order_id++;
00640     }
00641 
00642     /* Make sure to rebuild the whole list */
00643     InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
00644   }
00645 
00646   return CommandCost();
00647 }
00648 
00653 static CommandCost DecloneOrder(Vehicle *dst, DoCommandFlag flags)
00654 {
00655   if (flags & DC_EXEC) {
00656     DeleteVehicleOrders(dst);
00657     InvalidateVehicleOrder(dst, -1);
00658     InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
00659   }
00660   return CommandCost();
00661 }
00662 
00669 CommandCost CmdDeleteOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00670 {
00671   Vehicle *v;
00672   VehicleID veh_id = p1;
00673   VehicleOrderID sel_ord = p2;
00674   Order *order;
00675 
00676   if (!IsValidVehicleID(veh_id)) return CMD_ERROR;
00677 
00678   v = GetVehicle(veh_id);
00679 
00680   if (!CheckOwnership(v->owner)) return CMD_ERROR;
00681 
00682   /* If we did not select an order, we maybe want to de-clone the orders */
00683   if (sel_ord >= v->GetNumOrders())
00684     return DecloneOrder(v, flags);
00685 
00686   order = GetVehicleOrder(v, sel_ord);
00687   if (order == NULL) return CMD_ERROR;
00688 
00689   if (flags & DC_EXEC) {
00690     v->orders.list->DeleteOrderAt(sel_ord);
00691 
00692     Vehicle *u = v->FirstShared();
00693     DeleteOrderWarnings(u);
00694     for (; u != NULL; u = u->NextShared()) {
00695       if (sel_ord < u->cur_order_index)
00696         u->cur_order_index--;
00697 
00698       assert(v->orders.list == u->orders.list);
00699 
00700       /* NON-stop flag is misused to see if a train is in a station that is
00701        * on his order list or not */
00702       if (sel_ord == u->cur_order_index && u->current_order.IsType(OT_LOADING)) {
00703         u->current_order.SetNonStopType(ONSF_STOP_EVERYWHERE);
00704       }
00705 
00706       /* Update any possible open window of the vehicle */
00707       InvalidateVehicleOrder(u, sel_ord | (INVALID_VEH_ORDER_ID << 8));
00708     }
00709 
00710     /* As we delete an order, the order to skip to will be 'wrong'. */
00711     VehicleOrderID cur_order_id = 0;
00712     FOR_VEHICLE_ORDERS(v, order) {
00713       if (order->IsType(OT_CONDITIONAL)) {
00714         VehicleOrderID order_id = order->GetConditionSkipToOrder();
00715         if (order_id >= sel_ord) {
00716           order->SetConditionSkipToOrder(max(order_id - 1, 0));
00717         }
00718         if (order_id == cur_order_id) {
00719           order->SetConditionSkipToOrder((order_id + 1) % v->GetNumOrders());
00720         }
00721       }
00722       cur_order_id++;
00723     }
00724 
00725     InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
00726   }
00727 
00728   return CommandCost();
00729 }
00730 
00737 CommandCost CmdSkipToOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00738 {
00739   Vehicle *v;
00740   VehicleID veh_id = p1;
00741   VehicleOrderID sel_ord = p2;
00742 
00743   if (!IsValidVehicleID(veh_id)) return CMD_ERROR;
00744 
00745   v = GetVehicle(veh_id);
00746 
00747   if (!CheckOwnership(v->owner) || sel_ord == v->cur_order_index ||
00748       sel_ord >= v->GetNumOrders() || v->GetNumOrders() < 2) return CMD_ERROR;
00749 
00750   if (flags & DC_EXEC) {
00751     v->cur_order_index = sel_ord;
00752 
00753     if (v->type == VEH_ROAD) ClearSlot(v);
00754 
00755     if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
00756 
00757     InvalidateVehicleOrder(v, 0);
00758   }
00759 
00760   /* We have an aircraft/ship, they have a mini-schedule, so update them all */
00761   if (v->type == VEH_AIRCRAFT) InvalidateWindowClasses(WC_AIRCRAFT_LIST);
00762   if (v->type == VEH_SHIP) InvalidateWindowClasses(WC_SHIPS_LIST);
00763 
00764   return CommandCost();
00765 }
00766 
00777 CommandCost CmdMoveOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00778 {
00779   VehicleID veh = p1;
00780   VehicleOrderID moving_order = GB(p2,  0, 16);
00781   VehicleOrderID target_order = GB(p2, 16, 16);
00782 
00783   if (!IsValidVehicleID(veh)) return CMD_ERROR;
00784 
00785   Vehicle *v = GetVehicle(veh);
00786   if (!CheckOwnership(v->owner)) return CMD_ERROR;
00787 
00788   /* Don't make senseless movements */
00789   if (moving_order >= v->GetNumOrders() || target_order >= v->GetNumOrders() ||
00790       moving_order == target_order || v->GetNumOrders() <= 1)
00791     return CMD_ERROR;
00792 
00793   Order *moving_one = GetVehicleOrder(v, moving_order);
00794   /* Don't move an empty order */
00795   if (moving_one == NULL) return CMD_ERROR;
00796 
00797   if (flags & DC_EXEC) {
00798     v->orders.list->MoveOrder(moving_order, target_order);
00799 
00800     /* Update shared list */
00801     Vehicle *u = v->FirstShared();
00802 
00803     DeleteOrderWarnings(u);
00804 
00805     for (; u != NULL; u = u->NextShared()) {
00806       /* Update the current order */
00807       if (u->cur_order_index == moving_order) {
00808         u->cur_order_index = target_order;
00809       } else if(u->cur_order_index > moving_order && u->cur_order_index <= target_order) {
00810         u->cur_order_index--;
00811       } else if(u->cur_order_index < moving_order && u->cur_order_index >= target_order) {
00812         u->cur_order_index++;
00813       }
00814 
00815       assert(v->orders.list == u->orders.list);
00816       /* Update any possible open window of the vehicle */
00817       InvalidateVehicleOrder(u, moving_order | (target_order << 8));
00818     }
00819 
00820     /* As we move an order, the order to skip to will be 'wrong'. */
00821     Order *order;
00822     FOR_VEHICLE_ORDERS(v, order) {
00823       if (order->IsType(OT_CONDITIONAL)) {
00824         VehicleOrderID order_id = order->GetConditionSkipToOrder();
00825         if (order_id == moving_order) {
00826           order_id = target_order;
00827         } else if(order_id > moving_order && order_id <= target_order) {
00828           order_id--;
00829         } else if(order_id < moving_order && order_id >= target_order) {
00830           order_id++;
00831         }
00832         order->SetConditionSkipToOrder(order_id);
00833       }
00834     }
00835 
00836     /* Make sure to rebuild the whole list */
00837     InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
00838   }
00839 
00840   return CommandCost();
00841 }
00842 
00855 CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00856 {
00857   VehicleOrderID sel_ord = GB(p1, 16, 16); // XXX - automatically truncated to 8 bits.
00858   VehicleID veh          = GB(p1,  0, 16);
00859   ModifyOrderFlags mof   = (ModifyOrderFlags)GB(p2,  0,  4);
00860   uint16 data             = GB(p2, 4, 11);
00861 
00862   if (mof >= MOF_END) return CMD_ERROR;
00863   if (!IsValidVehicleID(veh)) return CMD_ERROR;
00864 
00865   Vehicle *v = GetVehicle(veh);
00866   if (!CheckOwnership(v->owner)) return CMD_ERROR;
00867 
00868   /* Is it a valid order? */
00869   if (sel_ord >= v->GetNumOrders()) return CMD_ERROR;
00870 
00871   Order *order = GetVehicleOrder(v, sel_ord);
00872   switch (order->GetType()) {
00873     case OT_GOTO_STATION:
00874       if (mof == MOF_COND_VARIABLE || mof == MOF_COND_COMPARATOR || mof == MOF_DEPOT_ACTION || mof == MOF_COND_VALUE || GetStation(order->GetDestination())->IsBuoy()) return CMD_ERROR;
00875       break;
00876 
00877     case OT_GOTO_DEPOT:
00878       if (mof != MOF_NON_STOP && mof != MOF_DEPOT_ACTION) return CMD_ERROR;
00879       break;
00880 
00881     case OT_GOTO_WAYPOINT:
00882       if (mof != MOF_NON_STOP) return CMD_ERROR;
00883       break;
00884 
00885     case OT_CONDITIONAL:
00886       if (mof != MOF_COND_VARIABLE && mof != MOF_COND_COMPARATOR && mof != MOF_COND_VALUE && mof != MOF_COND_DESTINATION) return CMD_ERROR;
00887       break;
00888 
00889     default:
00890       return CMD_ERROR;
00891   }
00892 
00893   switch (mof) {
00894     default: NOT_REACHED();
00895 
00896     case MOF_NON_STOP:
00897       if (v->type != VEH_TRAIN && v->type != VEH_ROAD) return CMD_ERROR;
00898       if (data >= ONSF_END) return CMD_ERROR;
00899       if (data == order->GetNonStopType()) return CMD_ERROR;
00900       break;
00901 
00902     case MOF_UNLOAD:
00903       if ((data & ~(OUFB_UNLOAD | OUFB_TRANSFER | OUFB_NO_UNLOAD)) != 0) return CMD_ERROR;
00904       /* Unload and no-unload are mutual exclusive and so are transfer and no unload. */
00905       if (data != 0 && ((data & (OUFB_UNLOAD | OUFB_TRANSFER)) != 0) == ((data & OUFB_NO_UNLOAD) != 0)) return CMD_ERROR;
00906       if (data == order->GetUnloadType()) return CMD_ERROR;
00907       break;
00908 
00909     case MOF_LOAD:
00910       if (data > OLFB_NO_LOAD || data == 1) return CMD_ERROR;
00911       if (data == order->GetLoadType()) return CMD_ERROR;
00912       break;
00913 
00914     case MOF_DEPOT_ACTION:
00915       if (data >= DA_END) return CMD_ERROR;
00916       break;
00917 
00918     case MOF_COND_VARIABLE:
00919       if (data >= OCV_END) return CMD_ERROR;
00920       break;
00921 
00922     case MOF_COND_COMPARATOR:
00923       if (data >= OCC_END) return CMD_ERROR;
00924       switch (order->GetConditionVariable()) {
00925         case OCV_UNCONDITIONALLY: return CMD_ERROR;
00926 
00927         case OCV_REQUIRES_SERVICE:
00928           if (data != OCC_IS_TRUE && data != OCC_IS_FALSE) return CMD_ERROR;
00929           break;
00930 
00931         default:
00932           if (data == OCC_IS_TRUE || data == OCC_IS_FALSE) return CMD_ERROR;
00933           break;
00934       }
00935       break;
00936 
00937     case MOF_COND_VALUE:
00938       switch (order->GetConditionVariable()) {
00939         case OCV_UNCONDITIONALLY: return CMD_ERROR;
00940 
00941         case OCV_LOAD_PERCENTAGE:
00942         case OCV_RELIABILITY:
00943           if (data > 100) return CMD_ERROR;
00944           break;
00945 
00946         default:
00947           if (data > 2047) return CMD_ERROR;
00948           break;
00949       }
00950       break;
00951 
00952     case MOF_COND_DESTINATION:
00953       if (data >= v->GetNumOrders()) return CMD_ERROR;
00954       break;
00955   }
00956 
00957   if (flags & DC_EXEC) {
00958     switch (mof) {
00959       case MOF_NON_STOP:
00960         order->SetNonStopType((OrderNonStopFlags)data);
00961         break;
00962 
00963       case MOF_UNLOAD:
00964         order->SetUnloadType((OrderUnloadFlags)data);
00965         if ((data & OUFB_NO_UNLOAD) != 0 && (order->GetLoadType() & OLFB_NO_LOAD) != 0) {
00966           order->SetLoadType((OrderLoadFlags)(order->GetLoadType() & ~OLFB_NO_LOAD));
00967         }
00968         break;
00969 
00970       case MOF_LOAD:
00971         order->SetLoadType((OrderLoadFlags)data);
00972         if ((data & OLFB_NO_LOAD) != 0 && (order->GetUnloadType() & OUFB_NO_UNLOAD) != 0) {
00973           /* No load + no unload isn't compatible */
00974           order->SetUnloadType((OrderUnloadFlags)(order->GetUnloadType() & ~OUFB_NO_UNLOAD));
00975         }
00976         break;
00977 
00978       case MOF_DEPOT_ACTION: {
00979         switch (data) {
00980           case DA_ALWAYS_GO:
00981             order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() & ~ODTFB_SERVICE));
00982             order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() & ~ODATFB_HALT));
00983             break;
00984 
00985           case DA_SERVICE:
00986             order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() | ODTFB_SERVICE));
00987             order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() & ~ODATFB_HALT));
00988             break;
00989 
00990           case DA_STOP:
00991             order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() & ~ODTFB_SERVICE));
00992             order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() | ODATFB_HALT));
00993             break;
00994 
00995           default:
00996             NOT_REACHED();
00997         }
00998       } break;
00999 
01000       case MOF_COND_VARIABLE: {
01001         order->SetConditionVariable((OrderConditionVariable)data);
01002 
01003         OrderConditionComparator occ = order->GetConditionComparator();
01004         switch (order->GetConditionVariable()) {
01005           case OCV_UNCONDITIONALLY:
01006             order->SetConditionComparator(OCC_EQUALS);
01007             order->SetConditionValue(0);
01008             break;
01009 
01010           case OCV_REQUIRES_SERVICE:
01011             if (occ != OCC_IS_TRUE && occ != OCC_IS_FALSE) order->SetConditionComparator(OCC_IS_TRUE);
01012             break;
01013 
01014           case OCV_LOAD_PERCENTAGE:
01015           case OCV_RELIABILITY:
01016             if (order->GetConditionValue() > 100) order->SetConditionValue(100);
01017             /* FALL THROUGH */
01018           default:
01019             if (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) order->SetConditionComparator(OCC_EQUALS);
01020             break;
01021         }
01022       } break;
01023 
01024       case MOF_COND_COMPARATOR:
01025         order->SetConditionComparator((OrderConditionComparator)data);
01026         break;
01027 
01028       case MOF_COND_VALUE:
01029         order->SetConditionValue(data);
01030         break;
01031 
01032       case MOF_COND_DESTINATION:
01033         order->SetConditionSkipToOrder(data);
01034         break;
01035 
01036       default: NOT_REACHED();
01037     }
01038 
01039     /* Update the windows and full load flags, also for vehicles that share the same order list */
01040     Vehicle *u = v->FirstShared();
01041     DeleteOrderWarnings(u);
01042     for (; u != NULL; u = u->NextShared()) {
01043       /* Toggle u->current_order "Full load" flag if it changed.
01044        * However, as the same flag is used for depot orders, check
01045        * whether we are not going to a depot as there are three
01046        * cases where the full load flag can be active and only
01047        * one case where the flag is used for depot orders. In the
01048        * other cases for the OrderTypeByte the flags are not used,
01049        * so do not care and those orders should not be active
01050        * when this function is called.
01051        */
01052       if (sel_ord == u->cur_order_index &&
01053           (u->current_order.IsType(OT_GOTO_STATION) || u->current_order.IsType(OT_LOADING)) &&
01054           u->current_order.GetLoadType() != order->GetLoadType()) {
01055         u->current_order.SetLoadType(order->GetLoadType());
01056       }
01057       InvalidateVehicleOrder(u, 0);
01058     }
01059   }
01060 
01061   return CommandCost();
01062 }
01063 
01072 CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01073 {
01074   Vehicle *dst;
01075   VehicleID veh_src = GB(p1, 16, 16);
01076   VehicleID veh_dst = GB(p1,  0, 16);
01077 
01078   if (!IsValidVehicleID(veh_dst)) return CMD_ERROR;
01079 
01080   dst = GetVehicle(veh_dst);
01081 
01082   if (!CheckOwnership(dst->owner)) return CMD_ERROR;
01083 
01084   switch (p2) {
01085     case CO_SHARE: {
01086       Vehicle *src;
01087 
01088       if (!IsValidVehicleID(veh_src)) return CMD_ERROR;
01089 
01090       src = GetVehicle(veh_src);
01091 
01092       /* Sanity checks */
01093       if (!CheckOwnership(src->owner) || dst->type != src->type || dst == src)
01094         return CMD_ERROR;
01095 
01096       /* Trucks can't share orders with busses (and visa versa) */
01097       if (src->type == VEH_ROAD) {
01098         if (src->cargo_type != dst->cargo_type && (IsCargoInClass(src->cargo_type, CC_PASSENGERS) || IsCargoInClass(dst->cargo_type, CC_PASSENGERS)))
01099           return CMD_ERROR;
01100       }
01101 
01102       /* Is the vehicle already in the shared list? */
01103       if (src->FirstShared() == dst->FirstShared()) return CMD_ERROR;
01104 
01105       const Order *order;
01106 
01107       FOR_VEHICLE_ORDERS(src, order) {
01108         if (OrderGoesToStation(dst, order) &&
01109             !CanVehicleUseStation(dst, GetStation(order->GetDestination()))) {
01110           return_cmd_error(STR_CAN_T_COPY_SHARE_ORDER);
01111         }
01112       }
01113 
01114       if (flags & DC_EXEC) {
01115         /* If the destination vehicle had a OrderList, destroy it */
01116         DeleteVehicleOrders(dst);
01117 
01118         dst->orders.list = src->orders.list;
01119 
01120         /* Link this vehicle in the shared-list */
01121         dst->AddToShared(src);
01122 
01123         InvalidateVehicleOrder(dst, -1);
01124         InvalidateVehicleOrder(src, 0);
01125 
01126         InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
01127       }
01128     } break;
01129 
01130     case CO_COPY: {
01131       Vehicle *src;
01132       int delta;
01133 
01134       if (!IsValidVehicleID(veh_src)) return CMD_ERROR;
01135 
01136       src = GetVehicle(veh_src);
01137 
01138       /* Sanity checks */
01139       if (!CheckOwnership(src->owner) || dst->type != src->type || dst == src)
01140         return CMD_ERROR;
01141 
01142       /* Trucks can't copy all the orders from busses (and visa versa),
01143        * and neither can helicopters and aircarft. */
01144       const Order *order;
01145       FOR_VEHICLE_ORDERS(src, order) {
01146         if (OrderGoesToStation(dst, order) &&
01147             !CanVehicleUseStation(dst, GetStation(order->GetDestination()))) {
01148           return_cmd_error(STR_CAN_T_COPY_SHARE_ORDER);
01149         }
01150       }
01151 
01152       /* make sure there are orders available */
01153       delta = dst->IsOrderListShared() ? src->GetNumOrders() + 1 : src->GetNumOrders() - dst->GetNumOrders();
01154       if (!Order::CanAllocateItem(delta) ||
01155           ((dst->orders.list == NULL || dst->IsOrderListShared()) && !OrderList::CanAllocateItem())) {
01156         return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS);
01157       }
01158 
01159       if (flags & DC_EXEC) {
01160         const Order *order;
01161         Order *first = NULL;
01162         Order **order_dst;
01163 
01164         /* If the destination vehicle had an order list, destroy the chain but keep the OrderList */
01165         DeleteVehicleOrders(dst, true);
01166 
01167         order_dst = &first;
01168         FOR_VEHICLE_ORDERS(src, order) {
01169           *order_dst = new Order();
01170           (*order_dst)->AssignOrder(*order);
01171           order_dst = &(*order_dst)->next;
01172         }
01173         if (dst->orders.list == NULL) dst->orders.list = new OrderList(first, dst);
01174         else {
01175           assert(dst->orders.list->GetFirstOrder() == NULL);
01176           new (dst->orders.list) OrderList(first, dst);
01177         }
01178 
01179         InvalidateVehicleOrder(dst, -1);
01180 
01181         InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
01182       }
01183     } break;
01184 
01185     case CO_UNSHARE: return DecloneOrder(dst, flags);
01186     default: return CMD_ERROR;
01187   }
01188 
01189   return CommandCost();
01190 }
01191 
01201 CommandCost CmdOrderRefit(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01202 {
01203   const Vehicle *v;
01204   Order *order;
01205   VehicleID veh = GB(p1, 0, 16);
01206   VehicleOrderID order_number  = GB(p2, 16, 8);
01207   CargoID cargo = GB(p2, 0, 8);
01208   byte subtype  = GB(p2, 8, 8);
01209 
01210   if (!IsValidVehicleID(veh)) return CMD_ERROR;
01211 
01212   v = GetVehicle(veh);
01213 
01214   if (!CheckOwnership(v->owner)) return CMD_ERROR;
01215 
01216   order = GetVehicleOrder(v, order_number);
01217   if (order == NULL) return CMD_ERROR;
01218 
01219   if (flags & DC_EXEC) {
01220     order->SetRefit(cargo, subtype);
01221 
01222     for (Vehicle *u = v->FirstShared(); u != NULL; u = u->NextShared()) {
01223       /* Update any possible open window of the vehicle */
01224       InvalidateVehicleOrder(u, 0);
01225 
01226       /* If the vehicle already got the current depot set as current order, then update current order as well */
01227       if (u->cur_order_index == order_number && u->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01228         u->current_order.SetRefit(cargo, subtype);
01229       }
01230     }
01231   }
01232 
01233   return CommandCost();
01234 }
01235 
01242 void BackupVehicleOrders(const Vehicle *v, BackuppedOrders *bak)
01243 {
01244   /* Make sure we always have freed the stuff */
01245   free(bak->order);
01246   bak->order = NULL;
01247   free(bak->name);
01248   bak->name = NULL;
01249 
01250   /* Save general info */
01251   bak->orderindex       = v->cur_order_index;
01252   bak->group            = v->group_id;
01253   bak->service_interval = v->service_interval;
01254   if (v->name != NULL) bak->name = strdup(v->name);
01255 
01256   /* If we have shared orders, store it on a special way */
01257   if (v->IsOrderListShared()) {
01258     const Vehicle *u = (v->FirstShared() == v) ? v->NextShared() : v->FirstShared();
01259 
01260     bak->clone = u->index;
01261   } else {
01262     /* Else copy the orders */
01263 
01264     /* We do not have shared orders */
01265     bak->clone = INVALID_VEHICLE;
01266 
01267 
01268     /* Count the number of orders */
01269     uint cnt = 0;
01270     const Order *order;
01271     FOR_VEHICLE_ORDERS(v, order) cnt++;
01272 
01273     /* Allocate memory for the orders plus an end-of-orders marker */
01274     bak->order = MallocT<Order>(cnt + 1);
01275 
01276     Order *dest = bak->order;
01277 
01278     /* Copy the orders */
01279     FOR_VEHICLE_ORDERS(v, order) {
01280       memcpy(dest, order, sizeof(Order));
01281       dest++;
01282     }
01283     /* End the list with an empty order */
01284     dest->Free();
01285   }
01286 }
01287 
01293 void RestoreVehicleOrders(const Vehicle *v, const BackuppedOrders *bak)
01294 {
01295   /* If we have a custom name, process that */
01296   if (bak->name != NULL) DoCommandP(0, v->index, 0, CMD_RENAME_VEHICLE, NULL, bak->name);
01297 
01298   /* If we had shared orders, recover that */
01299   if (bak->clone != INVALID_VEHICLE) {
01300     DoCommandP(0, v->index | (bak->clone << 16), CO_SHARE, CMD_CLONE_ORDER);
01301   } else {
01302 
01303     /* CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the
01304      *  order number is one more than the current amount of orders, and because
01305      *  in network the commands are queued before send, the second insert always
01306      *  fails in test mode. By bypassing the test-mode, that no longer is a problem. */
01307     for (uint i = 0; bak->order[i].IsValid(); i++) {
01308       Order o = bak->order[i];
01309       /* Conditional orders need to have their destination to be valid on insertion. */
01310       if (o.IsType(OT_CONDITIONAL)) o.SetConditionSkipToOrder(0);
01311 
01312       if (!DoCommandP(0, v->index + (i << 16), o.Pack(),
01313           CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) {
01314         break;
01315       }
01316 
01317       /* Copy timetable if enabled */
01318       if (_settings_game.order.timetabling && !DoCommandP(0, v->index | (i << 16) | (1 << 25),
01319           o.wait_time << 16 | o.travel_time,
01320           CMD_CHANGE_TIMETABLE | CMD_NO_TEST_IF_IN_NETWORK)) {
01321         break;
01322       }
01323     }
01324 
01325       /* Fix the conditional orders' destination. */
01326     for (uint i = 0; bak->order[i].IsValid(); i++) {
01327       if (!bak->order[i].IsType(OT_CONDITIONAL)) continue;
01328 
01329       if (!DoCommandP(0, v->index + (i << 16), MOF_LOAD | (bak->order[i].GetConditionSkipToOrder() << 4),
01330           CMD_MODIFY_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) {
01331         break;
01332       }
01333     }
01334   }
01335 
01336   /* Restore vehicle order-index and service interval */
01337   DoCommandP(0, v->index, bak->orderindex | (bak->service_interval << 16) , CMD_RESTORE_ORDER_INDEX);
01338 
01339   /* Restore vehicle group */
01340   DoCommandP(0, bak->group, v->index, CMD_ADD_VEHICLE_GROUP);
01341 }
01342 
01357 CommandCost CmdRestoreOrderIndex(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01358 {
01359   Vehicle *v;
01360   VehicleOrderID cur_ord = GB(p2,  0, 16);
01361   uint16 serv_int = GB(p2, 16, 16);
01362 
01363   if (!IsValidVehicleID(p1)) return CMD_ERROR;
01364 
01365   v = GetVehicle(p1);
01366 
01367   /* Check the vehicle type and ownership, and if the service interval and order are in range */
01368   if (!CheckOwnership(v->owner)) return CMD_ERROR;
01369   if (serv_int != GetServiceIntervalClamped(serv_int) || cur_ord >= v->GetNumOrders()) return CMD_ERROR;
01370 
01371   if (flags & DC_EXEC) {
01372     v->cur_order_index = cur_ord;
01373     v->service_interval = serv_int;
01374   }
01375 
01376   return CommandCost();
01377 }
01378 
01379 
01380 static TileIndex GetStationTileForVehicle(const Vehicle *v, const Station *st)
01381 {
01382   if (!CanVehicleUseStation(v, st)) return INVALID_TILE;
01383 
01384   switch (v->type) {
01385     default: NOT_REACHED();
01386     case VEH_TRAIN:     return st->train_tile;
01387     case VEH_AIRCRAFT:  return st->airport_tile;
01388     case VEH_SHIP:      return st->dock_tile;
01389     case VEH_ROAD:      return st->GetPrimaryRoadStop(v)->xy;
01390   }
01391 }
01392 
01393 
01399 void CheckOrders(const Vehicle *v)
01400 {
01401   /* Does the user wants us to check things? */
01402   if (_settings_client.gui.order_review_system == 0) return;
01403 
01404   /* Do nothing for crashed vehicles */
01405   if (v->vehstatus & VS_CRASHED) return;
01406 
01407   /* Do nothing for stopped vehicles if setting is '1' */
01408   if (_settings_client.gui.order_review_system == 1 && v->vehstatus & VS_STOPPED)
01409     return;
01410 
01411   /* do nothing we we're not the first vehicle in a share-chain */
01412   if (v->FirstShared() != v) return;
01413 
01414   /* Only check every 20 days, so that we don't flood the message log */
01415   if (v->owner == _local_company && v->day_counter % 20 == 0) {
01416     int n_st, problem_type = -1;
01417     const Order *order;
01418     int message = 0;
01419 
01420     /* Check the order list */
01421     n_st = 0;
01422 
01423     FOR_VEHICLE_ORDERS(v, order) {
01424       /* Dummy order? */
01425       if (order->IsType(OT_DUMMY)) {
01426         problem_type = 1;
01427         break;
01428       }
01429       /* Does station have a load-bay for this vehicle? */
01430       if (order->IsType(OT_GOTO_STATION)) {
01431         const Station *st = GetStation(order->GetDestination());
01432         TileIndex required_tile = GetStationTileForVehicle(v, st);
01433 
01434         n_st++;
01435         if (required_tile == INVALID_TILE) problem_type = 3;
01436       }
01437     }
01438 
01439     /* Check if the last and the first order are the same */
01440     if (v->GetNumOrders() > 1) {
01441       const Order *last = GetLastVehicleOrder(v);
01442 
01443       if (v->orders.list->GetFirstOrder()->Equals(*last)) {
01444         problem_type = 2;
01445       }
01446     }
01447 
01448     /* Do we only have 1 station in our order list? */
01449     if (n_st < 2 && problem_type == -1) problem_type = 0;
01450 
01451 #ifndef NDEBUG
01452     if (v->orders.list != NULL) v->orders.list->DebugCheckSanity();
01453 #endif
01454 
01455     /* We don't have a problem */
01456     if (problem_type < 0) return;
01457 
01458     message = STR_VEHICLE_HAS_TOO_FEW_ORDERS + problem_type;
01459     //DEBUG(misc, 3, "Triggered News Item for vehicle %d", v->index);
01460 
01461     SetDParam(0, v->index);
01462     AddNewsItem(
01463       message,
01464       NS_ADVICE,
01465       v->index,
01466       0
01467     );
01468   }
01469 }
01470 
01476 void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination)
01477 {
01478   Vehicle *v;
01479 
01480   /* Aircraft have StationIDs for depot orders and never use DepotIDs
01481    * This fact is handled specially below
01482    */
01483 
01484   /* Go through all vehicles */
01485   FOR_ALL_VEHICLES(v) {
01486     Order *order;
01487 
01488     /* Forget about this station if this station is removed */
01489     if (v->last_station_visited == destination && type == OT_GOTO_STATION) {
01490       v->last_station_visited = INVALID_STATION;
01491     }
01492 
01493     order = &v->current_order;
01494     if ((v->type == VEH_AIRCRAFT && order->IsType(OT_GOTO_DEPOT) ? OT_GOTO_STATION : order->GetType()) == type &&
01495         v->current_order.GetDestination() == destination) {
01496       order->MakeDummy();
01497       InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01498     }
01499 
01500     /* Clear the order from the order-list */
01501     int id = -1;
01502     FOR_VEHICLE_ORDERS(v, order) {
01503       id++;
01504       if (order->IsType(OT_GOTO_DEPOT) && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
01505       if ((v->type == VEH_AIRCRAFT && order->IsType(OT_GOTO_DEPOT) ? OT_GOTO_STATION : order->GetType()) == type &&
01506           order->GetDestination() == destination) {
01507         order->MakeDummy();
01508         for (const Vehicle *w = v->FirstShared(); w != NULL; w = w->NextShared()) {
01509           /* In GUI, simulate by removing the order and adding it back */
01510           InvalidateVehicleOrder(w, id | (INVALID_VEH_ORDER_ID << 8));
01511           InvalidateVehicleOrder(w, (INVALID_VEH_ORDER_ID << 8) | id);
01512         }
01513       }
01514     }
01515   }
01516 }
01517 
01525 bool VehicleHasDepotOrders(const Vehicle *v)
01526 {
01527   const Order *order;
01528 
01529   FOR_VEHICLE_ORDERS(v, order) {
01530     if (order->IsType(OT_GOTO_DEPOT))
01531       return true;
01532   }
01533 
01534   return false;
01535 }
01536 
01542 void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist)
01543 {
01544   DeleteOrderWarnings(v);
01545 
01546   if (v->IsOrderListShared()) {
01547     /* Remove ourself from the shared order list. */
01548     v->RemoveFromShared();
01549     v->orders.list = NULL;
01550   } else if (v->orders.list != NULL) {
01551     /* Remove the orders */
01552     v->orders.list->FreeChain(keep_orderlist);
01553     if (!keep_orderlist) v->orders.list = NULL;
01554   }
01555 }
01556 
01557 Date GetServiceIntervalClamped(uint index)
01558 {
01559   return (_settings_game.vehicle.servint_ispercent) ? Clamp(index, MIN_SERVINT_PERCENT, MAX_SERVINT_PERCENT) : Clamp(index, MIN_SERVINT_DAYS, MAX_SERVINT_DAYS);
01560 }
01561 
01569 static bool CheckForValidOrders(const Vehicle *v)
01570 {
01571   const Order *order;
01572 
01573   FOR_VEHICLE_ORDERS(v, order) if (!order->IsType(OT_DUMMY)) return true;
01574 
01575   return false;
01576 }
01577 
01581 static bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value)
01582 {
01583   switch (occ) {
01584     case OCC_EQUALS:      return variable == value;
01585     case OCC_NOT_EQUALS:  return variable != value;
01586     case OCC_LESS_THAN:   return variable <  value;
01587     case OCC_LESS_EQUALS: return variable <= value;
01588     case OCC_MORE_THAN:   return variable >  value;
01589     case OCC_MORE_EQUALS: return variable >= value;
01590     case OCC_IS_TRUE:     return variable != 0;
01591     case OCC_IS_FALSE:    return variable == 0;
01592     default: NOT_REACHED();
01593   }
01594 }
01595 
01602 VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
01603 {
01604   if (order->GetType() != OT_CONDITIONAL) return INVALID_VEH_ORDER_ID;
01605 
01606   bool skip_order = false;
01607   OrderConditionComparator occ = order->GetConditionComparator();
01608   uint16 value = order->GetConditionValue();
01609 
01610   switch (order->GetConditionVariable()) {
01611     case OCV_LOAD_PERCENTAGE:  skip_order = OrderConditionCompare(occ, CalcPercentVehicleFilled(v, NULL), value); break;
01612     case OCV_RELIABILITY:      skip_order = OrderConditionCompare(occ, v->reliability * 100 >> 16,        value); break;
01613     case OCV_MAX_SPEED:        skip_order = OrderConditionCompare(occ, v->GetDisplayMaxSpeed() * 10 / 16, value); break;
01614     case OCV_AGE:              skip_order = OrderConditionCompare(occ, v->age / DAYS_IN_LEAP_YEAR,        value); break;
01615     case OCV_REQUIRES_SERVICE: skip_order = OrderConditionCompare(occ, v->NeedsServicing(),               value); break;
01616     case OCV_UNCONDITIONALLY:  skip_order = true; break;
01617     default: NOT_REACHED();
01618   }
01619 
01620   return skip_order ? order->GetConditionSkipToOrder() : (VehicleOrderID)INVALID_VEH_ORDER_ID;
01621 }
01622 
01628 bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth)
01629 {
01630   if (conditional_depth > v->GetNumOrders()) return false;
01631 
01632   switch (order->GetType()) {
01633     case OT_GOTO_STATION:
01634       v->dest_tile = v->GetOrderStationLocation(order->GetDestination());
01635       return true;
01636 
01637     case OT_GOTO_DEPOT:
01638       if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) {
01639         /* We need to search for the nearest depot (hangar). */
01640         TileIndex location;
01641         DestinationID destination;
01642         bool reverse;
01643 
01644         if (v->FindClosestDepot(&location, &destination, &reverse)) {
01645           v->dest_tile = location;
01646           v->current_order.MakeGoToDepot(destination, v->current_order.GetDepotOrderType(), v->current_order.GetNonStopType(), (OrderDepotActionFlags)(v->current_order.GetDepotActionType() & ~ODATFB_NEAREST_DEPOT), v->current_order.GetRefitCargo(), v->current_order.GetRefitSubtype());
01647 
01648           /* If there is no depot in front, reverse automatically (trains only) */
01649           if (v->type == VEH_TRAIN && reverse) DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01650 
01651           if (v->type == VEH_AIRCRAFT && v->u.air.state == FLYING && v->u.air.targetairport != destination) {
01652             /* The aircraft is now heading for a different hangar than the next in the orders */
01653             extern void AircraftNextAirportPos_and_Order(Vehicle *v);
01654             AircraftNextAirportPos_and_Order(v);
01655           }
01656           return true;
01657         }
01658 
01659         UpdateVehicleTimetable(v, true);
01660         v->cur_order_index++;
01661       } else if ((order->GetDepotOrderType() & ODTFB_SERVICE) && !v->NeedsServicing()) {
01662         UpdateVehicleTimetable(v, true);
01663         v->cur_order_index++;
01664       } else {
01665         if (v->type != VEH_AIRCRAFT) {
01666           v->dest_tile = GetDepot(order->GetDestination())->xy;
01667         }
01668         return true;
01669       }
01670       break;
01671 
01672     case OT_GOTO_WAYPOINT:
01673       v->dest_tile = GetWaypoint(order->GetDestination())->xy;
01674       return true;
01675 
01676     case OT_CONDITIONAL: {
01677       VehicleOrderID next_order = ProcessConditionalOrder(order, v);
01678       if (next_order != INVALID_VEH_ORDER_ID) {
01679         UpdateVehicleTimetable(v, false);
01680         v->cur_order_index = next_order;
01681         v->current_order_time += GetVehicleOrder(v, next_order)->travel_time;
01682       } else {
01683         UpdateVehicleTimetable(v, true);
01684         v->cur_order_index++;
01685       }
01686       break;
01687     }
01688 
01689     default:
01690       v->dest_tile = 0;
01691       return false;
01692   }
01693 
01694   /* Get the current order */
01695   if (v->cur_order_index >= v->GetNumOrders()) v->cur_order_index = 0;
01696 
01697   order = GetVehicleOrder(v, v->cur_order_index);
01698   v->current_order = *order;
01699   return UpdateOrderDest(v, order, conditional_depth + 1);
01700 }
01701 
01709 bool ProcessOrders(Vehicle *v)
01710 {
01711   switch (v->current_order.GetType()) {
01712     case OT_GOTO_DEPOT:
01713       /* Let a depot order in the orderlist interrupt. */
01714       if (!(v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) return false;
01715       break;
01716 
01717     case OT_LOADING:
01718       return false;
01719 
01720     case OT_LEAVESTATION:
01721       if (v->type != VEH_AIRCRAFT) return false;
01722       break;
01723 
01724     default: break;
01725   }
01726 
01734   bool may_reverse = v->current_order.IsType(OT_NOTHING);
01735 
01736   /* Check if we've reached the waypoint? */
01737   if (v->current_order.IsType(OT_GOTO_WAYPOINT) && v->tile == v->dest_tile) {
01738     UpdateVehicleTimetable(v, true);
01739     v->cur_order_index++;
01740   }
01741 
01742   /* Check if we've reached a non-stop station.. */
01743   if (v->current_order.IsType(OT_GOTO_STATION) && (v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) &&
01744       IsTileType(v->tile, MP_STATION) &&
01745       v->current_order.GetDestination() == GetStationIndex(v->tile)) {
01746     v->last_station_visited = v->current_order.GetDestination();
01747     UpdateVehicleTimetable(v, true);
01748     v->cur_order_index++;
01749   }
01750 
01751   /* Get the current order */
01752   if (v->cur_order_index >= v->GetNumOrders()) v->cur_order_index = 0;
01753 
01754   const Order *order = GetVehicleOrder(v, v->cur_order_index);
01755 
01756   /* If no order, do nothing. */
01757   if (order == NULL || (v->type == VEH_AIRCRAFT && order->IsType(OT_DUMMY) && !CheckForValidOrders(v))) {
01758     if (v->type == VEH_AIRCRAFT) {
01759       /* Aircraft do something vastly different here, so handle separately */
01760       extern void HandleMissingAircraftOrders(Vehicle *v);
01761       HandleMissingAircraftOrders(v);
01762       return false;
01763     }
01764 
01765     v->current_order.Free();
01766     v->dest_tile = 0;
01767     if (v->type == VEH_ROAD) ClearSlot(v);
01768     return false;
01769   }
01770 
01771   /* If it is unchanged, keep it. */
01772   if (order->Equals(v->current_order) && (v->type == VEH_AIRCRAFT || v->dest_tile != 0) &&
01773       (v->type != VEH_SHIP || !order->IsType(OT_GOTO_STATION) || GetStation(order->GetDestination())->dock_tile != INVALID_TILE)) {
01774     return false;
01775   }
01776 
01777   /* Otherwise set it, and determine the destination tile. */
01778   v->current_order = *order;
01779 
01780   InvalidateVehicleOrder(v, 0);
01781   switch (v->type) {
01782     default:
01783       NOT_REACHED();
01784 
01785     case VEH_ROAD:
01786     case VEH_TRAIN:
01787       break;
01788 
01789     case VEH_AIRCRAFT:
01790     case VEH_SHIP:
01791       InvalidateWindowClasses(GetWindowClassForVehicleType(v->type));
01792       break;
01793   }
01794 
01795   return UpdateOrderDest(v, order) && may_reverse;
01796 }
01797 
01805 bool Order::ShouldStopAtStation(const Vehicle *v, StationID station) const
01806 {
01807   bool is_dest_station = this->IsType(OT_GOTO_STATION) && this->dest == station;
01808   return
01809       (!this->IsType(OT_GOTO_DEPOT) || (this->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0) &&
01810       v->last_station_visited != station && // Do stop only when we've not just been there
01811       /* Finally do stop when there is no non-stop flag set for this type of station. */
01812       !(this->GetNonStopType() & (is_dest_station ? ONSF_NO_STOP_AT_DESTINATION_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS));
01813 }
01814 
01815 void InitializeOrders()
01816 {
01817   _Order_pool.CleanPool();
01818   _Order_pool.AddBlockToPool();
01819 
01820   _OrderList_pool.CleanPool();
01821   _OrderList_pool.AddBlockToPool();
01822 
01823   _backup_orders_tile = 0;
01824 }

Generated on Tue Jul 21 18:48:25 2009 for OpenTTD by  doxygen 1.5.6