order_cmd.cpp

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

Generated on Wed Apr 1 14:38:08 2009 for OpenTTD by  doxygen 1.5.6