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 }

Generated on Mon Feb 16 23:12:05 2009 for openttd by  doxygen 1.5.6