Update readme and changelog for v1.27.0
[openttd-joker.git] / src / timetable_cmd.cpp
blob6e4822c975d4267e9397dc17cbfda9cb2eb7cf26
1 /* $Id$ */
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 */
10 /** @file timetable_cmd.cpp Commands related to time tabling. */
12 #include "stdafx.h"
13 #include "command_func.h"
14 #include "company_func.h"
15 #include "date_func.h"
16 #include "window_func.h"
17 #include "vehicle_base.h"
18 #include "cmd_helper.h"
19 #include "settings_type.h"
20 #include "core/sort_func.hpp"
21 #include "scope.h"
23 #include "table/strings.h"
25 #include "safeguards.h"
27 /**
28 * Change/update a particular timetable entry.
29 * @param v The vehicle to change the timetable of.
30 * @param order_number The index of the timetable in the order list.
31 * @param val The new data of the timetable entry.
32 * @param mtf Which part of the timetable entry to change.
33 * @param timetabled If the new value is explicitly timetabled.
35 static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, ModifyTimetableFlags mtf, bool timetabled)
37 Order *order = v->GetOrder(order_number);
38 int total_delta = 0;
39 int timetable_delta = 0;
41 switch (mtf) {
42 case MTF_WAIT_TIME:
43 if (!order->IsType(OT_CONDITIONAL)) {
44 total_delta = val - order->GetWaitTime();
45 timetable_delta = (timetabled ? val : 0) - order->GetTimetabledWait();
47 order->SetWaitTime(val);
48 order->SetWaitTimetabled(timetabled);
49 break;
51 case MTF_TRAVEL_TIME:
52 if (!order->IsType(OT_CONDITIONAL)) {
53 total_delta = val - order->GetTravelTime();
54 timetable_delta = (timetabled ? val : 0) - order->GetTimetabledTravel();
56 if (order->IsType(OT_CONDITIONAL)) assert_msg(val == order->GetTravelTime(), "%u == %u", val, order->GetTravelTime());
57 order->SetTravelTime(val);
58 order->SetTravelTimetabled(timetabled);
59 break;
61 case MTF_TRAVEL_SPEED:
62 order->SetMaxSpeed(val);
63 break;
65 default:
66 NOT_REACHED();
68 v->UpdateTotalDuration(total_delta);
69 v->UpdateTimetableDuration(timetable_delta);
71 for (v = v->FirstShared(); v != nullptr; v = v->NextShared()) {
72 if (v->cur_real_order_index == order_number && v->current_order.Equals(*order)) {
73 switch (mtf) {
74 case MTF_WAIT_TIME:
75 v->current_order.SetWaitTime(val);
76 v->current_order.SetWaitTimetabled(timetabled);
77 break;
79 case MTF_TRAVEL_TIME:
80 v->current_order.SetTravelTime(val);
81 v->current_order.SetTravelTimetabled(timetabled);
82 break;
84 case MTF_TRAVEL_SPEED:
85 v->current_order.SetMaxSpeed(val);
86 break;
88 default:
89 NOT_REACHED();
92 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
96 /**
97 * Change timetable data of an order.
98 * @param tile Not used.
99 * @param flags Operation to perform.
100 * @param p1 Various bitstuffed elements
101 * - p1 = (bit 0-19) - Vehicle with the orders to change.
102 * - p1 = (bit 20-27) - Order index to modify.
103 * - p1 = (bit 28-29) - Timetable data to change (@see ModifyTimetableFlags)
104 * @param p2 The amount of time to wait.
105 * - p2 = (bit 0-15) - The data to modify as specified by p1 bits 28-29.
106 * 0 to clear times, UINT16_MAX to clear speed limit.
107 * @param text unused
108 * @return the cost of this operation or an error
110 CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
112 VehicleID veh = GB(p1, 0, 20);
114 Vehicle *v = Vehicle::GetIfValid(veh);
115 if (v == nullptr || !v->IsPrimaryVehicle()) return CommandError();
117 CommandCost ret = CheckOwnership(v->owner);
118 if (ret.Failed()) return ret;
120 VehicleOrderID order_number = GB(p1, 20, 8);
121 Order *order = v->GetOrder(order_number);
122 if (order == nullptr || order->IsType(OT_IMPLICIT)) return CommandError();
124 ModifyTimetableFlags mtf = Extract<ModifyTimetableFlags, 28, 2>(p1);
125 if (mtf >= MTF_END) return CommandError();
127 int wait_time = order->GetWaitTime();
128 int travel_time = order->GetTravelTime();
129 int max_speed = order->GetMaxSpeed();
130 switch (mtf) {
131 case MTF_WAIT_TIME:
132 wait_time = GB(p2, 0, 16);
133 break;
135 case MTF_TRAVEL_TIME:
136 travel_time = GB(p2, 0, 16);
137 break;
139 case MTF_TRAVEL_SPEED:
140 max_speed = GB(p2, 0, 16);
141 if (max_speed == 0) max_speed = UINT16_MAX; // Disable speed limit.
142 break;
144 default:
145 NOT_REACHED();
148 if (wait_time != order->GetWaitTime()) {
149 switch (order->GetType()) {
150 case OT_GOTO_STATION:
151 if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) return CommandError(STR_ERROR_TIMETABLE_NOT_STOPPING_HERE);
152 break;
154 case OT_GOTO_DEPOT:
155 break;
157 case OT_CONDITIONAL:
158 break;
160 default: return CommandError(STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS);
164 if (travel_time != order->GetTravelTime() && order->IsType(OT_CONDITIONAL)) return CommandError();
165 if (max_speed != order->GetMaxSpeed() && (order->IsType(OT_CONDITIONAL) || v->type == VEH_AIRCRAFT)) return CommandError();
167 if (flags & DC_EXEC) {
168 switch (mtf) {
169 case MTF_WAIT_TIME:
170 /* Set time if changing the value or confirming an estimated time as timetabled. */
171 if (wait_time != order->GetWaitTime() || (wait_time > 0 && !order->IsWaitTimetabled())) {
172 ChangeTimetable(v, order_number, wait_time, MTF_WAIT_TIME, wait_time > 0);
174 break;
176 case MTF_TRAVEL_TIME:
177 /* Set time if changing the value or confirming an estimated time as timetabled. */
178 if (travel_time != order->GetTravelTime() || (travel_time > 0 && !order->IsTravelTimetabled())) {
179 ChangeTimetable(v, order_number, travel_time, MTF_TRAVEL_TIME, travel_time > 0);
181 break;
183 case MTF_TRAVEL_SPEED:
184 if (max_speed != order->GetMaxSpeed()) {
185 ChangeTimetable(v, order_number, max_speed, MTF_TRAVEL_SPEED, max_speed != UINT16_MAX);
187 break;
189 default:
190 break;
194 return CommandCost();
198 * Change timetable data of all orders of a vehicle.
199 * @param tile Not used.
200 * @param flags Operation to perform.
201 * @param p1 Various bitstuffed elements
202 * - p1 = (bit 0-19) - Vehicle with the orders to change.
203 * - p1 = (bit 20-27) - unused
204 * - p1 = (bit 28-29) - Timetable data to change (@see ModifyTimetableFlags)
205 * - p1 = (bit 30) - 0 to set timetable wait/travel time, 1 to clear it
206 * @param p2 The amount of time to wait.
207 * - p2 = (bit 0-15) - The data to modify as specified by p1 bits 28-29.
208 * 0 to clear times, UINT16_MAX to clear speed limit.
209 * @param text unused
210 * @return the cost of this operation or an error
212 CommandCost CmdBulkChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
214 VehicleID veh = GB(p1, 0, 20);
216 Vehicle *v = Vehicle::GetIfValid(veh);
217 if (v == nullptr || !v->IsPrimaryVehicle()) return CommandError();
219 CommandCost ret = CheckOwnership(v->owner);
220 if (ret.Failed()) return ret;
222 ModifyTimetableFlags mtf = Extract<ModifyTimetableFlags, 28, 2>(p1);
223 if (mtf >= MTF_END) return CommandError();
225 if (v->GetNumOrders() == 0) return CommandError();
227 if (flags & DC_EXEC) {
228 for (VehicleOrderID order_number = 0; order_number < v->GetNumOrders(); order_number++) {
229 Order *order = v->GetOrder(order_number);
230 if (order == nullptr || order->IsType(OT_IMPLICIT)) continue;
232 uint32 new_p1 = p1;
233 SB(new_p1, 20, 8, order_number);
234 DoCommand(tile, new_p1, p2, flags, CMD_CHANGE_TIMETABLE);
238 return CommandCost();
242 * Clear the lateness counter to make the vehicle on time.
243 * @param tile Not used.
244 * @param flags Operation to perform.
245 * @param p1 Various bitstuffed elements
246 * - p1 = (bit 0-19) - Vehicle with the orders to change.
247 * @param p2 unused
248 * @param text unused
249 * @return the cost of this operation or an error
251 CommandCost CmdSetVehicleOnTime(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
253 VehicleID veh = GB(p1, 0, 20);
255 Vehicle *v = Vehicle::GetIfValid(veh);
256 if (v == nullptr || !v->IsPrimaryVehicle() || !v->HasOrdersList()) return CommandError();
258 CommandCost ret = CheckOwnership(v->owner);
259 if (ret.Failed()) return ret;
261 if (flags & DC_EXEC) {
262 v->lateness_counter = 0;
263 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
266 return CommandCost();
270 * Order vehicles based on their timetable. The vehicles will be sorted in order
271 * they would reach the first station.
273 * @param ap First Vehicle pointer.
274 * @param bp Second Vehicle pointer.
275 * @return Comparison value.
277 static int CDECL VehicleTimetableSorter(Vehicle * const *ap, Vehicle * const *bp)
279 const Vehicle *a = *ap;
280 const Vehicle *b = *bp;
282 VehicleOrderID a_order = a->cur_real_order_index;
283 VehicleOrderID b_order = b->cur_real_order_index;
284 int j = (int)b_order - (int)a_order;
286 /* Are we currently at an ordered station (un)loading? */
287 bool a_load = a->current_order.IsType(OT_LOADING) && a->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
288 bool b_load = b->current_order.IsType(OT_LOADING) && b->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
290 /* If the current order is not loading at the ordered station, decrease the order index by one since we have
291 * not yet arrived at the station (and thus the timetable entry; still in the travelling of the previous one).
292 * Since the ?_order variables are unsigned the -1 will flow under and place the vehicles going to order #0 at
293 * the begin of the list with vehicles arriving at #0. */
294 if (!a_load) a_order--;
295 if (!b_load) b_order--;
297 /* First check the order index that accounted for loading, then just the raw one. */
298 int i = (int)b_order - (int)a_order;
299 if (i != 0) return i;
300 if (j != 0) return j;
302 /* Look at the time we spent in this order; the higher, the closer to its destination. */
303 i = b->current_order_time - a->current_order_time;
304 if (i != 0) return i;
306 /* If all else is equal, use some unique index to sort it the same way. */
307 return b->unitnumber - a->unitnumber;
311 * Set the start date of the timetable.
312 * @param tile Not used.
313 * @param flags Operation to perform.
314 * @param p2 Various bitstuffed elements
315 * - p2 = (bit 0-19) - Vehicle ID.
316 * - p2 = (bit 20) - Set to 1 to set timetable start for all vehicles sharing this order
317 * @param p2 The timetable start date.
318 * @param text Not used.
319 * @return The error or cost of the operation.
321 CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
323 bool timetable_all = HasBit(p1, 20);
324 Vehicle *v = Vehicle::GetIfValid(GB(p1, 0, 20));
325 if (v == nullptr || !v->IsPrimaryVehicle() || !v->HasOrdersList()) return CommandError();
327 CommandCost ret = CheckOwnership(v->owner);
328 if (ret.Failed()) return ret;
330 /* Don't let a timetable start more than 15 years into the future or 1 year in the past. */
331 Date start_date = (Date)p2;
332 if (start_date < 0 || start_date > MAX_DAY) return CommandError();
333 if (start_date - _date > 15 * DAYS_IN_LEAP_YEAR) return CommandError();
334 if (_date - start_date > DAYS_IN_LEAP_YEAR) return CommandError();
335 if (timetable_all && !v->HasCompleteTimetable()) return CommandError();
337 if (flags & DC_EXEC) {
338 SmallVector<Vehicle *, 8> vehs;
340 if (timetable_all) {
341 for (Vehicle *w = v->FirstShared(); w != nullptr; w = w->NextShared()) {
342 *vehs.Append() = w;
344 } else {
345 *vehs.Append() = v;
348 int total_duration = v->GetTimetableTotalDuration();
349 int num_vehs = vehs.Length();
351 if (num_vehs >= 2) {
352 QSortT(vehs.Begin(), vehs.Length(), &VehicleTimetableSorter);
355 int base = vehs.FindIndex(v);
357 for (Vehicle **viter = vehs.Begin(); viter != vehs.End(); viter++) {
358 int idx = (viter - vehs.Begin()) - base;
359 Vehicle *w = *viter;
361 w->lateness_counter = 0;
362 ClrBit(w->vehicle_flags, VF_TIMETABLE_STARTED);
363 /* Do multiplication, then division to reduce rounding errors. */
364 w->timetable_start = start_date + idx * total_duration / num_vehs / DAY_TICKS;
365 SetWindowDirty(WC_VEHICLE_TIMETABLE, w->index);
370 return CommandCost();
374 * Start or stop automatic management of timetables.
375 * @param tile Not used.
376 * @param flags Operation to perform.
377 * @param p1 Vehicle index.
378 * @param p2 Various bitstuffed elements
379 * - p2 = (bit 0) - Set to 1 to enable, 0 to disable automation.
380 * - p2 = (bit 1) - Ctrl was pressed. Used to keep wait times.
381 * @param text unused
382 * @return the cost of this operation or an error
384 CommandCost CmdAutomateTimetable(TileIndex index, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
386 VehicleID veh = GB(p1, 0, 16);
388 Vehicle *v = Vehicle::GetIfValid(veh);
389 if (v == nullptr || !v->IsPrimaryVehicle()) return CommandError();
391 CommandCost ret = CheckOwnership(v->owner);
392 if (ret.Failed()) return ret;
394 if (flags & DC_EXEC) {
395 for (Vehicle *v2 = v->FirstShared(); v2 != nullptr; v2 = v2->NextShared()) {
396 if (HasBit(p2, 0)) {
397 /* Automated timetable. */
398 SetBit(v2->vehicle_flags, VF_AUTOMATE_TIMETABLE);
400 if (HasBit(p2, 1)) {
401 SetBit(v2->vehicle_flags, VF_AUTOMATE_PRES_WAIT_TIME);
404 ClrBit(v2->vehicle_flags, VF_TIMETABLE_STARTED);
405 v2->timetable_start = 0;
406 v2->lateness_counter = 0;
407 v2->current_loading_time = 0;
409 else {
410 /* De-automate timetable. */
411 ClrBit(v2->vehicle_flags, VF_AUTOMATE_TIMETABLE);
412 ClrBit(v2->vehicle_flags, VF_AUTOMATE_PRES_WAIT_TIME);
414 SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
418 return CommandCost();
422 * Confirm all estimated wait and travel times as timetabled.
423 * @param tile Not used.
424 * @param flags Operation to perform.
425 * @param p1 Vehicle index.
426 * @param p2 Unused
427 * @param text unused
428 * @return the cost of this operation or an error
430 CommandCost CmdConfirmAll(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
432 VehicleID veh = GB(p1, 0, 20);
434 Vehicle *v = Vehicle::GetIfValid(veh);
435 if (v == nullptr || !v->IsPrimaryVehicle() || !v->HasOrdersList()) return CommandError();
437 CommandCost ret = CheckOwnership(v->owner);
438 if (ret.Failed()) return ret;
440 if (flags & DC_EXEC) {
441 int num_orders = v->GetNumOrders();
442 int timetable_delta = 0;
444 for (int i = 0; i < num_orders; ++i) {
445 Order* order = v->GetOrder(i);
447 assert(order != nullptr);
449 if (!order->IsType(OT_IMPLICIT)) {
450 if (order->GetWaitTime() != 0 && !order->IsWaitTimetabled()) {
451 timetable_delta += order->GetWaitTime() - order->GetTimetabledWait();
452 order->SetWaitTimetabled(true);
455 if (order->GetTravelTime() != 0 && !order->IsTravelTimetabled()) {
456 timetable_delta += order->GetTravelTime() - order->GetTimetabledTravel();
457 order->SetTravelTimetabled(true);
462 v->UpdateTimetableDuration(timetable_delta);
464 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
467 return CommandCost();
471 * Set new separation parameters
472 * @param tile Not used.
473 * @param flags Operation to perform.
474 * @param p1 Vehicle id.
475 * @param p2
476 * - p2 = (bit 0-2) - Separation mode (@see TTSepMode)
477 * - p2 = (bit 3-31) - Separation parameter (Unused if #TTS_MODE_OFF | #TTS_MODE_AUTO,
478 * Number of vehicles if #TTS_MODE_MAN_N, separation delay in ticks if #TTS_MODE_MAN_T).
479 * @param text Not used.
480 * @return The error or cost of the operation.
482 CommandCost CmdReinitSeparation(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char* text)
484 Vehicle* vehicle = Vehicle::GetIfValid(p1);
485 if (vehicle == nullptr || !vehicle->IsPrimaryVehicle()) return CommandError();
487 CommandCost ret = CheckOwnership(vehicle->owner);
488 if (ret.Failed()) return ret;
490 if (flags & DC_EXEC) {
491 vehicle->SetSepSettings(static_cast<TTSepMode>(GB(p2, 0, 3)), GB(p2, 3, 29));
493 InvalidateWindowData(WC_VEHICLE_TIMETABLE, vehicle->index);
496 return CommandCost();
500 * Update the timetable for the vehicle.
501 * @param v The vehicle to update the timetable for.
502 * @param travelling Whether we just traveled or waited at a station.
504 void UpdateVehicleTimetable(Vehicle *v, bool travelling)
506 if (!travelling) v->current_loading_time++; // +1 because this time is one tick behind
507 uint time_taken = v->current_order_time;
508 uint time_loading = v->current_loading_time;
510 // We are on our way so vehicle separation has finished.
511 if (travelling) ClrBit(v->vehicle_flags, VF_SEPARATION_IN_PROGRESS);
513 v->current_order_time = 0;
514 v->current_loading_time = 0;
516 if (v->current_order.IsType(OT_IMPLICIT)) return; // no timetabling of auto orders
518 if (v->cur_real_order_index >= v->GetNumOrders()) return;
519 Order *real_current_order = v->GetOrder(v->cur_real_order_index);
520 Order *real_timetable_order = v->cur_timetable_order_index != INVALID_VEH_ORDER_ID ? v->GetOrder(v->cur_timetable_order_index) : nullptr;
522 auto guard = scope_guard([v, travelling]() {
523 /* On next call, when updating waiting time, use current order even if travel field of current order isn't being updated */
524 if (travelling) v->cur_timetable_order_index = v->cur_real_order_index;
527 VehicleOrderID first_manual_order = 0;
528 for (Order *o = v->GetFirstOrder(); o != nullptr && o->IsType(OT_IMPLICIT); o = o->next) {
529 ++first_manual_order;
532 bool just_started = false;
534 /* Start automated timetables at first opportunity */
535 if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED) && HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE)) {
536 SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
538 v->lateness_counter = 0;
540 for (v = v->FirstShared(); v != nullptr; v = v->NextShared()) {
541 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
543 return;
546 /* This vehicle is arriving at the first destination in the timetable. */
547 if (v->cur_real_order_index == first_manual_order && travelling) {
548 v->trip_history.NewRound();
549 /* If the start date hasn't been set, or it was set automatically when
550 * the vehicle last arrived at the first destination, update it to the
551 * current time. Otherwise set the late counter appropriately to when
552 * the vehicle should have arrived. */
553 just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
555 if (v->timetable_start != 0) {
556 v->lateness_counter = (_date - v->timetable_start) * DAY_TICKS + _date_fract;
557 v->timetable_start = 0;
560 SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
561 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
564 if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) return;
565 if (real_timetable_order == nullptr) return;
567 bool is_conditional = real_timetable_order->IsType(OT_CONDITIONAL);
568 bool remeasure_wait_time = !is_conditional && (!real_current_order->IsWaitTimetabled() ||
569 (HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && !HasBit(v->vehicle_flags, VF_AUTOMATE_PRES_WAIT_TIME)));
571 if (travelling && remeasure_wait_time) {
572 /* We just finished traveling and want to remeasure the loading time,
573 * so do not apply any restrictions for the loading to finish. */
574 v->current_order.SetWaitTime(0);
577 bool travel_field = travelling;
578 if (is_conditional) {
579 if (travelling) {
580 /* conditional orders use the wait field for the jump-taken travel time */
581 travel_field = false;
582 } else {
583 /* doesn't make sense to update wait time for conditional orders */
584 return;
586 } else {
587 const auto type = v->type == VEH_TRAIN ?
588 "Train" :
589 v->type == VEH_ROAD ?
590 "Road Vehicle" :
591 v->type == VEH_SHIP ?
592 "Ship" :
593 "Aircraft";
595 assert_msg(real_timetable_order == real_current_order, "%u == %u on %s #%u", v->cur_timetable_order_index, v->cur_real_order_index, type, v->unitnumber);
598 if (just_started) return;
600 /* Before modifying waiting times, check whether we want to preserve bigger ones. */
601 if (travelling || (time_taken > real_timetable_order->GetWaitTime()) || remeasure_wait_time) {
602 /* For trains/aircraft multiple movement cycles are done in one
603 * tick. This makes it possible to leave the station and process
604 * e.g. a depot order in the same tick, causing it to not fill
605 * the timetable entry like is done for road vehicles/ships.
606 * Thus always make sure at least one tick is used between the
607 * processing of different orders when filling the timetable. */
608 if (travel_field && !real_timetable_order->IsTravelTimetabled()) {
609 ChangeTimetable(v, v->cur_timetable_order_index, max(time_taken, 1U), MTF_TRAVEL_TIME, false);
610 } else if (!travel_field && !real_timetable_order->IsWaitTimetabled()) {
611 ChangeTimetable(v, v->cur_timetable_order_index, max(time_loading, 1U), MTF_WAIT_TIME, false);
615 uint timetabled = travel_field ? real_timetable_order->GetTimetabledTravel() :
616 real_timetable_order->GetTimetabledWait();
618 /* Update the timetable to gradually shift order times towards the actual travel times. */
619 if (timetabled != 0 && HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && (travelling || !HasBit(v->vehicle_flags, VF_AUTOMATE_PRES_WAIT_TIME))) {
620 int32 new_time;
621 if (travelling) {
622 new_time = time_taken + _settings_game.order.timetable_auto_travel_buffer;
624 assert(_settings_game.order.timetable_auto_travel_rounding != 0);
626 if (new_time % _settings_game.order.timetable_auto_travel_rounding != 0) {
627 new_time /= _settings_game.order.timetable_auto_travel_rounding;
628 new_time += 1;
629 new_time *= _settings_game.order.timetable_auto_travel_rounding;
632 else {
633 new_time = time_loading + _settings_game.order.timetable_auto_load_buffer;
635 assert(_settings_game.order.timetable_auto_load_rounding != 0);
637 if (new_time % _settings_game.order.timetable_auto_load_rounding != 0) {
638 new_time /= _settings_game.order.timetable_auto_load_rounding;
639 new_time += 1;
640 new_time *= _settings_game.order.timetable_auto_load_rounding;
644 if (new_time > (int32)timetabled * 4 && travelling) {
645 /* Possible jam, clear time and restart timetable for all vehicles.
646 * Otherwise we risk trains blocking 1-lane stations for long times. */
647 ChangeTimetable(v, v->cur_timetable_order_index, 0, travel_field ? MTF_TRAVEL_TIME : MTF_WAIT_TIME, true);
648 for (Vehicle *v2 = v->FirstShared(); v2 != nullptr; v2 = v2->NextShared()) {
649 ClrBit(v2->vehicle_flags, VF_TIMETABLE_STARTED);
650 SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
652 return;
654 else if (!travelling || (new_time >= (int32)timetabled / 2)) {
655 /* Compute running average, with sign conversion to avoid negative overflow. */
656 if (new_time < (int32)timetabled) {
657 new_time = ((int32)timetabled * 3 + new_time * 2 + 2) / 5;
659 else {
660 new_time = ((int32)timetabled * 9 + new_time + 5) / 10;
663 else {
664 /* new time is less than half old time, set value directly */
667 if (new_time < 1) new_time = 1;
668 if (new_time != (int32)timetabled) {
669 ChangeTimetable(v, v->cur_timetable_order_index, new_time, travel_field ? MTF_TRAVEL_TIME : MTF_WAIT_TIME, true);
672 else if (timetabled == 0 && HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE)) {
673 /* Add times for orders that are not yet timetabled, even while not autofilling */
674 const int32 new_time = travelling ? time_taken : time_loading;
676 ChangeTimetable(v, v->cur_timetable_order_index, new_time, travel_field ? MTF_TRAVEL_TIME : MTF_WAIT_TIME, true);
679 /* Vehicles will wait at stations if they arrive early even if they are not
680 * timetabled to wait there, so make sure the lateness counter is updated
681 * when this happens. */
682 if (timetabled == 0 && (travelling || v->lateness_counter >= 0)) return;
684 v->lateness_counter -= (timetabled - time_taken);
686 /* When we are more late than this timetabled bit takes we (somewhat expensively)
687 * check how many ticks the (fully filled) timetable has. If a timetable cycle is
688 * shorter than the amount of ticks we are late we reduce the lateness by the
689 * length of a full cycle till lateness is less than the length of a timetable
690 * cycle. When the timetable isn't fully filled the cycle will be INVALID_TICKS. */
691 if (v->lateness_counter > (int)timetabled) {
692 Ticks cycle = v->GetTimetableTotalDuration();
693 if (cycle != INVALID_TICKS && v->lateness_counter > cycle) {
694 v->lateness_counter %= cycle;
698 for (v = v->FirstShared(); v != nullptr; v = v->NextShared()) {
699 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);