~lewiscawte/+junk/openttd-stable

« back to all changes in this revision

Viewing changes to src/ai/api/ai_order.cpp

  • Committer: Lewis Cawte
  • Date: 2011-06-11 09:45:09 UTC
  • Revision ID: lewis@dev.lewiscawte.info-20110611094509-c5dqw2ot6pb9t10i
Adding stable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: ai_order.cpp 22520 2011-05-29 17:55:15Z rubidium $ */
 
2
 
 
3
/*
 
4
 * This file is part of OpenTTD.
 
5
 * 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.
 
6
 * 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.
 
7
 * 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/>.
 
8
 */
 
9
 
 
10
/** @file ai_order.cpp Implementation of AIOrder. */
 
11
 
 
12
#include "../../stdafx.h"
 
13
#include "ai_order.hpp"
 
14
#include "ai_vehicle.hpp"
 
15
#include "../ai_instance.hpp"
 
16
#include "../../debug.h"
 
17
#include "../../vehicle_base.h"
 
18
#include "../../roadstop_base.h"
 
19
#include "../../depot_base.h"
 
20
#include "../../station_base.h"
 
21
#include "../../waypoint_base.h"
 
22
 
 
23
/**
 
24
 * Gets the order type given a tile
 
25
 * @param t the tile to get the order from
 
26
 * @return the order type, or OT_END when there is no order
 
27
 */
 
28
static OrderType GetOrderTypeByTile(TileIndex t)
 
29
{
 
30
        if (!::IsValidTile(t)) return OT_END;
 
31
 
 
32
        switch (::GetTileType(t)) {
 
33
                default: break;
 
34
                case MP_STATION:
 
35
                        if (IsBuoy(t) || IsRailWaypoint(t)) return OT_GOTO_WAYPOINT;
 
36
                        if (IsHangar(t)) return OT_GOTO_DEPOT;
 
37
                        return OT_GOTO_STATION;
 
38
 
 
39
                case MP_WATER:   if (::IsShipDepot(t)) return OT_GOTO_DEPOT; break;
 
40
                case MP_ROAD:    if (::GetRoadTileType(t) == ROAD_TILE_DEPOT) return OT_GOTO_DEPOT; break;
 
41
                case MP_RAILWAY:
 
42
                        if (IsRailDepot(t)) return OT_GOTO_DEPOT;
 
43
                        break;
 
44
        }
 
45
 
 
46
        return OT_END;
 
47
}
 
48
 
 
49
/* static */ bool AIOrder::IsValidVehicleOrder(VehicleID vehicle_id, OrderPosition order_position)
 
50
{
 
51
        return AIVehicle::IsValidVehicle(vehicle_id) && order_position >= 0 && (order_position < ::Vehicle::Get(vehicle_id)->GetNumManualOrders() || order_position == ORDER_CURRENT);
 
52
}
 
53
 
 
54
/**
 
55
 * Get the current order the vehicle is executing. If the current order is in
 
56
 *  the order list, return the order from the orderlist. If the current order
 
57
 *  was a manual order, return the current order.
 
58
 */
 
59
static const Order *ResolveOrder(VehicleID vehicle_id, AIOrder::OrderPosition order_position)
 
