# ai_order.cpp

Go to the documentation of this file.

    00001 /* $Id: ai_order.cpp 15299 2009-01-31 20:16:06Z smatz $ */
    00002
    00005 #include "ai_order.hpp"
    00006 #include "ai_vehicle.hpp"
    00007 #include "../ai_instance.hpp"
    00008 #include "../../debug.h"
    00009 #include "../../vehicle_base.h"
    00010 #include "../../depot_base.h"
    00011 #include "../../station_map.h"
    00012 #include "../../waypoint.h"
    00013
    00019 static OrderType GetOrderTypeByTile(TileIndex t)
    00020 {
    00021   if (!::IsValidTile(t)) return OT_END;
    00022
    00023   switch (::GetTileType(t)) {
    00024     default: break;
    00025     case MP_STATION: return OT_GOTO_STATION; break;
    00026     case MP_WATER:   if (::IsShipDepot(t)) return OT_GOTO_DEPOT; break;
    00027     case MP_ROAD:    if (::GetRoadTileType(t) == ROAD_TILE_DEPOT) return OT_GOTO_DEPOT; break;
    00028     case MP_RAILWAY:
    00029       switch (::GetRailTileType(t)) {
    00030         case RAIL_TILE_DEPOT:    return OT_GOTO_DEPOT;
    00031         case RAIL_TILE_WAYPOINT: return OT_GOTO_WAYPOINT;
    00032         default: break;
    00033       }
    00034       break;
    00035   }
    00036
    00037   return OT_END;
    00038 }
    00039
    00040 /* static */ bool AIOrder::IsValidVehicleOrder(VehicleID vehicle_id, OrderPosition order_position)
    00041 {
    00042   return AIVehicle::IsValidVehicle(vehicle_id) && order_position >= 0 && (order_position < ::GetVehicle(vehicle_id)->GetNumOrders() || order_position == ORDER_CURRENT);
    00043 }
    00044
    00045 /* static */ AIOrder::OrderPosition AIOrder::ResolveOrderPosition(VehicleID vehicle_id, OrderPosition order_position)
    00046 {
    00047   if (!AIVehicle::IsValidVehicle(vehicle_id)) return ORDER_INVALID;
    00048
    00049   if (order_position == ORDER_CURRENT) return (AIOrder::OrderPosition)::GetVehicle(vehicle_id)->cur_order_index;
    00050   return (order_position >= 0 && order_position < ::GetVehicle(vehicle_id)->GetNumOrders()) ? order_position : ORDER_INVALID;
    00051 }
    00052
    00053
    00054 /* static */ bool AIOrder::AreOrderFlagsValid(TileIndex destination, AIOrderFlags order_flags)
    00055 {
    00056   switch (::GetOrderTypeByTile(destination)) {
    00057     case OT_GOTO_STATION:
    00058       return ((order_flags & ~(AIOF_NON_STOP_FLAGS | AIOF_UNLOAD_FLAGS | AIOF_LOAD_FLAGS)) == 0) &&
    00059           /* Test the different mutual exclusive flags. */
    00060           (((order_flags & AIOF_TRANSFER)      == 0) || ((order_flags & AIOF_UNLOAD)    == 0)) &&
    00061           (((order_flags & AIOF_TRANSFER)      == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) &&
    00062           (((order_flags & AIOF_UNLOAD)        == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) &&
    00063           (((order_flags & AIOF_UNLOAD)        == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) &&
    00064           (((order_flags & AIOF_NO_UNLOAD)     == 0) || ((order_flags & AIOF_NO_LOAD)   == 0)) &&
    00065           (((order_flags & AIOF_FULL_LOAD_ANY) == 0) || ((order_flags & AIOF_NO_LOAD)   == 0));
    00066
    00067     case OT_GOTO_DEPOT:    return (order_flags & ~(AIOF_NON_STOP_FLAGS | AIOF_SERVICE_IF_NEEDED)) == 0;
    00068     case OT_GOTO_WAYPOINT: return (order_flags & ~(AIOF_NON_STOP_FLAGS)) == 0;
    00069     default:               return false;
    00070   }
    00071 }
    00072
    00073 /* static */ int32 AIOrder::GetOrderCount(VehicleID vehicle_id)
    00074 {
    00075   return AIVehicle::IsValidVehicle(vehicle_id) ? ::GetVehicle(vehicle_id)->GetNumOrders() : -1;
    00076 }
    00077
    00078 /* static */ TileIndex AIOrder::GetOrderDestination(VehicleID vehicle_id, OrderPosition order_position)
    00079 {
    00080   if (!IsValidVehicleOrder(vehicle_id, order_position)) return INVALID_TILE;
    00081
    00082   const Order *order;
    00083   const Vehicle *v = ::GetVehicle(vehicle_id);
    00084   if (order_position == ORDER_CURRENT) {
    00085     order = &v->current_order;
    00086   } else {
    00087     order = v->GetFirstOrder();
    00088     for (int i = 0; i < order_position; i++) order = order->next;
    00089   }
    00090
    00091   switch (order->GetType()) {
    00092     case OT_GOTO_DEPOT:
    00093       if (v->type != VEH_AIRCRAFT) return ::GetDepot(order->GetDestination())->xy;
    00094       /* FALL THROUGH: aircraft's hangars are referenced by StationID, not DepotID */
    00095
    00096     case OT_GOTO_STATION:  return ::GetStation(order->GetDestination())->xy;
    00097     case OT_GOTO_WAYPOINT: return ::GetWaypoint(order->GetDestination())->xy;
    00098     default:               return INVALID_TILE;
    00099   }
    00100 }
    00101
    00102 /* static */ AIOrder::AIOrderFlags AIOrder::GetOrderFlags(VehicleID vehicle_id, OrderPosition order_position)
    00103 {
    00104   if (!IsValidVehicleOrder(vehicle_id, order_position)) return AIOF_INVALID;
    00105
    00106   const Order *order;
    00107   if (order_position == ORDER_CURRENT) {
    00108     order = &::GetVehicle(vehicle_id)->current_order;
    00109   } else {
    00110     order = ::GetVehicle(vehicle_id)->GetFirstOrder();
    00111     for (int i = 0; i < order_position; i++) order = order->next;
    00112   }
    00113
    00114   AIOrderFlags order_flags = AIOF_NONE;
    00115   order_flags |= (AIOrderFlags)order->GetNonStopType();
    00116   switch (order->GetType()) {
    00117     case OT_GOTO_DEPOT:
    00118       if (order->GetDepotOrderType() & ODTFB_SERVICE) order_flags |= AIOF_SERVICE_IF_NEEDED;
    00119       break;
    00120
    00121     case OT_GOTO_STATION:
    00122       order_flags |= (AIOrderFlags)(order->GetLoadType()   << 5);
    00123       order_flags |= (AIOrderFlags)(order->GetUnloadType() << 2);
    00124       break;
    00125
    00126     default: break;
    00127   }
    00128
    00129   return order_flags;
    00130 }
    00131
    00132 /* static */ bool AIOrder::AppendOrder(VehicleID vehicle_id, TileIndex destination, AIOrderFlags order_flags)
    00133 {
    00134   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00135   return InsertOrder(vehicle_id, (AIOrder::OrderPosition)::GetVehicle(vehicle_id)->GetNumOrders(), destination, order_flags);
    00136 }
    00137
    00138 /* static */ bool AIOrder::InsertOrder(VehicleID vehicle_id, OrderPosition order_position, TileIndex destination, AIOrder::AIOrderFlags order_flags)
    00139 {
    00140   /* IsValidVehicleOrder is not good enough because it does not allow appending. */
    00141   if (order_position == ORDER_CURRENT) order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
    00142
    00143   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00144   EnforcePrecondition(false, order_position >= 0 && order_position <= ::GetVehicle(vehicle_id)->GetNumOrders());
    00145   EnforcePrecondition(false, AreOrderFlagsValid(destination, order_flags));
    00146
    00147   Order order;
    00148   switch (::GetOrderTypeByTile(destination)) {
    00149     case OT_GOTO_DEPOT:
    00150       order.MakeGoToDepot(::GetDepotByTile(destination)->index, (OrderDepotTypeFlags)(ODTFB_PART_OF_ORDERS | ((order_flags & AIOF_SERVICE_IF_NEEDED) ? ODTFB_SERVICE : 0)));
    00151       break;
    00152
    00153     case OT_GOTO_STATION:
    00154       order.MakeGoToStation(::GetStationIndex(destination));
    00155       order.SetLoadType((OrderLoadFlags)GB(order_flags, 5, 3));
    00156       order.SetUnloadType((OrderUnloadFlags)GB(order_flags, 2, 3));
    00157       break;
    00158
    00159     case OT_GOTO_WAYPOINT:
    00160       order.MakeGoToWaypoint(::GetWaypointIndex(destination));
    00161       break;
    00162
    00163     default:
    00164       return false;
    00165   }
    00166
    00167   order.SetNonStopType((OrderNonStopFlags)GB(order_flags, 0, 2));
    00168
    00169   return AIObject::DoCommand(0, vehicle_id | (order_position << 16), order.Pack(), CMD_INSERT_ORDER);
    00170 }
    00171
    00172 /* static */ bool AIOrder::RemoveOrder(VehicleID vehicle_id, OrderPosition order_position)
    00173 {
    00174   order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
    00175
    00176   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
    00177
    00178   return AIObject::DoCommand(0, vehicle_id, order_position, CMD_DELETE_ORDER);
    00179 }
    00180
    00189 static void _DoCommandReturnChangeOrder(class AIInstance *instance)
    00190 {
    00191   AIObject::SetLastCommandRes(AIOrder::_ChangeOrder());
    00192   AIInstance::DoCommandReturn(instance);
    00193 }
    00194
    00195 /* static */ bool AIOrder::_ChangeOrder()
    00196 {
    00197   /* Make sure we don't go into an infinite loop */
    00198   int retry = AIObject::GetCallbackVariable(3) - 1;
    00199   if (retry < 0) {
    00200     DEBUG(ai, 0, "Possible infinite loop in ChangeOrder detected");
    00201     return false;
    00202   }
    00203   AIObject::SetCallbackVariable(3, retry);
    00204
    00205   VehicleID vehicle_id = (VehicleID)AIObject::GetCallbackVariable(0);
    00206   OrderPosition order_position = (OrderPosition)AIObject::GetCallbackVariable(1);
    00207   AIOrderFlags order_flags = (AIOrderFlags)AIObject::GetCallbackVariable(2);
    00208
    00209   order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
    00210
    00211   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
    00212   EnforcePrecondition(false, AreOrderFlagsValid(GetOrderDestination(vehicle_id, order_position), order_flags));
    00213
    00214   Order *order = ::GetVehicle(vehicle_id)->GetFirstOrder();
    00215   for (int i = 0; i < order_position; i++) order = order->next;
    00216
    00217   AIOrderFlags current = GetOrderFlags(vehicle_id, order_position);
    00218
    00219   if ((current & AIOF_NON_STOP_FLAGS) != (order_flags & AIOF_NON_STOP_FLAGS)) {
    00220     return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_NON_STOP_FLAGS) << 4 | MOF_NON_STOP, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder);
    00221   }
    00222
    00223   switch (order->GetType()) {
    00224     case OT_GOTO_DEPOT:
    00225       if ((current & AIOF_SERVICE_IF_NEEDED) != (order_flags & AIOF_SERVICE_IF_NEEDED)) {
    00226         return AIObject::DoCommand(0, vehicle_id | (order_position << 16), MOF_DEPOT_ACTION, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder);
    00227       }
    00228       break;
    00229
    00230     case OT_GOTO_STATION:
    00231       if ((current & AIOF_UNLOAD_FLAGS) != (order_flags & AIOF_UNLOAD_FLAGS)) {
    00232         return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_UNLOAD_FLAGS) << 2 | MOF_UNLOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder);
    00233       }
    00234       if ((current & AIOF_LOAD_FLAGS) != (order_flags & AIOF_LOAD_FLAGS)) {
    00235         return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_LOAD_FLAGS) >> 1 | MOF_LOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder);
    00236       }
    00237       break;
    00238
    00239     default: break;
    00240   }
    00241
    00242   assert(GetOrderFlags(vehicle_id, order_position) == order_flags);
    00243
    00244   return true;
    00245 }
    00246
    00247 /* static */ bool AIOrder::ChangeOrder(VehicleID vehicle_id, OrderPosition order_position, AIOrder::AIOrderFlags order_flags)
    00248 {
    00249   AIObject::SetCallbackVariable(0, vehicle_id);
    00250   AIObject::SetCallbackVariable(1, order_position);
    00251   AIObject::SetCallbackVariable(2, order_flags);
    00252   /* In case another client(s) change orders at the same time we could
    00253    * end in an infinite loop. This stops that from happening ever. */
    00254   AIObject::SetCallbackVariable(3, 8);
    00255   return AIOrder::_ChangeOrder();
    00256 }
    00257
    00258 /* static */ bool AIOrder::MoveOrder(VehicleID vehicle_id, OrderPosition order_position_move, OrderPosition order_position_target)
    00259 {
    00260   order_position_move   = AIOrder::ResolveOrderPosition(vehicle_id, order_position_move);
    00261   order_position_target = AIOrder::ResolveOrderPosition(vehicle_id, order_position_target);
    00262
    00263   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position_move));
    00264   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position_target));
    00265
    00266   return AIObject::DoCommand(0, vehicle_id, order_position_move | (order_position_target << 16), CMD_MOVE_ORDER);
    00267 }
    00268
    00269 /* static */ bool AIOrder::CopyOrders(VehicleID vehicle_id, VehicleID main_vehicle_id)
    00270 {
    00271   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00272   EnforcePrecondition(false, AIVehicle::IsValidVehicle(main_vehicle_id));
    00273
    00274   return AIObject::DoCommand(0, vehicle_id | (main_vehicle_id << 16), CO_COPY, CMD_CLONE_ORDER);
    00275 }
    00276
    00277 /* static */ bool AIOrder::ShareOrders(VehicleID vehicle_id, VehicleID main_vehicle_id)
    00278 {
    00279   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00280   EnforcePrecondition(false, AIVehicle::IsValidVehicle(main_vehicle_id));
    00281
    00282   return AIObject::DoCommand(0, vehicle_id | (main_vehicle_id << 16), CO_SHARE, CMD_CLONE_ORDER);
    00283 }
    00284
    00285 /* static */ bool AIOrder::UnshareOrders(VehicleID vehicle_id)
    00286 {
    00287   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00288
    00289   return AIObject::DoCommand(0, vehicle_id, CO_UNSHARE, CMD_CLONE_ORDER);
    00290 }