# ai_order.cpp

Go to the documentation of this file.

    00001 /* $Id: ai_order.cpp 15616 2009-03-04 23:12:48Z yexo $ */
    00002
    00005 #include "ai_order.hpp"
    00006 #include "ai_vehicle.hpp"
    00007 #include "ai_log.hpp"
    00008 #include "../ai_instance.hpp"
    00009 #include "../../debug.h"
    00010 #include "../../vehicle_base.h"
    00011 #include "../../depot_base.h"
    00012 #include "../../station_map.h"
    00013 #include "../../waypoint.h"
    00014
    00020 static OrderType GetOrderTypeByTile(TileIndex t)
    00021 {
    00022   if (!::IsValidTile(t)) return OT_END;
    00023
    00024   switch (::GetTileType(t)) {
    00025     default: break;
    00026     case MP_STATION: return OT_GOTO_STATION; break;
    00027     case MP_WATER:   if (::IsShipDepot(t)) return OT_GOTO_DEPOT; break;
    00028     case MP_ROAD:    if (::GetRoadTileType(t) == ROAD_TILE_DEPOT) return OT_GOTO_DEPOT; break;
    00029     case MP_RAILWAY:
    00030       switch (::GetRailTileType(t)) {
    00031         case RAIL_TILE_DEPOT:    return OT_GOTO_DEPOT;
    00032         case RAIL_TILE_WAYPOINT: return OT_GOTO_WAYPOINT;
    00033         default: break;
    00034       }
    00035       break;
    00036   }
    00037
    00038   return OT_END;
    00039 }
    00040
    00041 /* static */ bool AIOrder::IsValidVehicleOrder(VehicleID vehicle_id, OrderPosition order_position)
    00042 {
    00043   return AIVehicle::IsValidVehicle(vehicle_id) && order_position >= 0 && (order_position < ::GetVehicle(vehicle_id)->GetNumOrders() || order_position == ORDER_CURRENT);
    00044 }
    00045
    00046 /* static */ bool AIOrder::IsConditionalOrder(VehicleID vehicle_id, OrderPosition order_position)
    00047 {
    00048   if (order_position == ORDER_CURRENT) return false;
    00049   if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
    00050
    00051   const Order *order = ::GetVehicleOrder(GetVehicle(vehicle_id), order_position);
    00052   return order->GetType() == OT_CONDITIONAL;
    00053 }
    00054
    00055 /* static */ AIOrder::OrderPosition AIOrder::ResolveOrderPosition(VehicleID vehicle_id, OrderPosition order_position)
    00056 {
    00057   if (!AIVehicle::IsValidVehicle(vehicle_id)) return ORDER_INVALID;
    00058
    00059   if (order_position == ORDER_CURRENT) return (AIOrder::OrderPosition)::GetVehicle(vehicle_id)->cur_order_index;
    00060   return (order_position >= 0 && order_position < ::GetVehicle(vehicle_id)->GetNumOrders()) ? order_position : ORDER_INVALID;
    00061 }
    00062
    00063
    00064 /* static */ bool AIOrder::AreOrderFlagsValid(TileIndex destination, AIOrderFlags order_flags)
    00065 {
    00066   switch (::GetOrderTypeByTile(destination)) {
    00067     case OT_GOTO_STATION:
    00068       return ((order_flags & ~(AIOF_NON_STOP_FLAGS | AIOF_UNLOAD_FLAGS | AIOF_LOAD_FLAGS)) == 0) &&
    00069           /* Test the different mutual exclusive flags. */
    00070           (((order_flags & AIOF_TRANSFER)      == 0) || ((order_flags & AIOF_UNLOAD)    == 0)) &&
    00071           (((order_flags & AIOF_TRANSFER)      == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) &&
    00072           (((order_flags & AIOF_UNLOAD)        == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) &&
    00073           (((order_flags & AIOF_UNLOAD)        == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) &&
    00074           (((order_flags & AIOF_NO_UNLOAD)     == 0) || ((order_flags & AIOF_NO_LOAD)   == 0)) &&
    00075           (((order_flags & AIOF_FULL_LOAD_ANY) == 0) || ((order_flags & AIOF_NO_LOAD)   == 0));
    00076
    00077     case OT_GOTO_DEPOT:    return (order_flags & ~(AIOF_NON_STOP_FLAGS | AIOF_SERVICE_IF_NEEDED)) == 0;
    00078     case OT_GOTO_WAYPOINT: return (order_flags & ~(AIOF_NON_STOP_FLAGS)) == 0;
    00079     default:               return false;
    00080   }
    00081 }
    00082
    00083 /* static */ bool AIOrder::IsValidConditionalOrder(OrderCondition condition, CompareFunction compare)
    00084 {
    00085   switch (condition) {
    00086     case OC_LOAD_PERCENTAGE:
    00087     case OC_RELIABILITY:
    00088     case OC_MAX_SPEED:
    00089     case OC_AGE:
    00090       return compare >= CF_EQUALS && compare <= CF_MORE_EQUALS;
    00091
    00092     case OC_REQUIRES_SERVICE:
    00093       return compare == CF_IS_TRUE || compare == CF_IS_FALSE;
    00094
    00095     case OC_UNCONDITIONALLY:
    00096       return true;
    00097
    00098     default: return false;
    00099   }
    00100 }
    00101
    00102 /* static */ int32 AIOrder::GetOrderCount(VehicleID vehicle_id)
    00103 {
    00104   return AIVehicle::IsValidVehicle(vehicle_id) ? ::GetVehicle(vehicle_id)->GetNumOrders() : -1;
    00105 }
    00106
    00107 /* static */ TileIndex AIOrder::GetOrderDestination(VehicleID vehicle_id, OrderPosition order_position)
    00108 {
    00109   if (!IsValidVehicleOrder(vehicle_id, order_position)) return INVALID_TILE;
    00110
    00111   const Order *order;
    00112   const Vehicle *v = ::GetVehicle(vehicle_id);
    00113   if (order_position == ORDER_CURRENT) {
    00114     order = &v->current_order;
    00115   } else {
    00116     order = ::GetVehicleOrder(GetVehicle(vehicle_id), order_position);
    00117     if (order->GetType() == OT_CONDITIONAL) return INVALID_TILE;
    00118   }
    00119
    00120   switch (order->GetType()) {
    00121     case OT_GOTO_DEPOT:
    00122       if (v->type != VEH_AIRCRAFT) return ::GetDepot(order->GetDestination())->xy;
    00123       /* FALL THROUGH: aircraft's hangars are referenced by StationID, not DepotID */
    00124
    00125     case OT_GOTO_STATION:  return ::GetStation(order->GetDestination())->xy;
    00126     case OT_GOTO_WAYPOINT: return ::GetWaypoint(order->GetDestination())->xy;
    00127     default:               return INVALID_TILE;
    00128   }
    00129 }
    00130
    00131 /* static */ AIOrder::AIOrderFlags AIOrder::GetOrderFlags(VehicleID vehicle_id, OrderPosition order_position)
    00132 {
    00133   if (!IsValidVehicleOrder(vehicle_id, order_position)) return AIOF_INVALID;
    00134
    00135   const Order *order;
    00136   if (order_position == ORDER_CURRENT) {
    00137     order = &::GetVehicle(vehicle_id)->current_order;
    00138   } else {
    00139     order = ::GetVehicleOrder(GetVehicle(vehicle_id), order_position);
    00140     if (order->GetType() == OT_CONDITIONAL) return AIOF_INVALID;
    00141   }
    00142
    00143   AIOrderFlags order_flags = AIOF_NONE;
    00144   order_flags |= (AIOrderFlags)order->GetNonStopType();
    00145   switch (order->GetType()) {
    00146     case OT_GOTO_DEPOT:
    00147       if (order->GetDepotOrderType() & ODTFB_SERVICE) order_flags |= AIOF_SERVICE_IF_NEEDED;
    00148       break;
    00149
    00150     case OT_GOTO_STATION:
    00151       order_flags |= (AIOrderFlags)(order->GetLoadType()   << 5);
    00152       order_flags |= (AIOrderFlags)(order->GetUnloadType() << 2);
    00153       break;
    00154
    00155     default: break;
    00156   }
    00157
    00158   return order_flags;
    00159 }
    00160
    00161 /* static */ AIOrder::OrderPosition AIOrder::GetOrderJumpTo(VehicleID vehicle_id, OrderPosition order_position)
    00162 {
    00163   if (!IsValidVehicleOrder(vehicle_id, order_position)) return ORDER_INVALID;
    00164   if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return ORDER_INVALID;
    00165
    00166   const Order *order = ::GetVehicleOrder(GetVehicle(vehicle_id), order_position);
    00167   return (OrderPosition)order->GetConditionSkipToOrder();
    00168 }
    00169
    00170 /* static */ AIOrder::OrderCondition AIOrder::GetOrderCondition(VehicleID vehicle_id, OrderPosition order_position)
    00171 {
    00172   if (!IsValidVehicleOrder(vehicle_id, order_position)) return OC_INVALID;
    00173   if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return OC_INVALID;
    00174
    00175   const Order *order = ::GetVehicleOrder(GetVehicle(vehicle_id), order_position);
    00176   return (OrderCondition)order->GetConditionVariable();
    00177 }
    00178
    00179 /* static */ AIOrder::CompareFunction AIOrder::GetOrderCompareFunction(VehicleID vehicle_id, OrderPosition order_position)
    00180 {
    00181   if (!IsValidVehicleOrder(vehicle_id, order_position)) return CF_INVALID;
    00182   if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return CF_INVALID;
    00183
    00184   const Order *order = ::GetVehicleOrder(GetVehicle(vehicle_id), order_position);
    00185   return (CompareFunction)order->GetConditionComparator();
    00186 }
    00187
    00188 /* static */ int32 AIOrder::GetOrderCompareValue(VehicleID vehicle_id, OrderPosition order_position)
    00189 {
    00190   if (!IsValidVehicleOrder(vehicle_id, order_position)) return -1;
    00191   if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return -1;
    00192
    00193   const Order *order = ::GetVehicleOrder(GetVehicle(vehicle_id), order_position);
    00194   int32 value = order->GetConditionValue();
    00195   if (order->GetConditionVariable() == OCV_MAX_SPEED) value = value * 16 / 10;
    00196   return value;
    00197 }
    00198
    00199 /* static */ bool AIOrder::SetOrderJumpTo(VehicleID vehicle_id, OrderPosition order_position, OrderPosition jump_to)
    00200 {
    00201   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
    00202   EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
    00203   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, jump_to) && jump_to != ORDER_CURRENT);
    00204
    00205   return AIObject::DoCommand(0, vehicle_id | (order_position << 16), MOF_COND_DESTINATION | (jump_to << 4), CMD_MODIFY_ORDER);
    00206 }
    00207
    00208 /* static */ bool AIOrder::SetOrderCondition(VehicleID vehicle_id, OrderPosition order_position, OrderCondition condition)
    00209 {
    00210   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
    00211   EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
    00212   EnforcePrecondition(false, condition >= OC_LOAD_PERCENTAGE && condition <= OC_UNCONDITIONALLY);
    00213
    00214   return AIObject::DoCommand(0, vehicle_id | (order_position << 16), MOF_COND_VARIABLE | (condition << 4), CMD_MODIFY_ORDER);
    00215 }
    00216
    00217 /* static */ bool AIOrder::SetOrderCompareFunction(VehicleID vehicle_id, OrderPosition order_position, CompareFunction compare)
    00218 {
    00219   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
    00220   EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
    00221   EnforcePrecondition(false, compare >= CF_EQUALS && compare <= CF_IS_FALSE);
    00222
    00223   return AIObject::DoCommand(0, vehicle_id | (order_position << 16), MOF_COND_COMPARATOR | (compare << 4), CMD_MODIFY_ORDER);
    00224 }
    00225
    00226 /* static */ bool AIOrder::SetOrderCompareValue(VehicleID vehicle_id, OrderPosition order_position, int32 value)
    00227 {
    00228   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
    00229   EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
    00230   EnforcePrecondition(false, value >= 0 && value < 2048);
    00231   if (GetOrderCondition(vehicle_id, order_position) == OC_MAX_SPEED) value = value * 10 / 16;
    00232
    00233   return AIObject::DoCommand(0, vehicle_id | (order_position << 16), MOF_COND_VALUE | (value << 4), CMD_MODIFY_ORDER);
    00234 }
    00235
    00236 /* static */ bool AIOrder::AppendOrder(VehicleID vehicle_id, TileIndex destination, AIOrderFlags order_flags)
    00237 {
    00238   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00239   EnforcePrecondition(false, AreOrderFlagsValid(destination, order_flags));
    00240
    00241   return InsertOrder(vehicle_id, (AIOrder::OrderPosition)::GetVehicle(vehicle_id)->GetNumOrders(), destination, order_flags);
    00242 }
    00243
    00244 /* static */ bool AIOrder::AppendConditionalOrder(VehicleID vehicle_id, OrderPosition jump_to)
    00245 {
    00246   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00247   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, jump_to));
    00248
    00249   return InsertConditionalOrder(vehicle_id, (AIOrder::OrderPosition)::GetVehicle(vehicle_id)->GetNumOrders(), jump_to);
    00250 }
    00251
    00252 /* static */ bool AIOrder::InsertOrder(VehicleID vehicle_id, OrderPosition order_position, TileIndex destination, AIOrder::AIOrderFlags order_flags)
    00253 {
    00254   /* IsValidVehicleOrder is not good enough because it does not allow appending. */
    00255   if (order_position == ORDER_CURRENT) order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
    00256
    00257   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00258   EnforcePrecondition(false, order_position >= 0 && order_position <= ::GetVehicle(vehicle_id)->GetNumOrders());
    00259   EnforcePrecondition(false, AreOrderFlagsValid(destination, order_flags));
    00260
    00261   Order order;
    00262   switch (::GetOrderTypeByTile(destination)) {
    00263     case OT_GOTO_DEPOT:
    00264       order.MakeGoToDepot(::GetDepotByTile(destination)->index, (OrderDepotTypeFlags)(ODTFB_PART_OF_ORDERS | ((order_flags & AIOF_SERVICE_IF_NEEDED) ? ODTFB_SERVICE : 0)));
    00265       break;
    00266
    00267     case OT_GOTO_STATION:
    00268       order.MakeGoToStation(::GetStationIndex(destination));
    00269       order.SetLoadType((OrderLoadFlags)GB(order_flags, 5, 3));
    00270       order.SetUnloadType((OrderUnloadFlags)GB(order_flags, 2, 3));
    00271       break;
    00272
    00273     case OT_GOTO_WAYPOINT:
    00274       order.MakeGoToWaypoint(::GetWaypointIndex(destination));
    00275       break;
    00276
    00277     default:
    00278       return false;
    00279   }
    00280
    00281   order.SetNonStopType((OrderNonStopFlags)GB(order_flags, 0, 2));
    00282
    00283   return AIObject::DoCommand(0, vehicle_id | (order_position << 16), order.Pack(), CMD_INSERT_ORDER);
    00284 }
    00285
    00286 /* static */ bool AIOrder::InsertConditionalOrder(VehicleID vehicle_id, OrderPosition order_position, OrderPosition jump_to)
    00287 {
    00288   /* IsValidVehicleOrder is not good enough because it does not allow appending. */
    00289   if (order_position == ORDER_CURRENT) order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
    00290
    00291   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00292   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, jump_to));
    00293
    00294   Order order;
    00295   order.MakeConditional(jump_to);
    00296
    00297   return AIObject::DoCommand(0, vehicle_id | (order_position << 16), order.Pack(), CMD_INSERT_ORDER);
    00298 }
    00299
    00300 /* static */ bool AIOrder::RemoveOrder(VehicleID vehicle_id, OrderPosition order_position)
    00301 {
    00302   order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
    00303
    00304   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
    00305
    00306   return AIObject::DoCommand(0, vehicle_id, order_position, CMD_DELETE_ORDER);
    00307 }
    00308
    00317 static void _DoCommandReturnSetOrderFlags(class AIInstance *instance)
    00318 {
    00319   AIObject::SetLastCommandRes(AIOrder::_SetOrderFlags());
    00320   AIInstance::DoCommandReturn(instance);
    00321 }
    00322
    00323 /* static */ bool AIOrder::_SetOrderFlags()
    00324 {
    00325   /* Make sure we don't go into an infinite loop */
    00326   int retry = AIObject::GetCallbackVariable(3) - 1;
    00327   if (retry < 0) {
    00328     DEBUG(ai, 0, "Possible infinite loop in SetOrderFlags() detected");
    00329     return false;
    00330   }
    00331   AIObject::SetCallbackVariable(3, retry);
    00332
    00333   VehicleID vehicle_id = (VehicleID)AIObject::GetCallbackVariable(0);
    00334   OrderPosition order_position = (OrderPosition)AIObject::GetCallbackVariable(1);
    00335   AIOrderFlags order_flags = (AIOrderFlags)AIObject::GetCallbackVariable(2);
    00336
    00337   order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
    00338
    00339   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
    00340   EnforcePrecondition(false, AreOrderFlagsValid(GetOrderDestination(vehicle_id, order_position), order_flags));
    00341
    00342   const Order *order = ::GetVehicleOrder(GetVehicle(vehicle_id), order_position);
    00343
    00344   AIOrderFlags current = GetOrderFlags(vehicle_id, order_position);
    00345
    00346   if ((current & AIOF_NON_STOP_FLAGS) != (order_flags & AIOF_NON_STOP_FLAGS)) {
    00347     return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_NON_STOP_FLAGS) << 4 | MOF_NON_STOP, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
    00348   }
    00349
    00350   switch (order->GetType()) {
    00351     case OT_GOTO_DEPOT:
    00352       if ((current & AIOF_SERVICE_IF_NEEDED) != (order_flags & AIOF_SERVICE_IF_NEEDED)) {
    00353         return AIObject::DoCommand(0, vehicle_id | (order_position << 16), MOF_DEPOT_ACTION, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
    00354       }
    00355       break;
    00356
    00357     case OT_GOTO_STATION:
    00358       if ((current & AIOF_UNLOAD_FLAGS) != (order_flags & AIOF_UNLOAD_FLAGS)) {
    00359         return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_UNLOAD_FLAGS) << 2 | MOF_UNLOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
    00360       }
    00361       if ((current & AIOF_LOAD_FLAGS) != (order_flags & AIOF_LOAD_FLAGS)) {
    00362         return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_LOAD_FLAGS) >> 1 | MOF_LOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
    00363       }
    00364       break;
    00365
    00366     default: break;
    00367   }
    00368
    00369   assert(GetOrderFlags(vehicle_id, order_position) == order_flags);
    00370
    00371   return true;
    00372 }
    00373
    00374 /* static */ bool AIOrder::SetOrderFlags(VehicleID vehicle_id, OrderPosition order_position, AIOrder::AIOrderFlags order_flags)
    00375 {
    00376   AIObject::SetCallbackVariable(0, vehicle_id);
    00377   AIObject::SetCallbackVariable(1, order_position);
    00378   AIObject::SetCallbackVariable(2, order_flags);
    00379   /* In case another client(s) change orders at the same time we could
    00380    * end in an infinite loop. This stops that from happening ever. */
    00381   AIObject::SetCallbackVariable(3, 8);
    00382   return AIOrder::_SetOrderFlags();
    00383 }
    00384
    00385 /* static */ bool AIOrder::ChangeOrder(VehicleID vehicle_id, OrderPosition order_position, AIOrder::AIOrderFlags order_flags)
    00386 {
    00387   AILog::Warning("AIOrder::ChangeOrder is deprecated and will be removed soon, please use AIOrder::SetOrderFlags instead.");
    00388   return SetOrderFlags(vehicle_id, order_position, order_flags);
    00389 }
    00390
    00391 /* static */ bool AIOrder::MoveOrder(VehicleID vehicle_id, OrderPosition order_position_move, OrderPosition order_position_target)
    00392 {
    00393   order_position_move   = AIOrder::ResolveOrderPosition(vehicle_id, order_position_move);
    00394   order_position_target = AIOrder::ResolveOrderPosition(vehicle_id, order_position_target);
    00395
    00396   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position_move));
    00397   EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position_target));
    00398
    00399   return AIObject::DoCommand(0, vehicle_id, order_position_move | (order_position_target << 16), CMD_MOVE_ORDER);
    00400 }
    00401
    00402 /* static */ bool AIOrder::CopyOrders(VehicleID vehicle_id, VehicleID main_vehicle_id)
    00403 {
    00404   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00405   EnforcePrecondition(false, AIVehicle::IsValidVehicle(main_vehicle_id));
    00406
    00407   return AIObject::DoCommand(0, vehicle_id | (main_vehicle_id << 16), CO_COPY, CMD_CLONE_ORDER);
    00408 }
    00409
    00410 /* static */ bool AIOrder::ShareOrders(VehicleID vehicle_id, VehicleID main_vehicle_id)
    00411 {
    00412   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00413   EnforcePrecondition(false, AIVehicle::IsValidVehicle(main_vehicle_id));
    00414
    00415   return AIObject::DoCommand(0, vehicle_id | (main_vehicle_id << 16), CO_SHARE, CMD_CLONE_ORDER);
    00416 }
    00417
    00418 /* static */ bool AIOrder::UnshareOrders(VehicleID vehicle_id)
    00419 {
    00420   EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
    00421
    00422   return AIObject::DoCommand(0, vehicle_id, CO_UNSHARE, CMD_CLONE_ORDER);
    00423 }