60
{
 
61
        const Vehicle *v = ::Vehicle::Get(vehicle_id);
 
62
        if (order_position == AIOrder::ORDER_CURRENT) {
 
63
                const Order *order = &v->current_order;
 
64
                if (order->GetType() == OT_GOTO_DEPOT && !(order->GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) return order;
 
65
                order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
 
66
                if (order_position == AIOrder::ORDER_INVALID) return NULL;
 
67
        }
 
68
        const Order *order = v->orders.list->GetFirstOrder();
 
69
        while (order->GetType() == OT_IMPLICIT) order = order->next;
 
70
        while (order_position > 0) {
 
71
                order_position = (AIOrder::OrderPosition)(order_position - 1);
 
72
                order = order->next;
 
73
                while (order->GetType() == OT_IMPLICIT) order = order->next;
 
74
        }
 
75
        return order;
 
76
}
 
77
 
 
78
/* static */ bool AIOrder::IsGotoStationOrder(VehicleID vehicle_id, OrderPosition order_position)
 
79
{
 
80
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
 
81
 
 
82
        const Order *order = ::ResolveOrder(vehicle_id, order_position);
 
83
        return order != NULL && order->GetType() == OT_GOTO_STATION;
 
84
}
 
85
 
 
86
/* static */ bool AIOrder::IsGotoDepotOrder(VehicleID vehicle_id, OrderPosition order_position)
 
87
{
 
88
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
 
89
 
 
90
        const Order *order = ::ResolveOrder(vehicle_id, order_position);
 
91
        return order != NULL && order->GetType() == OT_GOTO_DEPOT;
 
92
}
 
93
 
 
94
/* static */ bool AIOrder::IsGotoWaypointOrder(VehicleID vehicle_id, OrderPosition order_position)
 
95
{
 
96
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
 
97
 
 
98
        const Order *order = ::ResolveOrder(vehicle_id, order_position);
 
99
        return order != NULL && order->GetType() == OT_GOTO_WAYPOINT;
 
100
}
 
101
 
 
102
/* static */ bool AIOrder::IsConditionalOrder(VehicleID vehicle_id, OrderPosition order_position)
 
103
{
 
104
        if (order_position == ORDER_CURRENT) return false;
 
105
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
 
106
 
 
107
        const Order *order = Vehicle::Get(vehicle_id)->GetOrder(order_position);
 
108
        return order->GetType() == OT_CONDITIONAL;
 
109
}
 
110
 
 
111
/* static */ bool AIOrder::IsVoidOrder(VehicleID vehicle_id, OrderPosition order_position)
 
112
{
 
113
        if (order_position == ORDER_CURRENT) return false;
 
114
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
 
115
 
 
116
        const Order *order = Vehicle::Get(vehicle_id)->GetOrder(order_position);
 
117
        return order->GetType() == OT_DUMMY;
 
118
}
 
119
 
 
120
/* static */ bool AIOrder::IsCurrentOrderPartOfOrderList(VehicleID vehicle_id)
 
121
{
 
122
        if (AIVehicle::IsValidVehicle(vehicle_id)) return false;
 
123
        if (GetOrderCount(vehicle_id) == 0) return false;
 
124
 
 
125
        const Order *order = &::Vehicle::Get(vehicle_id)->current_order;
 
126
        if (order->GetType() != OT_GOTO_DEPOT) return true;
 
127
        return (order->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0;
 
128
}
 
129
 
 
130
/* static */ AIOrder::OrderPosition AIOrder::ResolveOrderPosition(VehicleID vehicle_id, OrderPosition order_position)
 
131
{
 
132
        if (!AIVehicle::IsValidVehicle(vehicle_id)) return ORDER_INVALID;
 
133
 
 
134
        if (order_position == ORDER_CURRENT) {
 
135
                int cur_order_pos = ::Vehicle::Get(vehicle_id)->cur_real_order_index;
 
136
                const Order *order = ::Vehicle::Get(vehicle_id)->GetOrder(0);
 
137
                if (order == NULL) return ORDER_INVALID;
 
138
                int num_implicit_orders = 0;
 
139
                for (int i = 0; i < cur_order_pos; i++) {
 
140
                        if (order->GetType() == OT_IMPLICIT) num_implicit_orders++;
 
141
                        order = order->next;
 
142
                }
 
143
                return (AIOrder::OrderPosition)(cur_order_pos - num_implicit_orders);
 
144
        }
 
145
        return (order_position >= 0 && order_position < ::Vehicle::Get(vehicle_id)->GetNumManualOrders()) ? order_position : ORDER_INVALID;
 
146
}
 
147
 
 
148
 
 
149
/* static */ bool AIOrder::AreOrderFlagsValid(TileIndex destination, AIOrderFlags order_flags)
 
150
{
 
151
        OrderType ot = (order_flags & AIOF_GOTO_NEAREST_DEPOT) ? OT_GOTO_DEPOT : ::GetOrderTypeByTile(destination);
 
152
        switch (ot) {
 
153
                case OT_GOTO_STATION:
 
154
                        return (order_flags & ~(AIOF_NON_STOP_FLAGS | AIOF_UNLOAD_FLAGS | AIOF_LOAD_FLAGS)) == 0 &&
 
155
                                        /* Test the different mutual exclusive flags. */
 
156
                                        ((order_flags & AIOF_TRANSFER)      == 0 || (order_flags & AIOF_UNLOAD)    == 0) &&
 
157
                                        ((order_flags & AIOF_TRANSFER)      == 0 || (order_flags & AIOF_NO_UNLOAD) == 0) &&
 
158
                                        ((order_flags & AIOF_UNLOAD)        == 0 || (order_flags & AIOF_NO_UNLOAD) == 0) &&
 
159
                                        ((order_flags & AIOF_UNLOAD)        == 0 || (order_flags & AIOF_NO_UNLOAD) == 0) &&
 
160
                                        ((order_flags & AIOF_NO_UNLOAD)     == 0 || (order_flags & AIOF_NO_LOAD)   == 0) &&
 
161
                                        ((order_flags & AIOF_FULL_LOAD_ANY) == 0 || (order_flags & AIOF_NO_LOAD)   == 0);
 
162
 
 
163
                case OT_GOTO_DEPOT:
 
164
                        return (order_flags & ~(AIOF_NON_STOP_FLAGS | AIOF_DEPOT_FLAGS)) == 0 &&
 
165
                                        ((order_flags & AIOF_SERVICE_IF_NEEDED) == 0 || (order_flags & AIOF_STOP_IN_DEPOT) == 0);
 
166
 
 
167
                case OT_GOTO_WAYPOINT: return (order_flags & ~(AIOF_NON_STOP_FLAGS)) == 0;
 
168
                default:               return false;
 
169
        }
 
170
}
 
171
 
 
172
/* static */ bool AIOrder::IsValidConditionalOrder(OrderCondition condition, CompareFunction compare)
 
173
{
 
174
        switch (condition) {
 
175
                case OC_LOAD_PERCENTAGE:
 
176
                case OC_RELIABILITY:
 
177
                case OC_MAX_SPEED:
 
178
                case OC_AGE:
 
179
                        return compare >= CF_EQUALS && compare <= CF_MORE_EQUALS;
 
180
 
 
181
                case OC_REQUIRES_SERVICE:
 
182
                        return compare == CF_IS_TRUE || compare == CF_IS_FALSE;
 
183
 
 
184
                case OC_UNCONDITIONALLY:
 
185
                        return true;
 
186
 
 
187
                default: return false;
 
188
        }
 
189
}
 
190
 
 
191
/* static */ int32 AIOrder::GetOrderCount(VehicleID vehicle_id)
 
192
{
 
193
        return AIVehicle::IsValidVehicle(vehicle_id) ? ::Vehicle::Get(vehicle_id)->GetNumOrders() : -1;
 
194
}
 
195
 
 
196
/* static */ TileIndex AIOrder::GetOrderDestination(VehicleID vehicle_id, OrderPosition order_position)
 
197
{
 
198
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return INVALID_TILE;
 
199
 
 
200
        const Order *order = ::ResolveOrder(vehicle_id, order_position);
 
201
        if (order == NULL || order->GetType() == OT_CONDITIONAL) return INVALID_TILE;
 
202
        const Vehicle *v = ::Vehicle::Get(vehicle_id);
 
203
 
 
204
        switch (order->GetType()) {
 
205
                case OT_GOTO_DEPOT: {
 
206
                        /* We don't know where the nearest depot is... (yet) */
 
207
                        if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) return INVALID_TILE;
 
208
 
 
209
                        if (v->type != VEH_AIRCRAFT) return ::Depot::Get(order->GetDestination())->xy;
 
210
                        /* Aircraft's hangars are referenced by StationID, not DepotID */
 
211
                        const Station *st = ::Station::Get(order->GetDestination());
 
212
                        if (!st->airport.HasHangar()) return INVALID_TILE;
 
213
                        return st->airport.GetHangarTile(0);
 
214
                }
 
215
 
 
216
                case OT_GOTO_STATION: {
 
217
                        const Station *st = ::Station::Get(order->GetDestination());
 
218
                        if (st->train_station.tile != INVALID_TILE) {
 
219
                                TILE_AREA_LOOP(t, st->train_station) {
 
220
                                        if (st->TileBelongsToRailStation(t)) return t;
 
221
                                }
 
222
                        } else if (st->dock_tile != INVALID_TILE) {
 
223
                                return st->dock_tile;
 
224
                        } else if (st->bus_stops != NULL) {
 
225
                                return st->bus_stops->xy;
 
226
                        } else if (st->truck_stops != NULL) {
 
227
                                return st->truck_stops->xy;
 
228
                        } else if (st->airport.tile != INVALID_TILE) {
 
229
                                TILE_AREA_LOOP(tile, st->airport) {
 
230
                                        if (st->TileBelongsToAirport(tile) && !::IsHangar(tile)) return tile;
 
231
                                }
 
232
                        }
 
233
                        return INVALID_TILE;
 
234
                }
 
235
 
 
236
                case OT_GOTO_WAYPOINT: {
 
237
                        const Waypoint *wp = ::Waypoint::Get(order->GetDestination());
 
238
                        if (wp->train_station.tile != INVALID_TILE) {
 
239
                                TILE_AREA_LOOP(t, wp->train_station) {
 
240
                                        if (wp->TileBelongsToRailStation(t)) return t;
 
241
                                }
 
242
                        }
 
243
                        /* If the waypoint has no rail waypoint tiles, it must have a buoy */
 
244
                        return wp->xy;
 
245
                }
 
246
                default:               return INVALID_TILE;
 
247
        }
 
248
}
 
249
 
 
250
/* static */ AIOrder::AIOrderFlags AIOrder::GetOrderFlags(VehicleID vehicle_id, OrderPosition order_position)
 
251
{
 
252
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return AIOF_INVALID;
 
253
 
 
254
        const Order *order = ::ResolveOrder(vehicle_id, order_position);
 
255
        if (order == NULL || order->GetType() == OT_CONDITIONAL || order->GetType() == OT_DUMMY) return AIOF_INVALID;
 
256
 
 
257
        AIOrderFlags order_flags = AIOF_NONE;
 
258
        order_flags |= (AIOrderFlags)order->GetNonStopType();
 
259
        switch (order->GetType()) {
 
260
                case OT_GOTO_DEPOT:
 
261
                        if (order->GetDepotOrderType() & ODTFB_SERVICE) order_flags |= AIOF_SERVICE_IF_NEEDED;
 
262
                        if (order->GetDepotActionType() & ODATFB_HALT) order_flags |= AIOF_STOP_IN_DEPOT;
 
263
                        if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) order_flags |= AIOF_GOTO_NEAREST_DEPOT;
 
264
                        break;
 
265
 
 
266
                case OT_GOTO_STATION:
 
267
                        order_flags |= (AIOrderFlags)(order->GetLoadType()   << 5);
 
268
                        order_flags |= (AIOrderFlags)(order->GetUnloadType() << 2);
 
269
                        break;
 
270
 
 
271
                default: break;
 
272
        }
 
273
 
 
274
        return order_flags;
 
275
}
 
