4 #include "vehicle.h" |
4 #include "vehicle.h" |
5 #include "command.h" |
5 #include "command.h" |
6 #include "station.h" |
6 #include "station.h" |
7 #include "player.h" |
7 #include "player.h" |
8 #include "news.h" |
8 #include "news.h" |
9 |
9 #include "saveload.h" |
10 /* p1 & 0xFFFF = vehicle |
10 |
11 * p1 >> 16 = index in order list |
11 /** |
12 * p2 = order command to insert |
12 * |
13 */ |
13 * Unpacks a order from savegames made with TTD(Patch) |
14 int32 CmdInsertOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
14 * |
15 { |
15 */ |
16 Vehicle *v = GetVehicle(p1 & 0xFFFF); |
16 Order UnpackOldOrder(uint16 packed) |
17 int sel = p1 >> 16; |
17 { |
18 Order new_order = UnpackOrder(p2); |
18 Order order; |
19 |
19 order.type = (packed & 0x000F); |
20 if (sel > v->num_orders) return_cmd_error(STR_EMPTY); |
20 order.flags = (packed & 0x00F0) >> 4; |
21 if (_ptr_to_next_order == endof(_order_array)) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); |
21 order.station = (packed & 0xFF00) >> 8; |
22 if (v->num_orders >= 40) return_cmd_error(STR_8832_TOO_MANY_ORDERS); |
22 order.next = NULL; |
23 |
23 |
24 // for ships, make sure that the station is not too far away from the previous destination. |
24 // Sanity check |
|
25 // TTD stores invalid orders as OT_NOTHING with non-zero flags/station |
|
26 if (order.type == OT_NOTHING && (order.flags != 0 || order.station != 0)) { |
|
27 order.type = OT_DUMMY; |
|
28 order.flags = 0; |
|
29 } |
|
30 |
|
31 return order; |
|
32 } |
|
33 |
|
34 /** |
|
35 * |
|
36 * Unpacks a order from savegames with version 4 and lower |
|
37 * |
|
38 */ |
|
39 Order UnpackVersion4Order(uint16 packed) |
|
40 { |
|
41 Order order; |
|
42 order.type = (packed & 0x000F); |
|
43 order.flags = (packed & 0x00F0) >> 4; |
|
44 order.station = (packed & 0xFF00) >> 8; |
|
45 order.next = NULL; |
|
46 return order; |
|
47 } |
|
48 |
|
49 /** |
|
50 * |
|
51 * Updates the widgets of a vehicle which contains the order-data |
|
52 * |
|
53 */ |
|
54 void InvalidateVehicleOrder(const Vehicle *v) |
|
55 { |
|
56 InvalidateWindow(WC_VEHICLE_VIEW, v->index); |
|
57 InvalidateWindow(WC_VEHICLE_ORDERS, v->index); |
|
58 } |
|
59 |
|
60 /** |
|
61 * |
|
62 * Swap two orders |
|
63 * |
|
64 */ |
|
65 static void SwapOrders(Order *order1, Order *order2) |
|
66 { |
|
67 Order temp_order; |
|
68 |
|
69 temp_order = *order1; |
|
70 *order1 = *order2; |
|
71 *order2 = temp_order; |
|
72 } |
|
73 |
|
74 /** |
|
75 * |
|
76 * Allocate a new order |
|
77 * |
|
78 * @return Order* if a free space is found, else NULL. |
|
79 * |
|
80 */ |
|
81 static Order *AllocateOrder() |
|
82 { |
|
83 Order *order; |
|
84 |
|
85 FOR_ALL_ORDERS(order) { |
|
86 if (order->type == OT_NOTHING) { |
|
87 uint index = order->index; |
|
88 memset(order, 0, sizeof(Order)); |
|
89 order->index = index; |
|
90 return order; |
|
91 } |
|
92 } |
|
93 |
|
94 return NULL; |
|
95 } |
|
96 |
|
97 /** |
|
98 * |
|
99 * Assign data to an order (from an other order) |
|
100 * This function makes sure that the index is maintained correctly |
|
101 * |
|
102 */ |
|
103 void AssignOrder(Order *order, Order data) |
|
104 { |
|
105 order->type = data.type; |
|
106 order->flags = data.flags; |
|
107 order->station = data.station; |
|
108 } |
|
109 |
|
110 /** |
|
111 * |
|
112 * Add an order to the orderlist of a vehicle |
|
113 * |
|
114 * @param veh_sel First 16 bits are the ID of the vehicle. The next 16 are the selected order (if any) |
|
115 * If the lastone is given, order will be inserted above thatone |
|
116 * @param packed_order Packed order to insert |
|
117 * |
|
118 */ |
|
119 int32 CmdInsertOrder(int x, int y, uint32 flags, uint32 veh_sel, uint32 packed_order) |
|
120 { |
|
121 Vehicle *v = GetVehicle(veh_sel & 0xFFFF); |
|
122 int sel = veh_sel >> 16; |
|
123 Order new_order = UnpackOrder(packed_order); |
|
124 |
|
125 if (sel > v->num_orders) |
|
126 return_cmd_error(STR_EMPTY); |
|
127 |
|
128 if (IsOrderPoolFull()) |
|
129 return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); |
|
130 |
|
131 /* XXX - This limit is only here because the backuppedorders can't |
|
132 handle any more then this.. */ |
|
133 if (v->num_orders >= 40) |
|
134 return_cmd_error(STR_8832_TOO_MANY_ORDERS); |
|
135 |
|
136 /* For ships, make sure that the station is not too far away from the previous destination. */ |
25 if (v->type == VEH_Ship && IS_HUMAN_PLAYER(v->owner) && |
137 if (v->type == VEH_Ship && IS_HUMAN_PLAYER(v->owner) && |
26 sel != 0 && v->schedule_ptr[sel - 1].type == OT_GOTO_STATION) { |
138 sel != 0 && GetVehicleOrder(v, sel - 1)->type == OT_GOTO_STATION) { |
27 |
139 |
28 int dist = GetTileDist( |
140 int dist = GetTileDist( |
29 GetStation(v->schedule_ptr[sel - 1].station)->xy, |
141 GetStation(GetVehicleOrder(v, sel - 1)->station)->xy, |
30 GetStation(new_order.station)->xy |
142 GetStation(new_order.station)->xy |
31 ); |
143 ); |
32 if (dist >= 130) |
144 if (dist >= 130) |
33 return_cmd_error(STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO); |
145 return_cmd_error(STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO); |
34 } |
146 } |
35 |
147 |
36 if (flags & DC_EXEC) { |
148 if (flags & DC_EXEC) { |
37 Order *s1; |
149 Order *new; |
38 Order *s2; |
|
39 Vehicle *u; |
150 Vehicle *u; |
40 |
151 |
41 s1 = &v->schedule_ptr[sel]; |
152 new = AllocateOrder(); |
42 s2 = _ptr_to_next_order++; |
153 AssignOrder(new, new_order); |
43 do s2[1] = s2[0]; while (--s2 >= s1); |
154 |
44 *s1 = new_order; |
155 /* Create new order and link in list */ |
45 |
156 if (v->orders == NULL) { |
46 s1 = v->schedule_ptr; |
157 v->orders = new; |
47 |
158 } else { |
48 FOR_ALL_VEHICLES(u) { |
159 /* Try to get the previous item (we are inserting above the |
49 if (u->type != 0 && u->schedule_ptr != NULL) { |
160 selected) */ |
50 if (s1 < u->schedule_ptr) { |
161 Order *order = GetVehicleOrder(v, sel - 1); |
51 u->schedule_ptr++; |
162 |
52 } else if (s1 == u->schedule_ptr) { // handle shared orders |
163 if (order == NULL && GetVehicleOrder(v, sel) != NULL) { |
53 u->num_orders++; |
164 /* There is no previous item, so we are altering v->orders itself |
54 |
165 But because the orders can be shared, we copy the info over |
55 if ((byte)sel <= u->cur_order_index) { |
166 the v->orders, so we don't have to change the pointers of |
56 sel++; |
167 all vehicles */ |
57 if ((byte)sel < u->num_orders) |
168 SwapOrders(v->orders, new); |
58 u->cur_order_index = sel; |
169 /* Now update the next pointers */ |
59 } |
170 v->orders->next = new; |
60 InvalidateWindow(WC_VEHICLE_VIEW, u->index); |
171 } else if (order == NULL) { |
61 InvalidateWindow(WC_VEHICLE_ORDERS, u->index); |
172 /* 'sel' is a non-existing order, add him to the end */ |
62 } |
173 order = GetLastVehicleOrder(v); |
63 } |
174 order->next = new; |
64 } |
175 } else { |
65 |
176 /* Put the new order in between */ |
|
177 new->next = order->next; |
|
178 order->next = new; |
|
179 } |
|
180 } |
|
181 |
|
182 u = GetFirstVehicleFromSharedList(v); |
|
183 while (u != NULL) { |
|
184 /* Increase amount of orders */ |
|
185 u->num_orders++; |
|
186 |
|
187 /* If the orderlist was empty, assign it */ |
|
188 if (u->orders == NULL) |
|
189 u->orders = v->orders; |
|
190 |
|
191 assert(v->orders == u->orders); |
|
192 |
|
193 /* If there is added an order before the current one, we need |
|
194 to update the selected order */ |
|
195 if (sel <= u->cur_order_index) { |
|
196 uint cur = u->cur_order_index + 1; |
|
197 /* Check if we don't go out of bound */ |
|
198 if (cur < u->num_orders) |
|
199 u->cur_order_index = cur; |
|
200 } |
|
201 /* Update any possible open window of the vehicle */ |
|
202 InvalidateVehicleOrder(u); |
|
203 |
|
204 u = u->next_shared; |
|
205 } |
|
206 |
|
207 /* Make sure to rebuild the whole list */ |
66 RebuildVehicleLists(); |
208 RebuildVehicleLists(); |
67 } |
209 } |
68 |
210 |
69 return 0; |
211 return 0; |
70 } |
212 } |
71 |
213 |
|
214 /** |
|
215 * |
|
216 * Declone an order-list |
|
217 * |
|
218 */ |
72 static int32 DecloneOrder(Vehicle *dst, uint32 flags) |
219 static int32 DecloneOrder(Vehicle *dst, uint32 flags) |
73 { |
220 { |
74 if (_ptr_to_next_order == endof(_order_array)) |
|
75 return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); |
|
76 |
|
77 if (flags & DC_EXEC) { |
221 if (flags & DC_EXEC) { |
78 DeleteVehicleSchedule(dst); |
222 /* Delete orders from vehicle */ |
79 |
223 DeleteVehicleOrders(dst); |
80 dst->num_orders = 0; |
224 |
81 _ptr_to_next_order->type = OT_NOTHING; |
225 InvalidateVehicleOrder(dst); |
82 _ptr_to_next_order->flags = 0; |
|
83 dst->schedule_ptr = _ptr_to_next_order++; |
|
84 |
|
85 InvalidateWindow(WC_VEHICLE_ORDERS, dst->index); |
|
86 |
|
87 RebuildVehicleLists(); |
226 RebuildVehicleLists(); |
88 } |
227 } |
89 return 0; |
228 return 0; |
90 } |
229 } |
91 |
230 |
92 /* p1 = vehicle |
231 /** |
93 * p2 = sel |
232 * |
94 */ |
233 * Delete an order from the orderlist of a vehicle |
95 int32 CmdDeleteOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
234 * |
96 { |
235 * @param vehicle_id The ID of the vehicle |
97 Vehicle *v = GetVehicle(p1), *u; |
236 * @param selected The order to delete |
98 uint sel = (uint)p2; |
237 * |
99 |
238 */ |
|
239 int32 CmdDeleteOrder(int x, int y, uint32 flags, uint32 vehicle_id, uint32 selected) |
|
240 { |
|
241 Vehicle *v = GetVehicle(vehicle_id), *u; |
|
242 uint sel = selected; |
|
243 Order *order; |
|
244 |
|
245 /* XXX -- Why is this here? :s */ |
100 _error_message = STR_EMPTY; |
246 _error_message = STR_EMPTY; |
|
247 |
|
248 /* If we did not select an order, we maybe want to de-clone the orders */ |
101 if (sel >= v->num_orders) |
249 if (sel >= v->num_orders) |
102 return DecloneOrder(v, flags); |
250 return DecloneOrder(v, flags); |
103 |
251 |
|
252 order = GetVehicleOrder(v, sel); |
|
253 if (order == NULL) |
|
254 return CMD_ERROR; |
|
255 |
104 if (flags & DC_EXEC) { |
256 if (flags & DC_EXEC) { |
105 Order *s1 = &v->schedule_ptr[sel]; |
257 if (GetVehicleOrder(v, sel - 1) == NULL) { |
106 |
258 if (GetVehicleOrder(v, sel + 1) != NULL) { |
107 // copy all orders to get rid of the hole |
259 /* First item, but not the last, so we need to alter v->orders |
108 do s1[0] = s1[1]; while (++s1 != _ptr_to_next_order); |
260 Because we can have shared order, we copy the data |
109 _ptr_to_next_order--; |
261 from the next item over the deleted */ |
110 |
262 order = GetVehicleOrder(v, sel + 1); |
111 s1 = v->schedule_ptr; |
263 SwapOrders(v->orders, order); |
112 |
264 } else { |
113 FOR_ALL_VEHICLES(u) { |
265 /* Last item, so clean the list */ |
114 if (u->type != 0 && u->schedule_ptr != NULL) { |
266 v->orders = NULL; |
115 if (s1 < u->schedule_ptr) { |
267 } |
116 u->schedule_ptr--; |
268 } else { |
117 } else if (s1 == u->schedule_ptr) {// handle shared orders |
269 GetVehicleOrder(v, sel - 1)->next = order->next; |
118 u->num_orders--; |
270 } |
119 if ((byte)sel < u->cur_order_index) |
271 |
120 u->cur_order_index--; |
272 /* Give the item free */ |
121 |
273 order->type = OT_NOTHING; |
122 if ((byte)sel == u->cur_order_index && |
274 |
123 u->current_order.type == OT_LOADING && |
275 u = GetFirstVehicleFromSharedList(v); |
124 u->current_order.flags & OF_NON_STOP) { |
276 while (u != NULL) { |
125 u->current_order.flags = 0; |
277 u->num_orders--; |
126 } |
278 |
127 |
279 if (sel < u->cur_order_index) |
128 InvalidateWindow(WC_VEHICLE_VIEW, u->index); |
280 u->cur_order_index--; |
129 InvalidateWindow(WC_VEHICLE_ORDERS, u->index); |
281 |
130 } |
282 /* If we removed the last order, make sure the shared vehicles |
131 } |
283 also set their orders to NULL */ |
|
284 if (v->orders == NULL) |
|
285 u->orders = NULL; |
|
286 |
|
287 assert(v->orders == u->orders); |
|
288 |
|
289 /* NON-stop flag is misused to see if a train is in a station that is |
|
290 on his order list or not */ |
|
291 if (sel == u->cur_order_index && |
|
292 u->current_order.type == OT_LOADING && |
|
293 HASBIT(u->current_order.flags, OFB_NON_STOP)) { |
|
294 u->current_order.flags = 0; |
|
295 } |
|
296 |
|
297 /* Update any possible open window of the vehicle */ |
|
298 InvalidateVehicleOrder(u); |
|
299 |
|
300 u = u->next_shared; |
132 } |
301 } |
133 |
302 |
134 RebuildVehicleLists(); |
303 RebuildVehicleLists(); |
135 } |
304 } |
136 |
305 |
137 return 0; |
306 return 0; |
138 } |
307 } |
139 |
308 |
140 /* p1 = vehicle */ |
309 /** |
141 int32 CmdSkipOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
310 * |
142 { |
311 * Goto next order of order-list |
143 Vehicle *v = GetVehicle(p1); |
312 * |
|
313 * @param vehicle_id The ID of the vehicle |
|
314 * |
|
315 */ |
|
316 int32 CmdSkipOrder(int x, int y, uint32 flags, uint32 vehicle_id, uint32 not_used) |
|
317 { |
|
318 Vehicle *v = GetVehicle(vehicle_id); |
144 |
319 |
145 if (flags & DC_EXEC) { |
320 if (flags & DC_EXEC) { |
|
321 /* Goto next order */ |
146 { |
322 { |
147 byte b = v->cur_order_index + 1; |
323 byte b = v->cur_order_index + 1; |
148 if (b >= v->num_orders) b = 0; |
324 if (b >= v->num_orders) |
|
325 b = 0; |
|
326 |
149 v->cur_order_index = b; |
327 v->cur_order_index = b; |
150 |
328 |
151 if (v->type == VEH_Train) |
329 if (v->type == VEH_Train) |
152 v->u.rail.days_since_order_progr = 0; |
330 v->u.rail.days_since_order_progr = 0; |
153 } |
331 } |
154 |
332 |
|
333 /* NON-stop flag is misused to see if a train is in a station that is |
|
334 on his order list or not */ |
155 if (v->current_order.type == OT_LOADING && |
335 if (v->current_order.type == OT_LOADING && |
156 v->current_order.flags & OF_NON_STOP) { |
336 HASBIT(v->current_order.flags, OFB_NON_STOP)) { |
157 v->current_order.flags = 0; |
337 v->current_order.flags = 0; |
158 } |
338 } |
159 |
339 |
160 InvalidateWindow(WC_VEHICLE_ORDERS, v->index); |
340 InvalidateVehicleOrder(v); |
161 } |
341 } |
162 |
342 |
163 //we have an aircraft, they have a mini-schedule, so update them all |
343 /* We have an aircraft/ship, they have a mini-schedule, so update them all */ |
164 if (v->type == VEH_Aircraft) InvalidateAircraftWindows(v); |
344 if (v->type == VEH_Aircraft) InvalidateAircraftWindows(v); |
165 |
|
166 //same goes for ships |
|
167 if (v->type == VEH_Ship) InvalidateShipWindows(v); |
345 if (v->type == VEH_Ship) InvalidateShipWindows(v); |
168 |
346 |
169 return 0; |
347 return 0; |
170 } |
348 } |
171 |
349 |
172 /* p1 = vehicle |
350 |
173 * p2&0xFF = sel |
351 /** |
174 * p2>>8 = mode |
352 * |
175 */ |
353 * Add an order to the orderlist of a vehicle |
176 int32 CmdModifyOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
354 * |
177 { |
355 * @param veh_sel First 16 bits are the ID of the vehicle. The next 16 are the selected order (if any) |
178 Vehicle *v = GetVehicle(p1); |
356 * If the lastone is given, order will be inserted above thatone |
179 byte sel = (byte)p2; |
357 * @param mode Mode to change the order to |
180 Order *sched; |
358 * |
181 |
359 */ |
|
360 int32 CmdModifyOrder(int x, int y, uint32 flags, uint32 veh_sel, uint32 mode) |
|
361 { |
|
362 Vehicle *v = GetVehicle(veh_sel & 0xFFFF); |
|
363 byte sel = veh_sel >> 16; |
|
364 Order *order; |
|
365 |
|
366 /* Is it a valid order? */ |
182 if (sel >= v->num_orders) |
367 if (sel >= v->num_orders) |
183 return CMD_ERROR; |
368 return CMD_ERROR; |
184 |
369 |
185 sched = &v->schedule_ptr[sel]; |
370 order = GetVehicleOrder(v, sel); |
186 if (sched->type != OT_GOTO_STATION && |
371 if (order->type != OT_GOTO_STATION && |
187 (sched->type != OT_GOTO_DEPOT || (p2 >> 8) == 1) && |
372 (order->type != OT_GOTO_DEPOT || mode == OFB_UNLOAD) && |
188 (sched->type != OT_GOTO_WAYPOINT || (p2 >> 8) != 2)) |
373 (order->type != OT_GOTO_WAYPOINT || mode != OFB_NON_STOP)) |
189 return CMD_ERROR; |
374 return CMD_ERROR; |
190 |
375 |
191 if (flags & DC_EXEC) { |
376 if (flags & DC_EXEC) { |
192 switch (p2 >> 8) { |
377 switch (mode) { |
193 case 0: // full load |
378 case OFB_FULL_LOAD: |
194 sched->flags ^= OF_FULL_LOAD; |
379 TOGGLEBIT(order->flags, OFB_FULL_LOAD); |
195 if (sched->type != OT_GOTO_DEPOT) sched->flags &= ~OF_UNLOAD; |
380 if (order->type != OT_GOTO_DEPOT) |
|
381 CLRBIT(order->flags, OFB_UNLOAD); |
196 break; |
382 break; |
197 case 1: // unload |
383 case OFB_UNLOAD: |
198 sched->flags ^= OF_UNLOAD; |
384 TOGGLEBIT(order->flags, OFB_UNLOAD); |
199 sched->flags &= ~OF_FULL_LOAD; |
385 CLRBIT(order->flags, OFB_FULL_LOAD); |
200 break; |
386 break; |
201 case 2: // non stop |
387 case OFB_NON_STOP: |
202 sched->flags ^= OF_NON_STOP; |
388 TOGGLEBIT(order->flags, OFB_NON_STOP); |
203 break; |
389 break; |
204 } |
390 } |
205 sched = v->schedule_ptr; |
391 |
206 |
392 /* Update the windows, also for vehicles that share the same order list */ |
207 FOR_ALL_VEHICLES(v) { |
393 { |
208 if (v->schedule_ptr == sched) |
394 Vehicle *u = GetFirstVehicleFromSharedList(v); |
209 InvalidateWindow(WC_VEHICLE_ORDERS, v->index); |
395 while (u != NULL) { |
210 } |
396 InvalidateVehicleOrder(u); |
211 |
397 u = u->next_shared; |
|
398 } |
|
399 } |
212 } |
400 } |
213 |
401 |
214 return 0; |
402 return 0; |
215 } |
403 } |
216 |
404 |
217 // Clone an order |
405 /** |
218 // p1 & 0xFFFF is destination vehicle |
406 * |
219 // p1 >> 16 is source vehicle |
407 * Clone/share/copy an order-list of an other vehicle |
220 |
408 * |
221 // p2 is |
409 * @param veh1_veh2 First 16 bits are of destination vehicle, last 16 of source vehicle |
222 // 0 - clone |
410 * @param mode Mode of cloning (CO_SHARE, CO_COPY, CO_UNSHARE) |
223 // 1 - copy |
411 * |
224 // 2 - unclone |
412 */ |
225 |
413 int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 veh1_veh2, uint32 mode) |
226 |
414 { |
227 int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
415 Vehicle *dst = GetVehicle(veh1_veh2 & 0xFFFF); |
228 { |
416 |
229 Vehicle *dst = GetVehicle(p1 & 0xFFFF); |
417 if (dst->type == 0 || dst->owner != _current_player) |
230 |
|
231 if (!(dst->type && dst->owner == _current_player)) |
|
232 return CMD_ERROR; |
418 return CMD_ERROR; |
233 |
419 |
234 switch(p2) { |
420 switch(mode) { |
235 |
421 case CO_SHARE: { |
236 // share vehicle orders? |
422 Vehicle *src = GetVehicle(veh1_veh2 >> 16); |
237 case 0: { |
423 |
238 Vehicle *src = GetVehicle(p1 >> 16); |
424 /* Sanity checks */ |
239 |
425 if (src->type == 0 || src->owner != _current_player || dst->type != src->type || dst == src) |
240 // sanity checks |
|
241 if (!(src->owner == _current_player && dst->type == src->type && dst != src)) |
|
242 return CMD_ERROR; |
|
243 |
|
244 // let's see what happens with road vehicles |
|
245 if (src->type == VEH_Road) { |
|
246 if (src->cargo_type != dst->cargo_type && (src->cargo_type == CT_PASSENGERS || dst->cargo_type == CT_PASSENGERS)) |
|
247 return CMD_ERROR; |
426 return CMD_ERROR; |
248 } |
427 |
249 |
428 /* Trucks can't share orders with busses (and visa versa) */ |
250 if (flags & DC_EXEC) { |
429 if (src->type == VEH_Road) { |
251 DeleteVehicleSchedule(dst); |
430 if (src->cargo_type != dst->cargo_type && (src->cargo_type == CT_PASSENGERS || dst->cargo_type == CT_PASSENGERS)) |
252 dst->schedule_ptr = src->schedule_ptr; |
431 return CMD_ERROR; |
253 dst->num_orders = src->num_orders; |
432 } |
254 |
433 |
255 InvalidateWindow(WC_VEHICLE_ORDERS, src->index); |
434 /* Is the vehicle already in the shared list? */ |
256 InvalidateWindow(WC_VEHICLE_ORDERS, dst->index); |
435 { |
257 |
436 Vehicle *u = GetFirstVehicleFromSharedList(src); |
258 RebuildVehicleLists(); |
437 while (u != NULL) { |
259 } |
438 if (u == dst) |
260 break; |
|
261 } |
|
262 |
|
263 // copy vehicle orders? |
|
264 case 1: { |
|
265 Vehicle *src = GetVehicle(p1 >> 16); |
|
266 int delta; |
|
267 |
|
268 // sanity checks |
|
269 if (!(src->owner == _current_player && dst->type == src->type && dst != src)) |
|
270 return CMD_ERROR; |
|
271 |
|
272 // let's see what happens with road vehicles |
|
273 if (src->type == VEH_Road) { |
|
274 const Order *i; |
|
275 TileIndex required_dst; |
|
276 |
|
277 for (i = src->schedule_ptr; i->type != OT_NOTHING; ++i) { |
|
278 if (i->type == OT_GOTO_STATION) { |
|
279 const Station *st = GetStation(i->station); |
|
280 required_dst = (dst->cargo_type == CT_PASSENGERS) ? st->bus_tile : st->lorry_tile; |
|
281 if ( !required_dst ) |
|
282 return CMD_ERROR; |
439 return CMD_ERROR; |
|
440 u = u->next_shared; |
283 } |
441 } |
284 } |
442 } |
285 } |
443 |
286 |
444 if (flags & DC_EXEC) { |
287 // make sure there's orders available |
445 /* If the destination vehicle had a OrderList, destroy it */ |
288 delta = IsScheduleShared(dst) ? src->num_orders + 1 : src->num_orders - dst->num_orders; |
446 DeleteVehicleOrders(dst); |
289 if (delta > endof(_order_array) - _ptr_to_next_order) |
447 |
290 return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); |
448 dst->orders = src->orders; |
291 |
449 dst->num_orders = src->num_orders; |
292 if (flags & DC_EXEC) { |
450 |
293 DeleteVehicleSchedule(dst); |
451 /* Link this vehicle in the shared-list */ |
294 dst->schedule_ptr = _ptr_to_next_order; |
452 dst->next_shared = src->next_shared; |
295 dst->num_orders = src->num_orders; |
453 dst->prev_shared = src; |
296 _ptr_to_next_order += src->num_orders + 1; |
454 if (src->next_shared != NULL) |
297 memcpy(dst->schedule_ptr, src->schedule_ptr, (src->num_orders + 1) * sizeof(Order)); |
455 src->next_shared->prev_shared = dst; |
298 |
456 src->next_shared = dst; |
299 InvalidateWindow(WC_VEHICLE_ORDERS, dst->index); |
457 |
300 |
458 InvalidateVehicleOrder(dst); |
301 RebuildVehicleLists(); |
459 InvalidateVehicleOrder(src); |
302 } |
460 |
303 break; |
461 RebuildVehicleLists(); |
304 } |
462 } |
305 |
463 } break; |
306 // declone vehicle orders? |
464 |
307 case 2: return DecloneOrder(dst, flags); |
465 case CO_COPY: { |
|
466 Vehicle *src = GetVehicle(veh1_veh2 >> 16); |
|
467 int delta; |
|
468 |
|
469 /* Sanity checks */ |
|
470 if (src->type == 0 || src->owner != _current_player || dst->type != src->type || dst == src) |
|
471 return CMD_ERROR; |
|
472 |
|
473 /* Trucks can't copy all the orders from busses (and visa versa) */ |
|
474 if (src->type == VEH_Road) { |
|
475 const Order *order; |
|
476 TileIndex required_dst; |
|
477 |
|
478 FOR_VEHICLE_ORDERS(src, order) { |
|
479 if (order->type == OT_GOTO_STATION) { |
|
480 const Station *st = GetStation(order->station); |
|
481 required_dst = (dst->cargo_type == CT_PASSENGERS) ? st->bus_tile : st->lorry_tile; |
|
482 /* This station has not the correct road-bay, so we can't copy! */ |
|
483 if (!required_dst) |
|
484 return CMD_ERROR; |
|
485 } |
|
486 } |
|
487 } |
|
488 |
|
489 /* make sure there are orders available */ |
|
490 delta = IsOrderListShared(dst) ? src->num_orders + 1 : src->num_orders - dst->num_orders; |
|
491 if (!HasOrderPoolFree(delta)) |
|
492 return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); |
|
493 |
|
494 if (flags & DC_EXEC) { |
|
495 const Order *order; |
|
496 Order **order_dst; |
|
497 |
|
498 /* If the destination vehicle had a OrderList, destroy it */ |
|
499 DeleteVehicleOrders(dst); |
|
500 |
|
501 order_dst = &dst->orders; |
|
502 FOR_VEHICLE_ORDERS(src, order) { |
|
503 *order_dst = AllocateOrder(); |
|
504 AssignOrder(*order_dst, *order); |
|
505 order_dst = &(*order_dst)->next; |
|
506 } |
|
507 |
|
508 dst->num_orders = src->num_orders; |
|
509 |
|
510 InvalidateVehicleOrder(dst); |
|
511 |
|
512 RebuildVehicleLists(); |
|
513 } |
|
514 } break; |
|
515 |
|
516 case CO_UNSHARE: |
|
517 return DecloneOrder(dst, flags); |
308 } |
518 } |
309 |
519 |
310 return 0; |
520 return 0; |
311 } |
521 } |
312 |
522 |
|
523 /** |
|
524 * |
|
525 * Backup a vehicle order-list, so you can replace a vehicle |
|
526 * without loosing the order-list |
|
527 * |
|
528 */ |
313 void BackupVehicleOrders(Vehicle *v, BackuppedOrders *bak) |
529 void BackupVehicleOrders(Vehicle *v, BackuppedOrders *bak) |
314 { |
530 { |
315 Vehicle *u = IsScheduleShared(v); |
531 bool shared = IsOrderListShared(v); |
316 |
532 |
317 bak->orderindex = v->cur_order_index; |
533 /* Save general info */ |
|
534 bak->orderindex = v->cur_order_index; |
318 bak->service_interval = v->service_interval; |
535 bak->service_interval = v->service_interval; |
319 |
536 |
|
537 /* Safe custom string, if any */ |
320 if ((v->string_id & 0xF800) != 0x7800) { |
538 if ((v->string_id & 0xF800) != 0x7800) { |
321 bak->name[0] = 0; |
539 bak->name[0] = 0; |
322 } else { |
540 } else { |
323 GetName(v->string_id & 0x7FF, bak->name); |
541 GetName(v->string_id & 0x7FF, bak->name); |
324 } |
542 } |
325 |
543 |
326 // stored shared orders in this special way? |
544 /* If we have shared orders, store it on a special way */ |
327 if (u != NULL) { |
545 if (shared) { |
|
546 Vehicle *u; |
|
547 if (v->next_shared) |
|
548 u = v->next_shared; |
|
549 else |
|
550 u = v->prev_shared; |
|
551 |
328 bak->clone = u->index; |
552 bak->clone = u->index; |
329 } else { |
553 } else { |
330 Order *sched = v->schedule_ptr; |
554 /* Else copy the orders */ |
331 Order *os = bak->order; |
555 Order *order, *dest; |
332 |
556 |
|
557 dest = bak->order; |
|
558 |
|
559 /* We do not have shared orders */ |
333 bak->clone = INVALID_VEHICLE; |
560 bak->clone = INVALID_VEHICLE; |
334 |
561 |
335 do { |
562 /* Copy the orders */ |
336 *os++ = *sched++; |
563 FOR_VEHICLE_ORDERS(v, order) { |
337 } while (sched->type != OT_NOTHING); |
564 *dest = *order; |
338 /* Make sure the last item is OT_NOTHING */ |
565 dest++; |
339 os->type = OT_NOTHING; |
566 } |
340 } |
567 /* End the list with an OT_NOTHING */ |
341 } |
568 dest->type = OT_NOTHING; |
342 |
569 } |
|
570 } |
|
571 |
|
572 /** |
|
573 * |
|
574 * Restore vehicle orders that are backupped via BackupVehicleOrders |
|
575 * |
|
576 */ |
343 void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak) |
577 void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak) |
344 { |
578 { |
345 int i; |
579 int i; |
346 |
580 |
347 if (bak->name[0]) { |
581 /* If we have a custom name, process that */ |
|
582 if (bak->name[0] != 0) { |
348 strcpy((char*)_decode_parameters, bak->name); |
583 strcpy((char*)_decode_parameters, bak->name); |
349 DoCommandP(0, v->index, 0, NULL, CMD_NAME_VEHICLE); |
584 DoCommandP(0, v->index, 0, NULL, CMD_NAME_VEHICLE); |
350 } |
585 } |
351 |
586 |
352 DoCommandP(0, v->index, bak->orderindex|(bak->service_interval<<16) , NULL, CMD_RESTORE_ORDER_INDEX); |
587 /* Restore vehicle number and service interval */ |
353 |
588 DoCommandP(0, v->index, bak->orderindex | (bak->service_interval << 16) , NULL, CMD_RESTORE_ORDER_INDEX); |
|
589 |
|
590 /* If we had shared orders, recover that */ |
354 if (bak->clone != INVALID_VEHICLE) { |
591 if (bak->clone != INVALID_VEHICLE) { |
355 DoCommandP(0, v->index | bak->clone << 16, 0, NULL, CMD_CLONE_ORDER); |
592 DoCommandP(0, v->index | (bak->clone << 16), 0, NULL, CMD_CLONE_ORDER); |
356 return; |
593 return; |
357 } |
594 } |
358 |
595 |
359 // CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the |
596 /* CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the |
360 // order number is one more than the current amount of orders, and because |
597 order number is one more than the current amount of orders, and because |
361 // in network the commands are queued before send, the second insert always |
598 in network the commands are queued before send, the second insert always |
362 // fails in test mode. By bypassing the test-mode, that no longer is a problem. |
599 fails in test mode. By bypassing the test-mode, that no longer is a problem. */ |
363 for (i = 0; bak->order[i].type != OT_NOTHING; ++i) |
600 for (i = 0; bak->order[i].type != OT_NOTHING; i++) |
364 if (!DoCommandP(0, v->index + (i << 16), PackOrder(&bak->order[i]), NULL, CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) |
601 if (!DoCommandP(0, v->index + (i << 16), PackOrder(&bak->order[i]), NULL, CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) |
365 break; |
602 break; |
366 } |
603 } |
367 |
604 |
368 /* p1 = vehicle |
605 /** |
369 * upper 16 bits p2 = service_interval |
606 * |
370 * lower 16 bits p2 = cur_order_index |
607 * Restore the current-order-index of a vehicle and sets service-interval |
371 */ |
608 * |
372 int32 CmdRestoreOrderIndex(int x, int y, uint32 flags, uint32 p1, uint32 p2) |
609 * @param vehicle_id The ID of the vehicle |
373 { |
610 * @param data First 16 bits are the current-order-index |
374 // nonsense to update the windows, since, train rebought will have its window deleted |
611 * The last 16 bits are the service-interval |
|
612 * |
|
613 */ |
|
614 int32 CmdRestoreOrderIndex(int x, int y, uint32 flags, uint32 vehicle_id, uint32 data) |
|
615 { |
375 if (flags & DC_EXEC) { |
616 if (flags & DC_EXEC) { |
376 Vehicle *v = GetVehicle(p1); |
617 Vehicle *v = GetVehicle(vehicle_id); |
377 v->service_interval = (uint16)(p2>>16); |
618 v->service_interval = data >> 16; |
378 v->cur_order_index = (byte)(p2&0xFFFF); |
619 v->cur_order_index = data & 0xFFFF; |
379 } |
620 } |
|
621 |
380 return 0; |
622 return 0; |
381 } |
623 } |
382 |
624 |
383 int CheckOrders(Vehicle *v) |
625 /** |
384 { |
626 * |
385 if (!_patches.order_review_system) //User doesn't want things to be checked |
627 * Check the orders of a vehicle, to see if there are invalid orders and stuff |
386 return 0; |
628 * |
387 |
629 */ |
|
630 bool CheckOrders(const Vehicle *v) |
|
631 { |
|
632 /* Does the user wants us to check things? */ |
|
633 if (_patches.order_review_system == 0) |
|
634 return false; |
|
635 |
|
636 /* Do nothing for crashed vehicles */ |
388 if(v->vehstatus & VS_CRASHED) |
637 if(v->vehstatus & VS_CRASHED) |
389 return 0; |
638 return false; |
390 |
639 |
|
640 /* Do nothing for stopped vehicles if setting is '1' */ |
391 if ( (_patches.order_review_system == 1) && (v->vehstatus & VS_STOPPED) ) |
641 if ( (_patches.order_review_system == 1) && (v->vehstatus & VS_STOPPED) ) |
392 return 0; |
642 return false; |
393 |
643 |
394 /* only check every 20 days, so that we don't flood the message log */ |
644 /* Only check every 20 days, so that we don't flood the message log */ |
395 if ( ( ( v->day_counter % 20) == 0 ) && (v->owner == _local_player) ) { |
645 if ( ( ( v->day_counter % 20) == 0 ) && (v->owner == _local_player) ) { |
396 Order order; |
646 int n_st, problem_type = -1; |
397 Order old_order; |
647 const Order *order; |
398 int i, n_st, problem_type = -1; |
648 const Station *st; |
399 Station *st; |
649 int message = 0; |
400 int message=0; |
650 |
401 TileIndex required_tile=-1; |
651 /* Check the order list */ |
402 |
|
403 /* check the order list */ |
|
404 order = v->schedule_ptr[0]; |
|
405 n_st = 0; |
652 n_st = 0; |
406 |
653 |
407 old_order.type = OT_NOTHING; |
654 FOR_VEHICLE_ORDERS(v, order) { |
408 old_order.flags = 0; |
655 /* Dummy order? */ |
409 for (i = 0; order.type != OT_NOTHING; i++) { |
656 if (order->type == OT_DUMMY) { |
410 order = v->schedule_ptr[i]; |
|
411 if (order.type == old_order.type && |
|
412 order.flags == old_order.flags && |
|
413 order.station == old_order.station) { |
|
414 problem_type = 2; |
|
415 break; |
|
416 } |
|
417 if (order.type == OT_DUMMY) { |
|
418 problem_type = 1; |
657 problem_type = 1; |
419 break; |
658 break; |
420 } |
659 } |
421 if (order.type == OT_GOTO_STATION /*&& (order != old_order) */) { |
660 /* Does station have a load-bay for this vehicle? */ |
422 //I uncommented this in order not to get two error messages |
661 if (order->type == OT_GOTO_STATION) { |
423 //when two identical entries are in the list |
662 TileIndex required_tile; |
|
663 |
424 n_st++; |
664 n_st++; |
425 st = GetStation(order.station); |
665 st = GetStation(order->station); |
426 required_tile = GetStationTileForVehicle(v,st); |
666 required_tile = GetStationTileForVehicle(v, st); |
427 if (!required_tile) problem_type = 3; |
667 if (!required_tile) |
428 } |
668 problem_type = 3; |
429 old_order = order; //store the old order |
669 } |
430 } |
670 } |
431 |
671 |
432 //Now, check the last and the first order |
672 /* Check if the last and the first order are the same */ |
433 //as the last order is the end of order marker, jump back 2 |
673 if (v->num_orders > 1 && |
434 if (i > 2 && |
674 v->orders->type == GetLastVehicleOrder(v)->type && |
435 v->schedule_ptr[0].type == v->schedule_ptr[i - 2].type && |
675 v->orders->flags == GetLastVehicleOrder(v)->flags && |
436 v->schedule_ptr[0].flags == v->schedule_ptr[i - 2].flags && |
676 v->orders->station == GetLastVehicleOrder(v)->station) |
437 v->schedule_ptr[0].station == v->schedule_ptr[i - 2].station) |
|
438 problem_type = 2; |
677 problem_type = 2; |
439 |
678 |
440 if ( (n_st < 2) && (problem_type == -1) ) problem_type = 0; |
679 /* Do we only have 1 station in our order list? */ |
|
680 if ((n_st < 2) && (problem_type == -1)) |
|
681 problem_type = 0; |
|
682 |
|
683 /* We don't have a problem */ |
|
684 if (problem_type < 0) |
|
685 return false; |
|
686 |
|
687 message = (STR_TRAIN_HAS_TOO_FEW_ORDERS) + (((v->type) - VEH_Train) << 2) + problem_type; |
441 |
688 |
442 SetDParam(0, v->unitnumber); |
689 SetDParam(0, v->unitnumber); |
443 |
|
444 message = (STR_TRAIN_HAS_TOO_FEW_ORDERS) + (((v->type) - VEH_Train) << 2) + problem_type; |
|
445 |
|
446 if (problem_type < 0) return 0; |
|
447 |
|
448 AddNewsItem( |
690 AddNewsItem( |
449 message, |
691 message, |
450 NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), |
692 NEWS_FLAGS(NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, 0), |
451 v->index, |
693 v->index, |
452 0); |
694 0); |
453 } |
695 } |
454 // End of order check |
696 |
455 |
697 return true; |
456 return 1; |
698 } |
457 } |
699 |
|
700 /** |
|
701 * |
|
702 * Delete a destination (like station, waypoint, ..) from the orders of vehicles |
|
703 * |
|
704 * @param dest type and station has to be set. This order will be removed from all orders of vehicles |
|
705 * |
|
706 */ |
|
707 void DeleteDestinationFromVehicleOrder(Order dest) |
|
708 { |
|
709 Vehicle *v; |
|
710 Order *order; |
|
711 bool need_invalidate; |
|
712 |
|
713 /* Go through all vehicles */ |
|
714 FOR_ALL_VEHICLES(v) { |
|
715 if (v->type == 0 || v->orders == NULL) |
|
716 continue; |
|
717 |
|
718 /* Forget about this station if this station is removed */ |
|
719 if (v->last_station_visited == dest.station && dest.type == OT_GOTO_STATION) |
|
720 v->last_station_visited = 0xFFFF; |
|
721 |
|
722 /* Check the current order */ |
|
723 if (v->current_order.type == dest.type && |
|
724 v->current_order.station == dest.station) { |
|
725 /* Mark the order as DUMMY */ |
|
726 v->current_order.type = OT_DUMMY; |
|
727 v->current_order.flags = 0; |
|
728 InvalidateWindow(WC_VEHICLE_VIEW, v->index); |
|
729 } |
|
730 |
|
731 /* Clear the order from the order-list */ |
|
732 need_invalidate = false; |
|
733 FOR_VEHICLE_ORDERS(v, order) { |
|
734 if (order->type == dest.type && order->station == dest.station) { |
|
735 /* Mark the order as DUMMY */ |
|
736 order->type = OT_DUMMY; |
|
737 order->flags = 0; |
|
738 |
|
739 need_invalidate = true; |
|
740 } |
|
741 } |
|
742 |
|
743 /* Only invalidate once, and if needed */ |
|
744 if (need_invalidate) |
|
745 InvalidateWindow(WC_VEHICLE_ORDERS, v->index); |
|
746 } |
|
747 } |
|
748 |
|
749 /** |
|
750 * |
|
751 * Checks if a vehicle has a GOTO_DEPOT in his order list |
|
752 * |
|
753 * @return True if this is true (lol ;)) |
|
754 * |
|
755 */ |
|
756 bool VehicleHasDepotOrders(const Vehicle *v) |
|
757 { |
|
758 const Order *order; |
|
759 |
|
760 FOR_VEHICLE_ORDERS(v, order) { |
|
761 if (order->type == OT_GOTO_DEPOT) |
|
762 return true; |
|
763 } |
|
764 |
|
765 return false; |
|
766 } |
|
767 |
|
768 /** |
|
769 * |
|
770 * Delete all orders from a vehicle |
|
771 * |
|
772 */ |
|
773 void DeleteVehicleOrders(Vehicle *v) |
|
774 { |
|
775 Order *order, *cur; |
|
776 |
|
777 /* If we have a shared order-list, don't delete the list, but just |
|
778 remove our pointer */ |
|
779 if (IsOrderListShared(v)) { |
|
780 const Vehicle *u = v; |
|
781 |
|
782 v->orders = NULL; |
|
783 v->num_orders = 0; |
|
784 |
|
785 /* Unlink ourself */ |
|
786 if (v->prev_shared != NULL) { |
|
787 v->prev_shared->next_shared = v->next_shared; |
|
788 u = v->prev_shared; |
|
789 } |
|
790 if (v->next_shared != NULL) { |
|
791 v->next_shared->prev_shared = v->prev_shared; |
|
792 u = v->next_shared; |
|
793 } |
|
794 v->prev_shared = NULL; |
|
795 v->next_shared = NULL; |
|
796 |
|
797 /* We only need to update this-one, because if there is a third |
|
798 vehicle which shares the same order-list, nothing will change. If |
|
799 this is the last vehicle, the last line of the order-window |
|
800 will change from Shared order list, to Order list, so it needs |
|
801 an update */ |
|
802 InvalidateVehicleOrder(u); |
|
803 return; |
|
804 } |
|
805 |
|
806 /* Remove the orders */ |
|
807 cur = v->orders; |
|
808 v->orders = NULL; |
|
809 v->num_orders = 0; |
|
810 |
|
811 order = NULL; |
|
812 while (cur != NULL) { |
|
813 if (order != NULL) { |
|
814 order->type = OT_NOTHING; |
|
815 order->next = NULL; |
|
816 } |
|
817 |
|
818 order = cur; |
|
819 cur = cur->next; |
|
820 } |
|
821 |
|
822 if (order != NULL) { |
|
823 order->type = OT_NOTHING; |
|
824 order->next = NULL; |
|
825 } |
|
826 } |
|
827 |
|
828 /** |
|
829 * |
|
830 * Check if we share our orders with an other vehicle |
|
831 * |
|
832 * @return Returns the vehicle who has the same order |
|
833 * |
|
834 */ |
|
835 bool IsOrderListShared(const Vehicle *v) |
|
836 { |
|
837 if (v->next_shared != NULL) |
|
838 return true; |
|
839 |
|
840 if (v->prev_shared != NULL) |
|
841 return true; |
|
842 |
|
843 return false; |
|
844 } |
|
845 |
|
846 /** |
|
847 * |
|
848 * Check if a vehicle has any valid orders |
|
849 * |
|
850 * @return false if there are no valid orders |
|
851 * |
|
852 */ |
|
853 bool CheckForValidOrders(Vehicle *v) |
|
854 { |
|
855 const Order *order; |
|
856 |
|
857 FOR_VEHICLE_ORDERS(v, order) |
|
858 if (order->type != OT_DUMMY) |
|
859 return true; |
|
860 |
|
861 return false; |
|
862 } |
|
863 |
|
864 void InitializeOrders(void) |
|
865 { |
|
866 Order *order; |
|
867 int i; |
|
868 |
|
869 memset(&_orders, 0, sizeof(_orders[0]) * _orders_size); |
|
870 |
|
871 i = 0; |
|
872 FOR_ALL_ORDERS(order) |
|
873 order->index = i++; |
|
874 |
|
875 _backup_orders_tile = 0; |
|
876 } |
|
877 |
|
878 static const byte _order_desc[] = { |
|
879 SLE_VAR(Order,type, SLE_UINT8), |
|
880 SLE_VAR(Order,flags, SLE_UINT8), |
|
881 SLE_VAR(Order,station, SLE_UINT16), |
|
882 SLE_REF(Order,next, REF_ORDER), |
|
883 |
|
884 // reserve extra space in savegame here. (currently 10 bytes) |
|
885 SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 10, 5, 255), |
|
886 SLE_END() |
|
887 }; |
|
888 |
|
889 static void Save_ORDR() |
|
890 { |
|
891 Order *order; |
|
892 |
|
893 FOR_ALL_ORDERS(order) { |
|
894 if (order->type != OT_NOTHING) { |
|
895 SlSetArrayIndex(order->index); |
|
896 SlObject(order, _order_desc); |
|
897 } |
|
898 } |
|
899 } |
|
900 |
|
901 static void Load_ORDR() |
|
902 { |
|
903 if (_sl.full_version <= 0x501) { |
|
904 /* Version older than 0x502 did not have a ->next pointer. Convert them |
|
905 (in the old days, the orderlist was 5000 items big) */ |
|
906 uint len = SlGetFieldLength(); |
|
907 uint i; |
|
908 |
|
909 if (_sl.version < 5) { |
|
910 /* Pre-version 5 had an other layout for orders |
|
911 (uint16 instead of uint32) */ |
|
912 uint16 orders[5000]; |
|
913 |
|
914 len /= sizeof(uint16); |
|
915 assert (len <= _orders_size); |
|
916 |
|
917 SlArray(orders, len, SLE_UINT16); |
|
918 |
|
919 for (i = 0; i < len; ++i) { |
|
920 AssignOrder(GetOrder(i), UnpackVersion4Order(orders[i])); |
|
921 } |
|
922 } else if (_sl.full_version <= 0x501) { |
|
923 uint32 orders[5000]; |
|
924 |
|
925 len /= sizeof(uint32); |
|
926 assert (len <= _orders_size); |
|
927 |
|
928 SlArray(orders, len, SLE_UINT32); |
|
929 |
|
930 for (i = 0; i < len; ++i) { |
|
931 AssignOrder(GetOrder(i), UnpackOrder(orders[i])); |
|
932 } |
|
933 } |
|
934 |
|
935 /* Update all the next pointer */ |
|
936 for (i = 1; i < len; ++i) { |
|
937 /* The orders were built like this: |
|
938 Vehicle one had order[0], and as long as order++.type was not |
|
939 OT_NOTHING, it was part of the order-list of that vehicle */ |
|
940 if (GetOrder(i)->type != OT_NOTHING) |
|
941 GetOrder(i - 1)->next = GetOrder(i); |
|
942 } |
|
943 } else { |
|
944 int index; |
|
945 |
|
946 while ((index = SlIterateArray()) != -1) { |
|
947 Order *order = GetOrder(index); |
|
948 |
|
949 SlObject(order, _order_desc); |
|
950 } |
|
951 } |
|
952 } |
|
953 |
|
954 const ChunkHandler _order_chunk_handlers[] = { |
|
955 { 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY | CH_LAST}, |
|
956 }; |