order_cmd.cpp

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

Generated on Mon May 11 15:48:05 2009 for OpenTTD by  doxygen 1.5.6