276
 
 
277
/* static */ AIOrder::OrderPosition AIOrder::GetOrderJumpTo(VehicleID vehicle_id, OrderPosition order_position)
 
278
{
 
279
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return ORDER_INVALID;
 
280
        if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return ORDER_INVALID;
 
281
 
 
282
        const Order *order = ::ResolveOrder(vehicle_id, order_position);
 
283
        return (OrderPosition)order->GetConditionSkipToOrder();
 
284
}
 
285
 
 
286
/* static */ AIOrder::OrderCondition AIOrder::GetOrderCondition(VehicleID vehicle_id, OrderPosition order_position)
 
287
{
 
288
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return OC_INVALID;
 
289
        if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return OC_INVALID;
 
290
 
 
291
        const Order *order = ::ResolveOrder(vehicle_id, order_position);
 
292
        return (OrderCondition)order->GetConditionVariable();
 
293
}
 
294
 
 
295
/* static */ AIOrder::CompareFunction AIOrder::GetOrderCompareFunction(VehicleID vehicle_id, OrderPosition order_position)
 
296
{
 
297
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return CF_INVALID;
 
298
        if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return CF_INVALID;
 
299
 
 
300
        const Order *order = ::ResolveOrder(vehicle_id, order_position);
 
301
        return (CompareFunction)order->GetConditionComparator();
 
302
}
 
