order_cmd.cpp

Go to the documentation of this file.
00001 /* $Id: order_cmd.cpp 21603 2010-12-22 21:19:39Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "debug.h"
00014 #include "cmd_helper.h"
00015 #include "command_func.h"
00016 #include "company_func.h"
00017 #include "news_func.h"
00018 #include "vehicle_gui.h"
00019 #include "strings_func.h"
00020 #include "functions.h"
00021 #include "window_func.h"
00022 #include "timetable.h"
00023 #include "vehicle_func.h"
00024 #include "depot_base.h"
00025 #include "core/pool_func.hpp"
00026 #include "aircraft.h"
00027 #include "roadveh.h"
00028 #include "station_base.h"
00029 #include "waypoint_base.h"
00030 #include "company_base.h"
00031 
00032 #include "table/strings.h"
00033 
00034 /* DestinationID must be at least as large as every these below, because it can
00035  * be any of them
00036  */
00037 assert_compile(sizeof(DestinationID) >= sizeof(DepotID));
00038 assert_compile(sizeof(DestinationID) >= sizeof(StationID));
00039 
00040 OrderPool _order_pool("Order");
00041 INSTANTIATE_POOL_METHODS(Order)
00042 OrderListPool _orderlist_pool("OrderList");
00043 INSTANTIATE_POOL_METHODS(OrderList)
00044 
00045 void Order::Free()
00046 {
00047   this->type  = OT_NOTHING;
00048   this->flags = 0;
00049   this->dest  = 0;
00050   this->next  = NULL;
00051 }
00052 
00053 void Order::MakeGoToStation(StationID destination)
00054 {
00055   this->type = OT_GOTO_STATION;
00056   this->flags = 0;
00057   this->dest = destination;
00058 }
00059 
00060 void Order::MakeGoToDepot(DepotID destination, OrderDepotTypeFlags order, OrderNonStopFlags non_stop_type, OrderDepotActionFlags action, CargoID cargo, byte subtype)
00061 {
00062   this->type = OT_GOTO_DEPOT;
00063   this->SetDepotOrderType(order);
00064   this->SetDepotActionType(action);
00065   this->SetNonStopType(non_stop_type);
00066   this->dest = destination;
00067   this->SetRefit(cargo, subtype);
00068 }
00069 
00070 void Order::MakeGoToWaypoint(StationID destination)
00071 {
00072   this->type = OT_GOTO_WAYPOINT;
00073   this->flags = 0;
00074   this->dest = destination;
00075 }
00076 
00077 void Order::MakeLoading(bool ordered)
00078 {
00079   this->type = OT_LOADING;
00080   if (!ordered) this->flags = 0;
00081 }
00082 
00083 void Order::MakeLeaveStation()
00084 {
00085   this->type = OT_LEAVESTATION;
00086   this->flags = 0;
00087 }
00088 
00089 void Order::MakeDummy()
00090 {
00091   this->type = OT_DUMMY;
00092   this->flags = 0;
00093 }
00094 
00095 void Order::MakeConditional(VehicleOrderID order)
00096 {
00097   this->type = OT_CONDITIONAL;
00098   this->flags = order;
00099   this->dest = 0;
00100 }
00101 
00102 void Order::SetRefit(CargoID cargo, byte subtype)
00103 {
00104   this->refit_cargo = cargo;
00105   this->refit_subtype = subtype;
00106 }
00107 
00108 bool Order::Equals(const Order &other) const
00109 {
00110   /* In case of go to nearest depot orders we need "only" compare the flags
00111    * with the other and not the nearest depot order bit or the actual
00112    * destination because those get clear/filled in during the order
00113    * evaluation. If we do not do this the order will continuously be seen as
00114    * a different order and it will try to find a "nearest depot" every tick. */
00115   if ((this->IsType(OT_GOTO_DEPOT) && this->type == other.type) &&
00116       ((this->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0 ||
00117        (other.GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0)) {
00118     return this->GetDepotOrderType() == other.GetDepotOrderType() &&
00119         (this->GetDepotActionType() & ~ODATFB_NEAREST_DEPOT) == (other.GetDepotActionType() & ~ODATFB_NEAREST_DEPOT);
00120   }
00121 
00122   return this->type == other.type && this->flags == other.flags && 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 uint16 Order::MapOldOrder() const
00131 {
00132   uint16 order = this->GetType();
00133   switch (this->type) {
00134     case OT_GOTO_STATION:
00135       if (this->GetUnloadType() & OUFB_UNLOAD) SetBit(order, 5);
00136       if (this->GetLoadType() & OLFB_FULL_LOAD) SetBit(order, 6);
00137       if (this->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) SetBit(order, 7);
00138       order |= GB(this->GetDestination(), 0, 8) << 8;
00139       break;
00140     case OT_GOTO_DEPOT:
00141       if (!(this->GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) SetBit(order, 6);
00142       SetBit(order, 7);
00143       order |= GB(this->GetDestination(), 0, 8) << 8;
00144       break;
00145     case OT_LOADING:
00146       if (this->GetLoadType() & OLFB_FULL_LOAD) SetBit(order, 6);
00147       break;
00148   }
00149   return order;
00150 }
00151 
00152 Order::Order(uint32 packed)
00153 {
00154   this->type    = (OrderType)GB(packed,  0,  8);
00155   this->flags   = GB(packed,  8,  8);
00156   this->dest    = GB(packed, 16, 16);
00157   this->next    = NULL;
00158   this->refit_cargo   = CT_NO_REFIT;
00159   this->refit_subtype = 0;
00160   this->wait_time     = 0;
00161   this->travel_time   = 0;
00162 }
00163 
00169 void InvalidateVehicleOrder(const Vehicle *v, int data)
00170 {
00171   SetWindowDirty(WC_VEHICLE_VIEW, v->index);
00172 
00173   if (data != 0) {
00174     /* Calls SetDirty() too */
00175     InvalidateWindowData(WC_VEHICLE_ORDERS,    v->index, data);
00176     InvalidateWindowData(WC_VEHICLE_TIMETABLE, v->index, data);
00177     return;
00178   }
00179 
00180   SetWindowDirty(WC_VEHICLE_ORDERS,    v->index);
00181   SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
00182 }
00183 
00190 void Order::AssignOrder(const Order &other)
00191 {
00192   this->type  = other.type;
00193   this->flags = other.flags;
00194   this->dest  = other.dest;
00195 
00196   this->refit_cargo   = other.refit_cargo;
00197   this->refit_subtype = other.refit_subtype;
00198 
00199   this->wait_time   = other.wait_time;
00200   this->travel_time = other.travel_time;
00201 }
00202 
00203 void OrderList::Initialize(Order *chain, Vehicle *v)
00204 {
00205   this->first = chain;
00206   this->first_shared = v;
00207 
00208   this->num_orders = 0;
00209   this->num_vehicles = 1;
00210   this->timetable_duration = 0;
00211 
00212   for (Order *o = this->first; o != NULL; o = o->next) {
00213     ++this->num_orders;
00214     this->timetable_duration += o->wait_time + o->travel_time;
00215   }
00216 
00217   for (Vehicle *u = this->first_shared->PreviousShared(); u != NULL; u = u->PreviousShared()) {
00218     ++this->num_vehicles;
00219     this->first_shared = u;
00220   }
00221 
00222   for (const Vehicle *u = v->NextShared(); u != NULL; u = u->NextShared()) ++this->num_vehicles;
00223 }
00224 
00225 void OrderList::FreeChain(bool keep_orderlist)
00226 {
00227   Order *next;
00228   for (Order *o = this->first; o != NULL; o = next) {
00229     next = o->next;
00230     delete o;
00231   }
00232 
00233   if (keep_orderlist) {
00234     this->first = NULL;
00235     this->num_orders = 0;
00236     this->timetable_duration = 0;
00237   } else {
00238     delete this;
00239   }
00240 }
00241 
00242 Order *OrderList::GetOrderAt(int index) const
00243 {
00244   if (index < 0) return NULL;
00245 
00246   Order *order = this->first;
00247 
00248   while (order != NULL && index-- > 0) {
00249     order = order->next;
00250   }
00251   return order;
00252 }
00253 
00254 void OrderList::InsertOrderAt(Order *new_order, int index)
00255 {
00256   if (this->first == NULL) {
00257     this->first = new_order;
00258   } else {
00259     if (index == 0) {
00260       /* Insert as first or only order */
00261       new_order->next = this->first;
00262       this->first = new_order;
00263     } else if (index >= this->num_orders) {
00264       /* index is after the last order, add it to the end */
00265       this->GetLastOrder()->next = new_order;
00266     } else {
00267       /* Put the new order in between */
00268       Order *order = this->GetOrderAt(index - 1);
00269       new_order->next = order->next;
00270       order->next = new_order;
00271     }
00272   }
00273   ++this->num_orders;
00274   this->timetable_duration += new_order->wait_time + new_order->travel_time;
00275 }
00276 
00277 
00278 void OrderList::DeleteOrderAt(int index)
00279 {
00280   if (index >= this->num_orders) return;
00281 
00282   Order *to_remove;
00283 
00284   if (index == 0) {
00285     to_remove = this->first;
00286     this->first = to_remove->next;
00287   } else {
00288     Order *prev = GetOrderAt(index - 1);
00289     to_remove = prev->next;
00290     prev->next = to_remove->next;
00291   }
00292   --this->num_orders;
00293   this->timetable_duration -= (to_remove->wait_time + to_remove->travel_time);
00294   delete to_remove;
00295 }
00296 
00297 void OrderList::MoveOrder(int from, int to)
00298 {
00299   if (from >= this->num_orders || to >= this->num_orders || from == to) return;
00300 
00301   Order *moving_one;
00302 
00303   /* Take the moving order out of the pointer-chain */
00304   if (from == 0) {
00305     moving_one = this->first;
00306     this->first = moving_one->next;
00307   } else {
00308     Order *one_before = GetOrderAt(from - 1);
00309     moving_one = one_before->next;
00310     one_before->next = moving_one->next;
00311   }
00312 
00313   /* Insert the moving_order again in the pointer-chain */
00314   if (to == 0) {
00315     moving_one->next = this->first;
00316     this->first = moving_one;
00317   } else {
00318     Order *one_before = GetOrderAt(to - 1);
00319     moving_one->next = one_before->next;
00320     one_before->next = moving_one;
00321   }
00322 }
00323 
00324 void OrderList::RemoveVehicle(Vehicle *v)
00325 {
00326   --this->num_vehicles;
00327   if (v == this->first_shared) this->first_shared = v->NextShared();
00328 }
00329 
00330 bool OrderList::IsVehicleInSharedOrdersList(const Vehicle *v) const
00331 {
00332   for (const Vehicle *v_shared = this->first_shared; v_shared != NULL; v_shared = v_shared->NextShared()) {
00333     if (v_shared == v) return true;
00334   }
00335 
00336   return false;
00337 }
00338 
00339 int OrderList::GetPositionInSharedOrderList(const Vehicle *v) const
00340 {
00341   int count = 0;
00342   for (const Vehicle *v_shared = v->PreviousShared(); v_shared != NULL; v_shared = v_shared->PreviousShared()) count++;
00343   return count;
00344 }
00345 
00346 bool OrderList::IsCompleteTimetable() const
00347 {
00348   for (Order *o = this->first; o != NULL; o = o->next) {
00349     if (!o->IsCompletelyTimetabled()) return false;
00350   }
00351   return true;
00352 }
00353 
00354 void OrderList::DebugCheckSanity() const
00355 {
00356   VehicleOrderID check_num_orders = 0;
00357   uint check_num_vehicles = 0;
00358   Ticks check_timetable_duration = 0;
00359 
00360   DEBUG(misc, 6, "Checking OrderList %hu for sanity...", this->index);
00361 
00362   for (const Order *o = this->first; o != NULL; o = o->next) {
00363     ++check_num_orders;
00364     check_timetable_duration += o->wait_time + o->travel_time;
00365   }
00366   assert(this->num_orders == check_num_orders);
00367   assert(this->timetable_duration == check_timetable_duration);
00368 
00369   for (const Vehicle *v = this->first_shared; v != NULL; v = v->NextShared()) {
00370     ++check_num_vehicles;
00371     assert(v->orders.list == this);
00372   }
00373   assert(this->num_vehicles == check_num_vehicles);
00374   DEBUG(misc, 6, "... detected %u orders, %u vehicles, %i ticks", (uint)this->num_orders,
00375         this->num_vehicles, this->timetable_duration);
00376 }
00377 
00385 static inline bool OrderGoesToStation(const Vehicle *v, const Order *o)
00386 {
00387   return o->IsType(OT_GOTO_STATION) ||
00388       (v->type == VEH_AIRCRAFT && o->IsType(OT_GOTO_DEPOT) && !(o->GetDepotActionType() & ODATFB_NEAREST_DEPOT));
00389 }
00390 
00397 static void DeleteOrderWarnings(const Vehicle *v)
00398 {
00399   DeleteVehicleNews(v->index, STR_NEWS_VEHICLE_HAS_TOO_FEW_ORDERS);
00400   DeleteVehicleNews(v->index, STR_NEWS_VEHICLE_HAS_VOID_ORDER);
00401   DeleteVehicleNews(v->index, STR_NEWS_VEHICLE_HAS_DUPLICATE_ENTRY);
00402   DeleteVehicleNews(v->index, STR_NEWS_VEHICLE_HAS_INVALID_ENTRY);
00403 }
00404 
00410 TileIndex Order::GetLocation(const Vehicle *v) const
00411 {
00412   switch (this->GetType()) {
00413     case OT_GOTO_WAYPOINT:
00414     case OT_GOTO_STATION:
00415       return BaseStation::Get(this->GetDestination())->xy;
00416 
00417     case OT_GOTO_DEPOT:
00418       if ((this->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) return INVALID_TILE;
00419       return (v->type == VEH_AIRCRAFT) ? Station::Get(this->GetDestination())->xy : Depot::Get(this->GetDestination())->xy;
00420 
00421     default:
00422       return INVALID_TILE;
00423   }
00424 }
00425 
00426 static uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int conditional_depth = 0)
00427 {
00428   assert(v->type == VEH_SHIP);
00429 
00430   if (cur->IsType(OT_CONDITIONAL)) {
00431     if (conditional_depth > v->GetNumOrders()) return 0;
00432 
00433     conditional_depth++;
00434 
00435     int dist1 = GetOrderDistance(prev, v->GetOrder(cur->GetConditionSkipToOrder()), v, conditional_depth);
00436     int dist2 = GetOrderDistance(prev, cur->next == NULL ? v->orders.list->GetFirstOrder() : cur->next, v, conditional_depth);
00437     return max(dist1, dist2);
00438   }
00439 
00440   TileIndex prev_tile = prev->GetLocation(v);
00441   TileIndex cur_tile = cur->GetLocation(v);
00442   if (prev_tile == INVALID_TILE || cur_tile == INVALID_TILE) return 0;
00443   return DistanceManhattan(prev_tile, cur_tile);
00444 }
00445 
00459 CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00460 {
00461   VehicleID veh          = GB(p1,  0, 20);
00462   VehicleOrderID sel_ord = GB(p1, 20, 8);
00463   Order new_order(p2);
00464 
00465   Vehicle *v = Vehicle::GetIfValid(veh);
00466   if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
00467 
00468   CommandCost ret = CheckOwnership(v->owner);
00469   if (ret.Failed()) return ret;
00470 
00471   /* Check if the inserted order is to the correct destination (owner, type),
00472    * and has the correct flags if any */
00473   switch (new_order.GetType()) {
00474     case OT_GOTO_STATION: {
00475       const Station *st = Station::GetIfValid(new_order.GetDestination());
00476       if (st == NULL) return CMD_ERROR;
00477 
00478       if (st->owner != OWNER_NONE) {
00479         CommandCost ret = CheckOwnership(st->owner);
00480         if (ret.Failed()) return ret;
00481       }
00482 
00483       if (!CanVehicleUseStation(v, st)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
00484       for (Vehicle *u = v->FirstShared(); u != NULL; u = u->NextShared()) {
00485         if (!CanVehicleUseStation(u, st)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER_SHARED);
00486       }
00487 
00488       /* Non stop only allowed for ground vehicles. */
00489       if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && !v->IsGroundVehicle()) return CMD_ERROR;
00490 
00491       /* No load and no unload are mutual exclusive. */
00492       if ((new_order.GetLoadType() & OLFB_NO_LOAD) && (new_order.GetUnloadType() & OUFB_NO_UNLOAD)) return CMD_ERROR;
00493 
00494       /* Filter invalid load/unload types. */
00495       switch (new_order.GetLoadType()) {
00496         case OLF_LOAD_IF_POSSIBLE: case OLFB_FULL_LOAD: case OLF_FULL_LOAD_ANY: case OLFB_NO_LOAD: break;
00497         default: return CMD_ERROR;
00498       }
00499       switch (new_order.GetUnloadType()) {
00500         case OUF_UNLOAD_IF_POSSIBLE: case OUFB_UNLOAD: case OUFB_TRANSFER: case OUFB_NO_UNLOAD: break;
00501         default: return CMD_ERROR;
00502       }
00503 
00504       /* Filter invalid stop locations */
00505       switch (new_order.GetStopLocation()) {
00506         case OSL_PLATFORM_NEAR_END:
00507         case OSL_PLATFORM_MIDDLE:
00508           if (v->type != VEH_TRAIN) return CMD_ERROR;
00509           /* FALL THROUGH */
00510         case OSL_PLATFORM_FAR_END:
00511           break;
00512 
00513         default:
00514           return CMD_ERROR;
00515       }
00516 
00517       break;
00518     }
00519 
00520     case OT_GOTO_DEPOT: {
00521       if ((new_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0) {
00522         if (v->type == VEH_AIRCRAFT) {
00523           const Station *st = Station::GetIfValid(new_order.GetDestination());
00524 
00525           if (st == NULL) return CMD_ERROR;
00526 
00527           CommandCost ret = CheckOwnership(st->owner);
00528           if (ret.Failed()) return ret;
00529 
00530           if (!CanVehicleUseStation(v, st) || !st->airport.HasHangar()) {
00531             return CMD_ERROR;
00532           }
00533         } else {
00534           const Depot *dp = Depot::GetIfValid(new_order.GetDestination());
00535 
00536           if (dp == NULL) return CMD_ERROR;
00537 
00538           CommandCost ret = CheckOwnership(GetTileOwner(dp->xy));
00539           if (ret.Failed()) return ret;
00540 
00541           switch (v->type) {
00542             case VEH_TRAIN:
00543               if (!IsRailDepotTile(dp->xy)) return CMD_ERROR;
00544               break;
00545 
00546             case VEH_ROAD:
00547               if (!IsRoadDepotTile(dp->xy)) return CMD_ERROR;
00548               break;
00549 
00550             case VEH_SHIP:
00551               if (!IsShipDepotTile(dp->xy)) return CMD_ERROR;
00552               break;
00553 
00554             default: return CMD_ERROR;
00555           }
00556         }
00557       }
00558 
00559       if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && !v->IsGroundVehicle()) return CMD_ERROR;
00560       if (new_order.GetDepotOrderType() & ~(ODTFB_PART_OF_ORDERS | ((new_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0 ? ODTFB_SERVICE : 0))) return CMD_ERROR;
00561       if (new_order.GetDepotActionType() & ~(ODATFB_HALT | ODATFB_NEAREST_DEPOT)) return CMD_ERROR;
00562       if ((new_order.GetDepotOrderType() & ODTFB_SERVICE) && (new_order.GetDepotActionType() & ODATFB_HALT)) return CMD_ERROR;
00563       break;
00564     }
00565 
00566     case OT_GOTO_WAYPOINT: {
00567       const Waypoint *wp = Waypoint::GetIfValid(new_order.GetDestination());
00568       if (wp == NULL) return CMD_ERROR;
00569 
00570       switch (v->type) {
00571         default: return CMD_ERROR;
00572 
00573         case VEH_TRAIN: {
00574           if (!(wp->facilities & FACIL_TRAIN)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
00575 
00576           CommandCost ret = CheckOwnership(wp->owner);
00577           if (ret.Failed()) return ret;
00578           break;
00579         }
00580 
00581         case VEH_SHIP:
00582           if (!(wp->facilities & FACIL_DOCK)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
00583           if (wp->owner != OWNER_NONE) {
00584             CommandCost ret = CheckOwnership(wp->owner);
00585             if (ret.Failed()) return ret;
00586           }
00587           break;
00588       }
00589 
00590       /* Order flags can be any of the following for waypoints:
00591        * [non-stop]
00592        * non-stop orders (if any) are only valid for trains */
00593       if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN) return CMD_ERROR;
00594       break;
00595     }
00596 
00597     case OT_CONDITIONAL: {
00598       VehicleOrderID skip_to = new_order.GetConditionSkipToOrder();
00599       if (skip_to != 0 && skip_to >= v->GetNumOrders()) return CMD_ERROR; // Always allow jumping to the first (even when there is no order).
00600       if (new_order.GetConditionVariable() > OCV_END) return CMD_ERROR;
00601 
00602       OrderConditionComparator occ = new_order.GetConditionComparator();
00603       if (occ > OCC_END) return CMD_ERROR;
00604       switch (new_order.GetConditionVariable()) {
00605         case OCV_REQUIRES_SERVICE:
00606           if (occ != OCC_IS_TRUE && occ != OCC_IS_FALSE) return CMD_ERROR;
00607           break;
00608 
00609         case OCV_UNCONDITIONALLY:
00610           if (occ != OCC_EQUALS) return CMD_ERROR;
00611           if (new_order.GetConditionValue() != 0) return CMD_ERROR;
00612           break;
00613 
00614         case OCV_LOAD_PERCENTAGE:
00615         case OCV_RELIABILITY:
00616           if (new_order.GetConditionValue() > 100) return CMD_ERROR;
00617           /* FALL THROUGH */
00618         default:
00619           if (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) return CMD_ERROR;
00620           break;
00621       }
00622       break;
00623     }
00624 
00625     default: return CMD_ERROR;
00626   }
00627 
00628   if (sel_ord > v->GetNumOrders()) return CMD_ERROR;
00629 
00630   if (v->GetNumOrders() >= MAX_VEH_ORDER_ID) return_cmd_error(STR_ERROR_TOO_MANY_ORDERS);
00631   if (!Order::CanAllocateItem()) return_cmd_error(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
00632   if (v->orders.list == NULL && !OrderList::CanAllocateItem()) return_cmd_error(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
00633 
00634   if (v->type == VEH_SHIP && _settings_game.pf.pathfinder_for_ships != VPF_NPF) {
00635     /* Make sure the new destination is not too far away from the previous */
00636     const Order *prev = NULL;
00637     uint n = 0;
00638 
00639     /* Find the last goto station or depot order before the insert location.
00640      * If the order is to be inserted at the beginning of the order list this
00641      * finds the last order in the list. */
00642     const Order *o;
00643     FOR_VEHICLE_ORDERS(v, o) {
00644       switch (o->GetType()) {
00645         case OT_GOTO_STATION:
00646         case OT_GOTO_DEPOT:
00647         case OT_GOTO_WAYPOINT:
00648           prev = o;
00649           break;
00650 
00651         default: break;
00652       }
00653       if (++n == sel_ord && prev != NULL) break;
00654     }
00655     if (prev != NULL) {
00656       uint dist = GetOrderDistance(prev, &new_order, v);
00657       if (dist >= 130) {
00658         return_cmd_error(STR_ERROR_TOO_FAR_FROM_PREVIOUS_DESTINATION);
00659       }
00660     }
00661   }
00662 
00663   if (flags & DC_EXEC) {
00664     Order *new_o = new Order();
00665     new_o->AssignOrder(new_order);
00666     InsertOrder(v, new_o, sel_ord);
00667   }
00668 
00669   return CommandCost();
00670 }
00671 
00678 void InsertOrder(Vehicle *v, Order *new_o, VehicleOrderID sel_ord)
00679 {
00680   /* Create new order and link in list */
00681   if (v->orders.list == NULL) {
00682     v->orders.list = new OrderList(new_o, v);
00683   } else {
00684     v->orders.list->InsertOrderAt(new_o, sel_ord);
00685   }
00686 
00687   Vehicle *u = v->FirstShared();
00688   DeleteOrderWarnings(u);
00689   for (; u != NULL; u = u->NextShared()) {
00690     assert(v->orders.list == u->orders.list);
00691 
00692     /* If there is added an order before the current one, we need
00693      * to update the selected order */
00694     if (sel_ord <= u->cur_order_index) {
00695       uint cur = u->cur_order_index + 1;
00696       /* Check if we don't go out of bound */
00697       if (cur < u->GetNumOrders()) {
00698         u->cur_order_index = cur;
00699       }
00700     }
00701     /* Update any possible open window of the vehicle */
00702     InvalidateVehicleOrder(u, INVALID_VEH_ORDER_ID | (sel_ord << 8));
00703   }
00704 
00705   /* As we insert an order, the order to skip to will be 'wrong'. */
00706   VehicleOrderID cur_order_id = 0;
00707   Order *order;
00708   FOR_VEHICLE_ORDERS(v, order) {
00709     if (order->IsType(OT_CONDITIONAL)) {
00710       VehicleOrderID order_id = order->GetConditionSkipToOrder();
00711       if (order_id >= sel_ord) {
00712         order->SetConditionSkipToOrder(order_id + 1);
00713       }
00714       if (order_id == cur_order_id) {
00715         order->SetConditionSkipToOrder((order_id + 1) % v->GetNumOrders());
00716       }
00717     }
00718     cur_order_id++;
00719   }
00720 
00721   /* Make sure to rebuild the whole list */
00722   InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
00723 }
00724 
00730 static CommandCost DecloneOrder(Vehicle *dst, DoCommandFlag flags)
00731 {
00732   if (flags & DC_EXEC) {
00733     DeleteVehicleOrders(dst);
00734     InvalidateVehicleOrder(dst, -1);
00735     InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
00736   }
00737   return CommandCost();
00738 }
00739 
00749 CommandCost CmdDeleteOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00750 {
00751   VehicleID veh_id = GB(p1, 0, 20);
00752   VehicleOrderID sel_ord = GB(p2, 0, 8);
00753 
00754   Vehicle *v = Vehicle::GetIfValid(veh_id);
00755 
00756   if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
00757 
00758   CommandCost ret = CheckOwnership(v->owner);
00759   if (ret.Failed()) return ret;
00760 
00761   /* If we did not select an order, we maybe want to de-clone the orders */
00762   if (sel_ord >= v->GetNumOrders()) return DecloneOrder(v, flags);
00763 
00764   if (v->GetOrder(sel_ord) == NULL) return CMD_ERROR;
00765 
00766   if (flags & DC_EXEC) DeleteOrder(v, sel_ord);
00767   return CommandCost();
00768 }
00769 
00775 void DeleteOrder(Vehicle *v, VehicleOrderID sel_ord)
00776 {
00777   v->orders.list->DeleteOrderAt(sel_ord);
00778 
00779   Vehicle *u = v->FirstShared();
00780   DeleteOrderWarnings(u);
00781   for (; u != NULL; u = u->NextShared()) {
00782     if (sel_ord < u->cur_order_index) u->cur_order_index--;
00783 
00784     assert(v->orders.list == u->orders.list);
00785 
00786     /* NON-stop flag is misused to see if a train is in a station that is
00787      * on his order list or not */
00788     if (sel_ord == u->cur_order_index && u->current_order.IsType(OT_LOADING)) {
00789       u->current_order.SetNonStopType(ONSF_STOP_EVERYWHERE);
00790       /* When full loading, "cancel" that order so the vehicle doesn't
00791        * stay indefinitely at this station anymore. */
00792       if (u->current_order.GetLoadType() & OLFB_FULL_LOAD) u->current_order.SetLoadType(OLF_LOAD_IF_POSSIBLE);
00793     }
00794 
00795     /* Update any possible open window of the vehicle */
00796     InvalidateVehicleOrder(u, sel_ord | (INVALID_VEH_ORDER_ID << 8));
00797   }
00798 
00799   /* As we delete an order, the order to skip to will be 'wrong'. */
00800   VehicleOrderID cur_order_id = 0;
00801   Order *order = NULL;
00802   FOR_VEHICLE_ORDERS(v, order) {
00803     if (order->IsType(OT_CONDITIONAL)) {
00804       VehicleOrderID order_id = order->GetConditionSkipToOrder();
00805       if (order_id >= sel_ord) {
00806         order->SetConditionSkipToOrder(max(order_id - 1, 0));
00807       }
00808       if (order_id == cur_order_id) {
00809         order->SetConditionSkipToOrder((order_id + 1) % v->GetNumOrders());
00810       }
00811     }
00812     cur_order_id++;
00813   }
00814 
00815   InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
00816 }
00817 
00827 CommandCost CmdSkipToOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00828 {
00829   VehicleID veh_id = GB(p1, 0, 20);
00830   VehicleOrderID sel_ord = GB(p2, 0, 8);
00831 
00832   Vehicle *v = Vehicle::GetIfValid(veh_id);
00833 
00834   if (v == NULL || !v->IsPrimaryVehicle() || sel_ord == v->cur_order_index || sel_ord >= v->GetNumOrders() || v->GetNumOrders() < 2) return CMD_ERROR;
00835 
00836   CommandCost ret = CheckOwnership(v->owner);
00837   if (ret.Failed()) return ret;
00838 
00839   if (flags & DC_EXEC) {
00840     v->cur_order_index = sel_ord;
00841 
00842     if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
00843 
00844     InvalidateVehicleOrder(v, -2);
00845   }
00846 
00847   /* We have an aircraft/ship, they have a mini-schedule, so update them all */
00848   if (v->type == VEH_AIRCRAFT) SetWindowClassesDirty(WC_AIRCRAFT_LIST);
00849   if (v->type == VEH_SHIP) SetWindowClassesDirty(WC_SHIPS_LIST);
00850 
00851   return CommandCost();
00852 }
00853 
00867 CommandCost CmdMoveOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00868 {
00869   VehicleID veh = GB(p1, 0, 20);
00870   VehicleOrderID moving_order = GB(p2,  0, 16);
00871   VehicleOrderID target_order = GB(p2, 16, 16);
00872 
00873   Vehicle *v = Vehicle::GetIfValid(veh);
00874   if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
00875 
00876   CommandCost ret = CheckOwnership(v->owner);
00877   if (ret.Failed()) return ret;
00878 
00879   /* Don't make senseless movements */
00880   if (moving_order >= v->GetNumOrders() || target_order >= v->GetNumOrders() ||
00881       moving_order == target_order || v->GetNumOrders() <= 1) return CMD_ERROR;
00882 
00883   Order *moving_one = v->GetOrder(moving_order);
00884   /* Don't move an empty order */
00885   if (moving_one == NULL) return CMD_ERROR;
00886 
00887   if (flags & DC_EXEC) {
00888     v->orders.list->MoveOrder(moving_order, target_order);
00889 
00890     /* Update shared list */
00891     Vehicle *u = v->FirstShared();
00892 
00893     DeleteOrderWarnings(u);
00894 
00895     for (; u != NULL; u = u->NextShared()) {
00896       /* Update the current order */
00897       if (u->cur_order_index == moving_order) {
00898         u->cur_order_index = target_order;
00899       } else if (u->cur_order_index > moving_order && u->cur_order_index <= target_order) {
00900         u->cur_order_index--;
00901       } else if (u->cur_order_index < moving_order && u->cur_order_index >= target_order) {
00902         u->cur_order_index++;
00903       }
00904 
00905       assert(v->orders.list == u->orders.list);
00906       /* Update any possible open window of the vehicle */
00907       InvalidateVehicleOrder(u, moving_order | (target_order << 8));
00908     }
00909 
00910     /* As we move an order, the order to skip to will be 'wrong'. */
00911     Order *order;
00912     FOR_VEHICLE_ORDERS(v, order) {
00913       if (order->IsType(OT_CONDITIONAL)) {
00914         VehicleOrderID order_id = order->GetConditionSkipToOrder();
00915         if (order_id == moving_order) {
00916           order_id = target_order;
00917         } else if (order_id > moving_order && order_id <= target_order) {
00918           order_id--;
00919         } else if (order_id < moving_order && order_id >= target_order) {
00920           order_id++;
00921         }
00922         order->SetConditionSkipToOrder(order_id);
00923       }
00924     }
00925 
00926     /* Make sure to rebuild the whole list */
00927     InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
00928   }
00929 
00930   return CommandCost();
00931 }
00932 
00948 CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00949 {
00950   VehicleOrderID sel_ord = GB(p1, 20,  8);
00951   VehicleID veh          = GB(p1,  0, 20);
00952   ModifyOrderFlags mof   = Extract<ModifyOrderFlags, 0, 4>(p2);
00953   uint16 data            = GB(p2,  4, 11);
00954 
00955   if (mof >= MOF_END) return CMD_ERROR;
00956 
00957   Vehicle *v = Vehicle::GetIfValid(veh);
00958   if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
00959 
00960   CommandCost ret = CheckOwnership(v->owner);
00961   if (ret.Failed()) return ret;
00962 
00963   /* Is it a valid order? */
00964   if (sel_ord >= v->GetNumOrders()) return CMD_ERROR;
00965 
00966   Order *order = v->GetOrder(sel_ord);
00967   switch (order->GetType()) {
00968     case OT_GOTO_STATION:
00969       if (mof == MOF_COND_VARIABLE || mof == MOF_COND_COMPARATOR || mof == MOF_DEPOT_ACTION || mof == MOF_COND_VALUE) return CMD_ERROR;
00970       break;
00971 
00972     case OT_GOTO_DEPOT:
00973       if (mof != MOF_NON_STOP && mof != MOF_DEPOT_ACTION) return CMD_ERROR;
00974       break;
00975 
00976     case OT_GOTO_WAYPOINT:
00977       if (mof != MOF_NON_STOP) return CMD_ERROR;
00978       break;
00979 
00980     case OT_CONDITIONAL:
00981       if (mof != MOF_COND_VARIABLE && mof != MOF_COND_COMPARATOR && mof != MOF_COND_VALUE && mof != MOF_COND_DESTINATION) return CMD_ERROR;
00982       break;
00983 
00984     default:
00985       return CMD_ERROR;
00986   }
00987 
00988   switch (mof) {
00989     default: NOT_REACHED();
00990 
00991     case MOF_NON_STOP:
00992       if (!v->IsGroundVehicle()) return CMD_ERROR;
00993       if (data >= ONSF_END) return CMD_ERROR;
00994       if (data == order->GetNonStopType()) return CMD_ERROR;
00995       break;
00996 
00997     case MOF_STOP_LOCATION:
00998       if (v->type != VEH_TRAIN) return CMD_ERROR;
00999       if (data >= OSL_END) return CMD_ERROR;
01000       break;
01001 
01002     case MOF_UNLOAD:
01003       if ((data & ~(OUFB_UNLOAD | OUFB_TRANSFER | OUFB_NO_UNLOAD)) != 0) return CMD_ERROR;
01004       /* Unload and no-unload are mutual exclusive and so are transfer and no unload. */
01005       if (data != 0 && ((data & (OUFB_UNLOAD | OUFB_TRANSFER)) != 0) == ((data & OUFB_NO_UNLOAD) != 0)) return CMD_ERROR;
01006       if (data == order->GetUnloadType()) return CMD_ERROR;
01007       break;
01008 
01009     case MOF_LOAD:
01010       if (data > OLFB_NO_LOAD || data == 1) return CMD_ERROR;
01011       if (data == order->GetLoadType()) return CMD_ERROR;
01012       break;
01013 
01014     case MOF_DEPOT_ACTION:
01015       if (data >= DA_END) return CMD_ERROR;
01016       break;
01017 
01018     case MOF_COND_VARIABLE:
01019       if (data >= OCV_END) return CMD_ERROR;
01020       break;
01021 
01022     case MOF_COND_COMPARATOR:
01023       if (data >= OCC_END) return CMD_ERROR;
01024       switch (order->GetConditionVariable()) {
01025         case OCV_UNCONDITIONALLY: return CMD_ERROR;
01026 
01027         case OCV_REQUIRES_SERVICE:
01028           if (data != OCC_IS_TRUE && data != OCC_IS_FALSE) return CMD_ERROR;
01029           break;
01030 
01031         default:
01032           if (data == OCC_IS_TRUE || data == OCC_IS_FALSE) return CMD_ERROR;
01033           break;
01034       }
01035       break;
01036 
01037     case MOF_COND_VALUE:
01038       switch (order->GetConditionVariable()) {
01039         case OCV_UNCONDITIONALLY: return CMD_ERROR;
01040 
01041         case OCV_LOAD_PERCENTAGE:
01042         case OCV_RELIABILITY:
01043           if (data > 100) return CMD_ERROR;
01044           break;
01045 
01046         default:
01047           if (data > 2047) return CMD_ERROR;
01048           break;
01049       }
01050       break;
01051 
01052     case MOF_COND_DESTINATION:
01053       if (data >= v->GetNumOrders()) return CMD_ERROR;
01054       break;
01055   }
01056 
01057   if (flags & DC_EXEC) {
01058     switch (mof) {
01059       case MOF_NON_STOP:
01060         order->SetNonStopType((OrderNonStopFlags)data);
01061         break;
01062 
01063       case MOF_STOP_LOCATION:
01064         order->SetStopLocation((OrderStopLocation)data);
01065         break;
01066 
01067       case MOF_UNLOAD:
01068         order->SetUnloadType((OrderUnloadFlags)data);
01069         if ((data & OUFB_NO_UNLOAD) != 0 && (order->GetLoadType() & OLFB_NO_LOAD) != 0) {
01070           order->SetLoadType((OrderLoadFlags)(order->GetLoadType() & ~OLFB_NO_LOAD));
01071         }
01072         break;
01073 
01074       case MOF_LOAD:
01075         order->SetLoadType((OrderLoadFlags)data);
01076         if ((data & OLFB_NO_LOAD) != 0 && (order->GetUnloadType() & OUFB_NO_UNLOAD) != 0) {
01077           /* No load + no unload isn't compatible */
01078           order->SetUnloadType((OrderUnloadFlags)(order->GetUnloadType() & ~OUFB_NO_UNLOAD));
01079         }
01080         break;
01081 
01082       case MOF_DEPOT_ACTION: {
01083         switch (data) {
01084           case DA_ALWAYS_GO:
01085             order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() & ~ODTFB_SERVICE));
01086             order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() & ~ODATFB_HALT));
01087             break;
01088 
01089           case DA_SERVICE:
01090             order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() | ODTFB_SERVICE));
01091             order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() & ~ODATFB_HALT));
01092             break;
01093 
01094           case DA_STOP:
01095             order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() & ~ODTFB_SERVICE));
01096             order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() | ODATFB_HALT));
01097             break;
01098 
01099           default:
01100             NOT_REACHED();
01101         }
01102         break;
01103       }
01104 
01105       case MOF_COND_VARIABLE: {
01106         order->SetConditionVariable((OrderConditionVariable)data);
01107 
01108         OrderConditionComparator occ = order->GetConditionComparator();
01109         switch (order->GetConditionVariable()) {
01110           case OCV_UNCONDITIONALLY:
01111             order->SetConditionComparator(OCC_EQUALS);
01112             order->SetConditionValue(0);
01113             break;
01114 
01115           case OCV_REQUIRES_SERVICE:
01116             if (occ != OCC_IS_TRUE && occ != OCC_IS_FALSE) order->SetConditionComparator(OCC_IS_TRUE);
01117             break;
01118 
01119           case OCV_LOAD_PERCENTAGE:
01120           case OCV_RELIABILITY:
01121             if (order->GetConditionValue() > 100) order->SetConditionValue(100);
01122             /* FALL THROUGH */
01123           default:
01124             if (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) order->SetConditionComparator(OCC_EQUALS);
01125             break;
01126         }
01127         break;
01128       }
01129 
01130       case MOF_COND_COMPARATOR:
01131         order->SetConditionComparator((OrderConditionComparator)data);
01132         break;
01133 
01134       case MOF_COND_VALUE:
01135         order->SetConditionValue(data);
01136         break;
01137 
01138       case MOF_COND_DESTINATION:
01139         order->SetConditionSkipToOrder(data);
01140         break;
01141 
01142       default: NOT_REACHED();
01143     }
01144 
01145     /* Update the windows and full load flags, also for vehicles that share the same order list */
01146     Vehicle *u = v->FirstShared();
01147     DeleteOrderWarnings(u);
01148     for (; u != NULL; u = u->NextShared()) {
01149       /* Toggle u->current_order "Full load" flag if it changed.
01150        * However, as the same flag is used for depot orders, check
01151        * whether we are not going to a depot as there are three
01152        * cases where the full load flag can be active and only
01153        * one case where the flag is used for depot orders. In the
01154        * other cases for the OrderTypeByte the flags are not used,
01155        * so do not care and those orders should not be active
01156        * when this function is called.
01157        */
01158       if (sel_ord == u->cur_order_index &&
01159           (u->current_order.IsType(OT_GOTO_STATION) || u->current_order.IsType(OT_LOADING)) &&
01160           u->current_order.GetLoadType() != order->GetLoadType()) {
01161         u->current_order.SetLoadType(order->GetLoadType());
01162       }
01163       InvalidateVehicleOrder(u, -2);
01164     }
01165   }
01166 
01167   return CommandCost();
01168 }
01169 
01181 CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01182 {
01183   VehicleID veh_src = GB(p2, 0, 20);
01184   VehicleID veh_dst = GB(p1, 0, 20);
01185 
01186   Vehicle *dst = Vehicle::GetIfValid(veh_dst);
01187   if (dst == NULL || !dst->IsPrimaryVehicle()) return CMD_ERROR;
01188 
01189   CommandCost ret = CheckOwnership(dst->owner);
01190   if (ret.Failed()) return ret;
01191 
01192   switch (GB(p1, 30, 2)) {
01193     case CO_SHARE: {
01194       Vehicle *src = Vehicle::GetIfValid(veh_src);
01195 
01196       /* Sanity checks */
01197       if (src == NULL || !src->IsPrimaryVehicle() || dst->type != src->type || dst == src) return CMD_ERROR;
01198 
01199       CommandCost ret = CheckOwnership(src->owner);
01200       if (ret.Failed()) return ret;
01201 
01202       /* Trucks can't share orders with busses (and visa versa) */
01203       if (src->type == VEH_ROAD && RoadVehicle::From(src)->IsBus() != RoadVehicle::From(dst)->IsBus()) {
01204         return CMD_ERROR;
01205       }
01206 
01207       /* Is the vehicle already in the shared list? */
01208       if (src->FirstShared() == dst->FirstShared()) return CMD_ERROR;
01209 
01210       const Order *order;
01211 
01212       FOR_VEHICLE_ORDERS(src, order) {
01213         if (OrderGoesToStation(dst, order) &&
01214             !CanVehicleUseStation(dst, Station::Get(order->GetDestination()))) {
01215           return_cmd_error(STR_ERROR_CAN_T_COPY_SHARE_ORDER);
01216         }
01217       }
01218 
01219       if (flags & DC_EXEC) {
01220         /* If the destination vehicle had a OrderList, destroy it */
01221         DeleteVehicleOrders(dst);
01222 
01223         dst->orders.list = src->orders.list;
01224 
01225         /* Link this vehicle in the shared-list */
01226         dst->AddToShared(src);
01227 
01228         InvalidateVehicleOrder(dst, -1);
01229         InvalidateVehicleOrder(src, -2);
01230 
01231         InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
01232       }
01233       break;
01234     }
01235 
01236     case CO_COPY: {
01237       Vehicle *src = Vehicle::GetIfValid(veh_src);
01238 
01239       /* Sanity checks */
01240       if (src == NULL || !src->IsPrimaryVehicle() || dst->type != src->type || dst == src) return CMD_ERROR;
01241 
01242       CommandCost ret = CheckOwnership(src->owner);
01243       if (ret.Failed()) return ret;
01244 
01245       /* Trucks can't copy all the orders from busses (and visa versa),
01246        * and neither can helicopters and aircarft. */
01247       const Order *order;
01248       FOR_VEHICLE_ORDERS(src, order) {
01249         if (OrderGoesToStation(dst, order) &&
01250             !CanVehicleUseStation(dst, Station::Get(order->GetDestination()))) {
01251           return_cmd_error(STR_ERROR_CAN_T_COPY_SHARE_ORDER);
01252         }
01253       }
01254 
01255       /* make sure there are orders available */
01256       int delta = dst->IsOrderListShared() ? src->GetNumOrders() + 1 : src->GetNumOrders() - dst->GetNumOrders();
01257       if (!Order::CanAllocateItem(delta) ||
01258           ((dst->orders.list == NULL || dst->IsOrderListShared()) && !OrderList::CanAllocateItem())) {
01259         return_cmd_error(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
01260       }
01261 
01262       if (flags & DC_EXEC) {
01263         const Order *order;
01264         Order *first = NULL;
01265         Order **order_dst;
01266 
01267         /* If the destination vehicle had an order list, destroy the chain but keep the OrderList */
01268         DeleteVehicleOrders(dst, true);
01269 
01270         order_dst = &first;
01271         FOR_VEHICLE_ORDERS(src, order) {
01272           *order_dst = new Order();
01273           (*order_dst)->AssignOrder(*order);
01274           order_dst = &(*order_dst)->next;
01275         }
01276         if (dst->orders.list == NULL) {
01277           dst->orders.list = new OrderList(first, dst);
01278         } else {
01279           assert(dst->orders.list->GetFirstOrder() == NULL);
01280           assert(!dst->orders.list->IsShared());
01281           delete dst->orders.list;
01282           dst->orders.list = new OrderList(first, dst);
01283         }
01284 
01285         InvalidateVehicleOrder(dst, -1);
01286 
01287         InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
01288       }
01289       break;
01290     }
01291 
01292     case CO_UNSHARE: return DecloneOrder(dst, flags);
01293     default: return CMD_ERROR;
01294   }
01295 
01296   return CommandCost();
01297 }
01298 
01311 CommandCost CmdOrderRefit(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01312 {
01313   VehicleID veh = GB(p1, 0, 20);
01314   VehicleOrderID order_number  = GB(p2, 16, 8);
01315   CargoID cargo = GB(p2, 0, 8);
01316   byte subtype  = GB(p2, 8, 8);
01317 
01318   if (cargo >= NUM_CARGO) return CMD_ERROR;
01319 
01320   const Vehicle *v = Vehicle::GetIfValid(veh);
01321   if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
01322 
01323   CommandCost ret = CheckOwnership(v->owner);
01324   if (ret.Failed()) return ret;
01325 
01326   Order *order = v->GetOrder(order_number);
01327   if (order == NULL) return CMD_ERROR;
01328 
01329   if (flags & DC_EXEC) {
01330     order->SetRefit(cargo, subtype);
01331 
01332     for (Vehicle *u = v->FirstShared(); u != NULL; u = u->NextShared()) {
01333       /* Update any possible open window of the vehicle */
01334       InvalidateVehicleOrder(u, -2);
01335 
01336       /* If the vehicle already got the current depot set as current order, then update current order as well */
01337       if (u->cur_order_index == order_number && (u->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) {
01338         u->current_order.SetRefit(cargo, subtype);
01339       }
01340     }
01341   }
01342 
01343   return CommandCost();
01344 }
01345 
01346 
01352 void CheckOrders(const Vehicle *v)
01353 {
01354   /* Does the user wants us to check things? */
01355   if (_settings_client.gui.order_review_system == 0) return;
01356 
01357   /* Do nothing for crashed vehicles */
01358   if (v->vehstatus & VS_CRASHED) return;
01359 
01360   /* Do nothing for stopped vehicles if setting is '1' */
01361   if (_settings_client.gui.order_review_system == 1 && (v->vehstatus & VS_STOPPED)) return;
01362 
01363   /* do nothing we we're not the first vehicle in a share-chain */
01364   if (v->FirstShared() != v) return;
01365 
01366   /* Only check every 20 days, so that we don't flood the message log */
01367   if (v->owner == _local_company && v->day_counter % 20 == 0) {
01368     int n_st, problem_type = -1;
01369     const Order *order;
01370     int message = 0;
01371 
01372     /* Check the order list */
01373     n_st = 0;
01374 
01375     FOR_VEHICLE_ORDERS(v, order) {
01376       /* Dummy order? */
01377       if (order->IsType(OT_DUMMY)) {
01378         problem_type = 1;
01379         break;
01380       }
01381       /* Does station have a load-bay for this vehicle? */
01382       if (order->IsType(OT_GOTO_STATION)) {
01383         const Station *st = Station::Get(order->GetDestination());
01384 
01385         n_st++;
01386         if (!CanVehicleUseStation(v, st)) problem_type = 3;
01387       }
01388     }
01389 
01390     /* Check if the last and the first order are the same */
01391     if (v->GetNumOrders() > 1) {
01392       const Order *last = v->GetLastOrder();
01393 
01394       if (v->orders.list->GetFirstOrder()->Equals(*last)) {
01395         problem_type = 2;
01396       }
01397     }
01398 
01399     /* Do we only have 1 station in our order list? */
01400     if (n_st < 2 && problem_type == -1) problem_type = 0;
01401 
01402 #ifndef NDEBUG
01403     if (v->orders.list != NULL) v->orders.list->DebugCheckSanity();
01404 #endif
01405 
01406     /* We don't have a problem */
01407     if (problem_type < 0) return;
01408 
01409     message = STR_NEWS_VEHICLE_HAS_TOO_FEW_ORDERS + problem_type;
01410     //DEBUG(misc, 3, "Triggered News Item for vehicle %d", v->index);
01411 
01412     SetDParam(0, v->index);
01413     AddVehicleNewsItem(
01414       message,
01415       NS_ADVICE,
01416       v->index
01417     );
01418   }
01419 }
01420 
01426 void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination)
01427 {
01428   Vehicle *v;
01429 
01430   /* Aircraft have StationIDs for depot orders and never use DepotIDs
01431    * This fact is handled specially below
01432    */
01433 
01434   /* Go through all vehicles */
01435   FOR_ALL_VEHICLES(v) {
01436     Order *order;
01437 
01438     order = &v->current_order;
01439     if ((v->type == VEH_AIRCRAFT && order->IsType(OT_GOTO_DEPOT) ? OT_GOTO_STATION : order->GetType()) == type &&
01440         v->current_order.GetDestination() == destination) {
01441       order->MakeDummy();
01442       SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01443     }
01444 
01445     /* Clear the order from the order-list */
01446     int id = -1;
01447     FOR_VEHICLE_ORDERS(v, order) {
01448       id++;
01449       if (order->IsType(OT_GOTO_DEPOT) && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
01450       if ((v->type == VEH_AIRCRAFT && order->IsType(OT_GOTO_DEPOT) ? OT_GOTO_STATION : order->GetType()) == type &&
01451           order->GetDestination() == destination) {
01452         order->MakeDummy();
01453         for (const Vehicle *w = v->FirstShared(); w != NULL; w = w->NextShared()) {
01454           /* In GUI, simulate by removing the order and adding it back */
01455           InvalidateVehicleOrder(w, id | (INVALID_VEH_ORDER_ID << 8));
01456           InvalidateVehicleOrder(w, (INVALID_VEH_ORDER_ID << 8) | id);
01457         }
01458       }
01459     }
01460   }
01461 }
01462 
01467 bool Vehicle::HasDepotOrder() const
01468 {
01469   const Order *order;
01470 
01471   FOR_VEHICLE_ORDERS(this, order) {
01472     if (order->IsType(OT_GOTO_DEPOT)) return true;
01473   }
01474 
01475   return false;
01476 }
01477 
01483 void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist)
01484 {
01485   DeleteOrderWarnings(v);
01486 
01487   if (v->IsOrderListShared()) {
01488     /* Remove ourself from the shared order list. */
01489     v->RemoveFromShared();
01490     v->orders.list = NULL;
01491   } else if (v->orders.list != NULL) {
01492     /* Remove the orders */
01493     v->orders.list->FreeChain(keep_orderlist);
01494     if (!keep_orderlist) v->orders.list = NULL;
01495   }
01496 }
01497 
01498 uint16 GetServiceIntervalClamped(uint interval, CompanyID company_id)
01499 {
01500   return (Company::Get(company_id)->settings.vehicle.servint_ispercent) ? Clamp(interval, MIN_SERVINT_PERCENT, MAX_SERVINT_PERCENT) : Clamp(interval, MIN_SERVINT_DAYS, MAX_SERVINT_DAYS);
01501 }
01502 
01511 static bool CheckForValidOrders(const Vehicle *v)
01512 {
01513   const Order *order;
01514 
01515   FOR_VEHICLE_ORDERS(v, order) {
01516     switch (order->GetType()) {
01517       case OT_GOTO_STATION:
01518       case OT_GOTO_DEPOT:
01519       case OT_GOTO_WAYPOINT:
01520         return true;
01521 
01522       default:
01523         break;
01524     }
01525   }
01526 
01527   return false;
01528 }
01529 
01533 static bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value)
01534 {
01535   switch (occ) {
01536     case OCC_EQUALS:      return variable == value;
01537     case OCC_NOT_EQUALS:  return variable != value;
01538     case OCC_LESS_THAN:   return variable <  value;
01539     case OCC_LESS_EQUALS: return variable <= value;
01540     case OCC_MORE_THAN:   return variable >  value;
01541     case OCC_MORE_EQUALS: return variable >= value;
01542     case OCC_IS_TRUE:     return variable != 0;
01543     case OCC_IS_FALSE:    return variable == 0;
01544     default: NOT_REACHED();
01545   }
01546 }
01547 
01554 VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
01555 {
01556   if (order->GetType() != OT_CONDITIONAL) return INVALID_VEH_ORDER_ID;
01557 
01558   bool skip_order = false;
01559   OrderConditionComparator occ = order->GetConditionComparator();
01560   uint16 value = order->GetConditionValue();
01561 
01562   switch (order->GetConditionVariable()) {
01563     case OCV_LOAD_PERCENTAGE:  skip_order = OrderConditionCompare(occ, CalcPercentVehicleFilled(v, NULL), value); break;
01564     case OCV_RELIABILITY:      skip_order = OrderConditionCompare(occ, ToPercent16(v->reliability),       value); break;
01565     case OCV_MAX_SPEED:        skip_order = OrderConditionCompare(occ, v->GetDisplayMaxSpeed() * 10 / 16, value); break;
01566     case OCV_AGE:              skip_order = OrderConditionCompare(occ, v->age / DAYS_IN_LEAP_YEAR,        value); break;
01567     case OCV_REQUIRES_SERVICE: skip_order = OrderConditionCompare(occ, v->NeedsServicing(),               value); break;
01568     case OCV_UNCONDITIONALLY:  skip_order = true; break;
01569     default: NOT_REACHED();
01570   }
01571 
01572   return skip_order ? order->GetConditionSkipToOrder() : (VehicleOrderID)INVALID_VEH_ORDER_ID;
01573 }
01574 
01581 bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth)
01582 {
01583   if (conditional_depth > v->GetNumOrders()) return false;
01584 
01585   switch (order->GetType()) {
01586     case OT_GOTO_STATION:
01587       v->dest_tile = v->GetOrderStationLocation(order->GetDestination());
01588       return true;
01589 
01590     case OT_GOTO_DEPOT:
01591       if ((order->GetDepotOrderType() & ODTFB_SERVICE) && !v->NeedsServicing()) {
01592         UpdateVehicleTimetable(v, true);
01593         v->IncrementOrderIndex();
01594         break;
01595       }
01596 
01597       if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) {
01598         /* We need to search for the nearest depot (hangar). */
01599         TileIndex location;
01600         DestinationID destination;
01601         bool reverse;
01602 
01603         if (v->FindClosestDepot(&location, &destination, &reverse)) {
01604           v->dest_tile = location;
01605           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());
01606 
01607           /* If there is no depot in front, reverse automatically (trains only) */
01608           if (v->type == VEH_TRAIN && reverse) DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01609 
01610           if (v->type == VEH_AIRCRAFT) {
01611             Aircraft *a = Aircraft::From(v);
01612             if (a->state == FLYING && a->targetairport != destination) {
01613               /* The aircraft is now heading for a different hangar than the next in the orders */
01614               extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01615               AircraftNextAirportPos_and_Order(a);
01616             }
01617           }
01618           return true;
01619         }
01620 
01621         UpdateVehicleTimetable(v, true);
01622         v->IncrementOrderIndex();
01623       } else {
01624         if (v->type != VEH_AIRCRAFT) {
01625           v->dest_tile = Depot::Get(order->GetDestination())->xy;
01626         }
01627         return true;
01628       }
01629       break;
01630 
01631     case OT_GOTO_WAYPOINT:
01632       v->dest_tile = Waypoint::Get(order->GetDestination())->xy;
01633       return true;
01634 
01635     case OT_CONDITIONAL: {
01636       VehicleOrderID next_order = ProcessConditionalOrder(order, v);
01637       if (next_order != INVALID_VEH_ORDER_ID) {
01638         UpdateVehicleTimetable(v, false);
01639         v->cur_order_index = next_order;
01640         v->current_order_time += v->GetOrder(next_order)->travel_time;
01641       } else {
01642         UpdateVehicleTimetable(v, true);
01643         v->IncrementOrderIndex();
01644       }
01645       break;
01646     }
01647 
01648     default:
01649       v->dest_tile = 0;
01650       return false;
01651   }
01652 
01653   assert(v->cur_order_index < v->GetNumOrders());
01654 
01655   /* Get the current order */
01656   order = v->GetOrder(v->cur_order_index);
01657   v->current_order = *order;
01658   return UpdateOrderDest(v, order, conditional_depth + 1);
01659 }
01660 
01668 bool ProcessOrders(Vehicle *v)
01669 {
01670   switch (v->current_order.GetType()) {
01671     case OT_GOTO_DEPOT:
01672       /* Let a depot order in the orderlist interrupt. */
01673       if (!(v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) return false;
01674       break;
01675 
01676     case OT_LOADING:
01677       return false;
01678 
01679     case OT_LEAVESTATION:
01680       if (v->type != VEH_AIRCRAFT) return false;
01681       break;
01682 
01683     default: break;
01684   }
01685 
01693   bool may_reverse = v->current_order.IsType(OT_NOTHING);
01694 
01695   /* Check if we've reached a 'via' destination. */
01696   if (((v->current_order.IsType(OT_GOTO_STATION) && (v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) || v->current_order.IsType(OT_GOTO_WAYPOINT)) &&
01697       IsTileType(v->tile, MP_STATION) &&
01698       v->current_order.GetDestination() == GetStationIndex(v->tile)) {
01699     /* We set the last visited station here because we do not want
01700      * the train to stop at this 'via' station if the next order
01701      * is a no-non-stop order; in that case not setting the last
01702      * visited station will cause the vehicle to still stop. */
01703     v->last_station_visited = v->current_order.GetDestination();
01704     UpdateVehicleTimetable(v, true);
01705     v->IncrementOrderIndex();
01706   }
01707 
01708   /* Get the current order */
01709   if (v->cur_order_index >= v->GetNumOrders()) v->cur_order_index = 0;
01710 
01711   const Order *order = v->GetOrder(v->cur_order_index);
01712 
01713   /* If no order, do nothing. */
01714   if (order == NULL || (v->type == VEH_AIRCRAFT && !CheckForValidOrders(v))) {
01715     if (v->type == VEH_AIRCRAFT) {
01716       /* Aircraft do something vastly different here, so handle separately */
01717       extern void HandleMissingAircraftOrders(Aircraft *v);
01718       HandleMissingAircraftOrders(Aircraft::From(v));
01719       return false;
01720     }
01721 
01722     v->current_order.Free();
01723     v->dest_tile = 0;
01724     return false;
01725   }
01726 
01727   /* If it is unchanged, keep it. */
01728   if (order->Equals(v->current_order) && (v->type == VEH_AIRCRAFT || v->dest_tile != 0) &&
01729       (v->type != VEH_SHIP || !order->IsType(OT_GOTO_STATION) || Station::Get(order->GetDestination())->dock_tile != INVALID_TILE)) {
01730     return false;
01731   }
01732 
01733   /* Otherwise set it, and determine the destination tile. */
01734   v->current_order = *order;
01735 
01736   InvalidateVehicleOrder(v, -2);
01737   switch (v->type) {
01738     default:
01739       NOT_REACHED();
01740 
01741     case VEH_ROAD:
01742     case VEH_TRAIN:
01743       break;
01744 
01745     case VEH_AIRCRAFT:
01746     case VEH_SHIP:
01747       SetWindowClassesDirty(GetWindowClassForVehicleType(v->type));
01748       break;
01749   }
01750 
01751   return UpdateOrderDest(v, order) && may_reverse;
01752 }
01753 
01761 bool Order::ShouldStopAtStation(const Vehicle *v, StationID station) const
01762 {
01763   bool is_dest_station = this->IsType(OT_GOTO_STATION) && this->dest == station;
01764 
01765   return (!this->IsType(OT_GOTO_DEPOT) || (this->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0) &&
01766       v->last_station_visited != station && // Do stop only when we've not just been there
01767       /* Finally do stop when there is no non-stop flag set for this type of station. */
01768       !(this->GetNonStopType() & (is_dest_station ? ONSF_NO_STOP_AT_DESTINATION_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS));
01769 }
01770 
01771 void InitializeOrders()
01772 {
01773   _order_pool.CleanPool();
01774   _orderlist_pool.CleanPool();
01775 }

Generated on Thu Dec 23 23:41:29 2010 for OpenTTD by  doxygen 1.6.1