303
 
 
304
/* static */ int32 AIOrder::GetOrderCompareValue(VehicleID vehicle_id, OrderPosition order_position)
 
305
{
 
306
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return -1;
 
307
        if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return -1;
 
308
 
 
309
        const Order *order = ::ResolveOrder(vehicle_id, order_position);
 
310
        int32 value = order->GetConditionValue();
 
311
        if (order->GetConditionVariable() == OCV_MAX_SPEED) value = value * 16 / 10;
 
312
        return value;
 
313
}
 
314
 
 
315
/* static */ AIOrder::StopLocation AIOrder::GetStopLocation(VehicleID vehicle_id, OrderPosition order_position)
 
316
{
 
317
        if (!IsValidVehicleOrder(vehicle_id, order_position)) return STOPLOCATION_INVALID;
 
318
        if (AIVehicle::GetVehicleType(vehicle_id) != AIVehicle::VT_RAIL) return STOPLOCATION_INVALID;
 
319
        if (!IsGotoStationOrder(vehicle_id, order_position)) return STOPLOCATION_INVALID;
 
320
 
 
321
        const Order *order = ::ResolveOrder(vehicle_id, order_position);
 
322
        return (AIOrder::StopLocation)order->GetStopLocation();
 
323
}
 
324
 
 
325
/* static */ bool AIOrder::SetOrderJumpTo(VehicleID vehicle_id, OrderPosition order_position, OrderPosition jump_to)
 
326
{
 
327
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
 
328
        EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
 
329
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, jump_to) && jump_to != ORDER_CURRENT);
 
330
 
 
331
        return AIObject::DoCommand(0, vehicle_id | (order_position << 20), MOF_COND_DESTINATION | (jump_to << 4), CMD_MODIFY_ORDER);
 
332
}
 
333
 
 
334
/* static */ bool AIOrder::SetOrderCondition(VehicleID vehicle_id, OrderPosition order_position, OrderCondition condition)
 
335
{
 
336
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
 
337
        EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
 
338
        EnforcePrecondition(false, condition >= OC_LOAD_PERCENTAGE && condition <= OC_UNCONDITIONALLY);
 
339
 
 
340
        return AIObject::DoCommand(0, vehicle_id | (order_position << 20), MOF_COND_VARIABLE | (condition << 4), CMD_MODIFY_ORDER);
 
341
}
 
342
 
 
343
/* static */ bool AIOrder::SetOrderCompareFunction(VehicleID vehicle_id, OrderPosition order_position, CompareFunction compare)
 
344
{
 
345
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
 
346
        EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
 
347
        EnforcePrecondition(false, compare >= CF_EQUALS && compare <= CF_IS_FALSE);
 
348
 
 
349
        return AIObject::DoCommand(0, vehicle_id | (order_position << 20), MOF_COND_COMPARATOR | (compare << 4), CMD_MODIFY_ORDER);
 
350
}
 
351
 
 
352
/* static */ bool AIOrder::SetOrderCompareValue(VehicleID vehicle_id, OrderPosition order_position, int32 value)
 
353
{
 
354
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
 
355
        EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
 
356
        EnforcePrecondition(false, value >= 0 && value < 2048);
 
357
        if (GetOrderCondition(vehicle_id, order_position) == OC_MAX_SPEED) value = value * 10 / 16;
 
358
 
 
359
        return AIObject::DoCommand(0, vehicle_id | (order_position << 20), MOF_COND_VALUE | (value << 4), CMD_MODIFY_ORDER);
 
360
}
 
361
 
 
362
/* static */ bool AIOrder::SetStopLocation(VehicleID vehicle_id, OrderPosition order_position, StopLocation stop_location)
 
363
{
 
364
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
 
365
        EnforcePrecondition(false, AIVehicle::GetVehicleType(vehicle_id) == AIVehicle::VT_RAIL);
 
366
        EnforcePrecondition(false, IsGotoStationOrder(vehicle_id, order_position));
 
367
        EnforcePrecondition(false, stop_location >= STOPLOCATION_NEAR && stop_location <= STOPLOCATION_FAR);
 
368
 
 
369
        uint32 p1 = vehicle_id | (order_position << 20);
 
370
        uint32 p2 = MOF_STOP_LOCATION | (stop_location << 4);
 
371
        return AIObject::DoCommand(0, p1, p2, CMD_MODIFY_ORDER);
 
372
}
 
373
 
 
374
/* static */ bool AIOrder::AppendOrder(VehicleID vehicle_id, TileIndex destination, AIOrderFlags order_flags)
 
375
{
 
376
        EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
 
377
        EnforcePrecondition(false, AreOrderFlagsValid(destination, order_flags));
 
378
 
 
379
        return InsertOrder(vehicle_id, (AIOrder::OrderPosition)::Vehicle::Get(vehicle_id)->GetNumOrders(), destination, order_flags);
 
380
}
 
381
 
 
382
/* static */ bool AIOrder::AppendConditionalOrder(VehicleID vehicle_id, OrderPosition jump_to)
 
383
{
 
384
        EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
 
385
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, jump_to));
 
386
 
 
387
        return InsertConditionalOrder(vehicle_id, (AIOrder::OrderPosition)::Vehicle::Get(vehicle_id)->GetNumOrders(), jump_to);
 
388
}
 
389
 
 
390
/* static */ bool AIOrder::InsertOrder(VehicleID vehicle_id, OrderPosition order_position, TileIndex destination, AIOrder::AIOrderFlags order_flags)
 
391
{
 
392
        /* IsValidVehicleOrder is not good enough because it does not allow appending. */
 
393
        if (order_position == ORDER_CURRENT) order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
 
394
 
 
395
        EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
 
396
        EnforcePrecondition(false, order_position >= 0 && order_position <= ::Vehicle::Get(vehicle_id)->GetNumOrders());
 
397
        EnforcePrecondition(false, AreOrderFlagsValid(destination, order_flags));
 
398
 
 
399
        Order order;
 
400
        OrderType ot = (order_flags & AIOF_GOTO_NEAREST_DEPOT) ? OT_GOTO_DEPOT : ::GetOrderTypeByTile(destination);
 
401
        switch (ot) {
 
402
                case OT_GOTO_DEPOT: {
 
403
                        OrderDepotTypeFlags odtf = (OrderDepotTypeFlags)(ODTFB_PART_OF_ORDERS | ((order_flags & AIOF_SERVICE_IF_NEEDED) ? ODTFB_SERVICE : 0));
 
404
                        OrderDepotActionFlags odaf = (OrderDepotActionFlags)(ODATF_SERVICE_ONLY | ((order_flags & AIOF_STOP_IN_DEPOT) ? ODATFB_HALT : 0));
 
405
                        if (order_flags & AIOF_GOTO_NEAREST_DEPOT) odaf |= ODATFB_NEAREST_DEPOT;
 
406
                        OrderNonStopFlags onsf = (OrderNonStopFlags)((order_flags & AIOF_NON_STOP_INTERMEDIATE) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
 
407
                        if (order_flags & AIOF_GOTO_NEAREST_DEPOT) {
 
408
                                order.MakeGoToDepot(0, odtf, onsf, odaf);
 
409
                        } else {
 
410
                                /* Check explicitly if the order is to a station (for aircraft) or
 
411
                                 * to a depot (other vehicle types). */
 
412
                                if (::Vehicle::Get(vehicle_id)->type == VEH_AIRCRAFT) {
 
413
                                        if (!::IsTileType(destination, MP_STATION)) return false;
 
414
                                        order.MakeGoToDepot(::GetStationIndex(destination), odtf, onsf, odaf);
 
415
                                } else {
 
416
                                        if (::IsTileType(destination, MP_STATION)) return false;
 
417
                                        order.MakeGoToDepot(::GetDepotIndex(destination), odtf, onsf, odaf);
 
418
                                }
 
419
                        }
 
420
                        break;
 
421
                }
 
422
 
 
423
                case OT_GOTO_STATION:
 
424
                        order.MakeGoToStation(::GetStationIndex(destination));
 
425
                        order.SetLoadType((OrderLoadFlags)GB(order_flags, 5, 3));
 
426
                        order.SetUnloadType((OrderUnloadFlags)GB(order_flags, 2, 3));
 
427
                        order.SetStopLocation(OSL_PLATFORM_FAR_END);
 
428
                        break;
 
429
 
 
430
                case OT_GOTO_WAYPOINT:
 
431
                        order.MakeGoToWaypoint(::GetStationIndex(destination));
 
432
                        break;
 
433
 
 
434
                default:
 
435
                        return false;
 
436
        }
 
437
 
 
438
        order.SetNonStopType((OrderNonStopFlags)GB(order_flags, 0, 2));
 
439
 
 
440
        return AIObject::DoCommand(0, vehicle_id | (order_position << 20), order.Pack(), CMD_INSERT_ORDER);
 
441
}
 
442
 
 
443
/* static */ bool AIOrder::InsertConditionalOrder(VehicleID vehicle_id, OrderPosition order_position, OrderPosition jump_to)
 
444
{
 
445
        /* IsValidVehicleOrder is not good enough because it does not allow appending. */
 
446
        if (order_position == ORDER_CURRENT) order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
 
447
 
 
448
        EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
 
449
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, jump_to));
 
450
 
 
451
        Order order;
 
452
        order.MakeConditional(jump_to);
 
453
 
 
454
        return AIObject::DoCommand(0, vehicle_id | (order_position << 20), order.Pack(), CMD_INSERT_ORDER);
 
455
}
 
456
 
 
457
/* static */ bool AIOrder::RemoveOrder(VehicleID vehicle_id, OrderPosition order_position)
 
458
{
 
459
        order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
 
460
 
 
461
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
 
462
 
 
463
        return AIObject::DoCommand(0, vehicle_id, order_position, CMD_DELETE_ORDER);
 
464
}
 
465
 
 
466
/* static */ bool AIOrder::SkipToOrder(VehicleID vehicle_id, OrderPosition next_order)
 
467
{
 
468
        next_order = AIOrder::ResolveOrderPosition(vehicle_id, next_order);
 
469
 
 
470
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, next_order));
 
471
 
 
472
        return AIObject::DoCommand(0, vehicle_id, next_order, CMD_SKIP_TO_ORDER);
 
473
}
 
474
 
 
475
/**
 
476
 * Callback handler as SetOrderFlags possibly needs multiple DoCommand calls
 
477
 * to be able to set all order flags correctly. As we need to wait till the
 
478
 * command has completed before we know the next bits to change we need to
 
479
 * call the function multiple times. Each time it'll reduce the difference
 
480
 * between the wanted and the current order.
 
481
 * @param instance The AI we are doing the callback for.
 
482
 */
 
483
static void _DoCommandReturnSetOrderFlags(class AIInstance *instance)
 
484
{
 
485
        AIObject::SetLastCommandRes(AIOrder::_SetOrderFlags());
 
486
        AIInstance::DoCommandReturn(instance);
 
487
}
 
488
 
 
489
/* static */ bool AIOrder::_SetOrderFlags()
 
490
{
 
491
        /* Make sure we don't go into an infinite loop */
 
492
        int retry = AIObject::GetCallbackVariable(3) - 1;
 
493
        if (retry < 0) {
 
494
                DEBUG(ai, 0, "Possible infinite loop in SetOrderFlags() detected");
 
495
                return false;
 
496
        }
 
497
        AIObject::SetCallbackVariable(3, retry);
 
498
 
 
499
        VehicleID vehicle_id = (VehicleID)AIObject::GetCallbackVariable(0);
 
500
        OrderPosition order_position = (OrderPosition)AIObject::GetCallbackVariable(1);
 
501
        AIOrderFlags order_flags = (AIOrderFlags)AIObject::GetCallbackVariable(2);
 
502
 
 
503
        order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
 
504
 
 
505
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
 
506
        EnforcePrecondition(false, AreOrderFlagsValid(GetOrderDestination(vehicle_id, order_position), order_flags));
 
507
 
 
508
        const Order *order = ::ResolveOrder(vehicle_id, order_position);
 
509
 
 
510
        AIOrderFlags current = GetOrderFlags(vehicle_id, order_position);
 
511
 
 
512
        EnforcePrecondition(false, (order_flags & AIOF_GOTO_NEAREST_DEPOT) == (current & AIOF_GOTO_NEAREST_DEPOT));
 
513
 
 
514
        if ((current & AIOF_NON_STOP_FLAGS) != (order_flags & AIOF_NON_STOP_FLAGS)) {
 
515
                return AIObject::DoCommand(0, vehicle_id | (order_position << 20), (order_flags & AIOF_NON_STOP_FLAGS) << 4 | MOF_NON_STOP, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
 
516
        }
 
517
 
 
518
        switch (order->GetType()) {
 
519
                case OT_GOTO_DEPOT:
 
520
                        if ((current & AIOF_DEPOT_FLAGS) != (order_flags & AIOF_DEPOT_FLAGS)) {
 
521
                                uint data = DA_ALWAYS_GO;
 
522
                                if (order_flags & AIOF_SERVICE_IF_NEEDED) data = DA_SERVICE;
 
523
                                if (order_flags & AIOF_STOP_IN_DEPOT) data = DA_STOP;
 
524
                                return AIObject::DoCommand(0, vehicle_id | (order_position << 20), (data << 4) | MOF_DEPOT_ACTION, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
 
525
                        }
 
526
                        break;
 
527
 
 
528
                case OT_GOTO_STATION:
 
529
                        if ((current & AIOF_UNLOAD_FLAGS) != (order_flags & AIOF_UNLOAD_FLAGS)) {
 
530
                                return AIObject::DoCommand(0, vehicle_id | (order_position << 20), (order_flags & AIOF_UNLOAD_FLAGS) << 2 | MOF_UNLOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
 
531
                        }
 
532
                        if ((current & AIOF_LOAD_FLAGS) != (order_flags & AIOF_LOAD_FLAGS)) {
 
533
                                return AIObject::DoCommand(0, vehicle_id | (order_position << 20), (order_flags & AIOF_LOAD_FLAGS) >> 1 | MOF_LOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
 
534
                        }
 
535
                        break;
 
536
 
 
537
                default: break;
 
538
        }
 
539
 
 
540
        assert(GetOrderFlags(vehicle_id, order_position) == order_flags);
 
541
 
 
542
        return true;
 
543
}
 
544
 
 
545
/* static */ bool AIOrder::SetOrderFlags(VehicleID vehicle_id, OrderPosition order_position, AIOrder::AIOrderFlags order_flags)
 
546
{
 
547
        AIObject::SetCallbackVariable(0, vehicle_id);
 
548
        AIObject::SetCallbackVariable(1, order_position);
 
549
        AIObject::SetCallbackVariable(2, order_flags);
 
550
        /* In case another client(s) change orders at the same time we could
 
551
         * end in an infinite loop. This stops that from happening ever. */
 
552
        AIObject::SetCallbackVariable(3, 8);
 
553
        return AIOrder::_SetOrderFlags();
 
554
}
 
555
 
 
556
/* static */ bool AIOrder::MoveOrder(VehicleID vehicle_id, OrderPosition order_position_move, OrderPosition order_position_target)
 
557
{
 
558
        order_position_move   = AIOrder::ResolveOrderPosition(vehicle_id, order_position_move);
 
559
        order_position_target = AIOrder::ResolveOrderPosition(vehicle_id, order_position_target);
 
560
 
 
561
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position_move));
 
562
        EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position_target));
 
563
 
 
564
        return AIObject::DoCommand(0, vehicle_id, order_position_move | (order_position_target << 16), CMD_MOVE_ORDER);
 
565
}
 
566
 
 
567
/* static */ bool AIOrder::CopyOrders(VehicleID vehicle_id, VehicleID main_vehicle_id)
 
568
{
 
569
        EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
 
570
        EnforcePrecondition(false, AIVehicle::IsValidVehicle(main_vehicle_id));
 
571
 
 
572
        return AIObject::DoCommand(0, vehicle_id | CO_COPY << 30, main_vehicle_id, CMD_CLONE_ORDER);
 
573
}
 
574
 
 
575
/* static */ bool AIOrder::ShareOrders(VehicleID vehicle_id, VehicleID main_vehicle_id)
 
576
{
 
577
        EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
 
578
        EnforcePrecondition(false, AIVehicle::IsValidVehicle(main_vehicle_id));
 
579
 
 
580
        return AIObject::DoCommand(0, vehicle_id | CO_SHARE << 30, main_vehicle_id, CMD_CLONE_ORDER);
 
581
}
 
582
 
 
583
/* static */ bool AIOrder::UnshareOrders(VehicleID vehicle_id)
 
584
{
 
585
        EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
 
586
 
 
587
        return AIObject::DoCommand(0, vehicle_id | CO_UNSHARE << 30, 0, CMD_CLONE_ORDER);
 
588
}