# timetable_cmd.cpp

Go to the documentation of this file.

    00001 /* $Id: timetable_cmd.cpp 24053 2012-03-20 13:11:20Z michi_cc $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "command_func.h"
    00014 #include "company_func.h"
    00015 #include "date_func.h"
    00016 #include "window_func.h"
    00017 #include "vehicle_base.h"
    00018 #include "cmd_helper.h"
    00019
    00020 #include "table/strings.h"
    00021
    00029 static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, ModifyTimetableFlags mtf)
    00030 {
    00031   Order *order = v->GetOrder(order_number);
    00032   int delta = 0;
    00033
    00034   switch (mtf) {
    00035     case MTF_WAIT_TIME:
    00036       delta = val - order->wait_time;
    00037       order->wait_time = val;
    00038       break;
    00039
    00040     case MTF_TRAVEL_TIME:
    00041       delta = val - order->travel_time;
    00042       order->travel_time = val;
    00043       break;
    00044
    00045     case MTF_TRAVEL_SPEED:
    00046       order->max_speed = val;
    00047       break;
    00048
    00049     default:
    00050       NOT_REACHED();
    00051   }
    00052   v->orders.list->UpdateOrderTimetable(delta);
    00053
    00054   for (v = v->FirstShared(); v != NULL; v = v->NextShared()) {
    00055     if (v->cur_real_order_index == order_number && v->current_order.Equals(*order)) {
    00056       switch (mtf) {
    00057         case MTF_WAIT_TIME:
    00058           v->current_order.wait_time = val;
    00059           break;
    00060
    00061         case MTF_TRAVEL_TIME:
    00062           v->current_order.travel_time = val;
    00063           break;
    00064
    00065         case MTF_TRAVEL_SPEED:
    00066           v->current_order.max_speed = val;
    00067           break;
    00068
    00069         default:
    00070           NOT_REACHED();
    00071       }
    00072     }
    00073     SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
    00074   }
    00075 }
    00076
    00090 CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00091 {
    00092   VehicleID veh = GB(p1, 0, 20);
    00093
    00094   Vehicle *v = Vehicle::GetIfValid(veh);
    00095   if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
    00096
    00097   CommandCost ret = CheckOwnership(v->owner);
    00098   if (ret.Failed()) return ret;
    00099
    00100   VehicleOrderID order_number = GB(p1, 20, 8);
    00101   Order *order = v->GetOrder(order_number);
    00102   if (order == NULL || order->IsType(OT_IMPLICIT)) return CMD_ERROR;
    00103
    00104   ModifyTimetableFlags mtf = Extract<ModifyTimetableFlags, 28, 2>(p1);
    00105   if (mtf >= MTF_END) return CMD_ERROR;
    00106
    00107   int wait_time   = order->wait_time;
    00108   int travel_time = order->travel_time;
    00109   int max_speed   = order->max_speed;
    00110   switch (mtf) {
    00111     case MTF_WAIT_TIME:
    00112       wait_time = GB(p2, 0, 16);
    00113       break;
    00114
    00115     case MTF_TRAVEL_TIME:
    00116       travel_time = GB(p2, 0, 16);
    00117       break;
    00118
    00119     case MTF_TRAVEL_SPEED:
    00120       max_speed = GB(p2, 0, 16);
    00121       if (max_speed == 0) max_speed = UINT16_MAX; // Disable speed limit.
    00122       break;
    00123
    00124     default:
    00125       NOT_REACHED();
    00126   }
    00127
    00128   if (wait_time != order->wait_time) {
    00129     switch (order->GetType()) {
    00130       case OT_GOTO_STATION:
    00131         if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) return_cmd_error(STR_ERROR_TIMETABLE_NOT_STOPPING_HERE);
    00132         break;
    00133
    00134       case OT_CONDITIONAL:
    00135         break;
    00136
    00137       default: return_cmd_error(STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS);
    00138     }
    00139   }
    00140
    00141   if (travel_time != order->travel_time && order->IsType(OT_CONDITIONAL)) return CMD_ERROR;
    00142   if (max_speed != order->max_speed && (order->IsType(OT_CONDITIONAL) || v->type == VEH_AIRCRAFT)) return CMD_ERROR;
    00143
    00144   if (flags & DC_EXEC) {
    00145     if (wait_time   != order->wait_time)   ChangeTimetable(v, order_number, wait_time,   MTF_WAIT_TIME);
    00146     if (travel_time != order->travel_time) ChangeTimetable(v, order_number, travel_time, MTF_TRAVEL_TIME);
    00147     if (max_speed   != order->max_speed)   ChangeTimetable(v, order_number, max_speed,   MTF_TRAVEL_SPEED);
    00148   }
    00149
    00150   return CommandCost();
    00151 }
    00152
    00163 CommandCost CmdSetVehicleOnTime(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00164 {
    00165   VehicleID veh = GB(p1, 0, 20);
    00166
    00167   Vehicle *v = Vehicle::GetIfValid(veh);
    00168   if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
    00169
    00170   CommandCost ret = CheckOwnership(v->owner);
    00171   if (ret.Failed()) return ret;
    00172
    00173   if (flags & DC_EXEC) {
    00174     v->lateness_counter = 0;
    00175     SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
    00176   }
    00177
    00178   return CommandCost();
    00179 }
    00180
    00190 CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00191 {
    00192   Vehicle *v = Vehicle::GetIfValid(GB(p1, 0, 20));
    00193   if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
    00194
    00195   CommandCost ret = CheckOwnership(v->owner);
    00196   if (ret.Failed()) return ret;
    00197
    00198   /* Don't let a timetable start more than 15 years into the future or 1 year in the past. */
    00199   Date start_date = (Date)p2;
    00200   if (start_date < 0 || start_date > MAX_DAY) return CMD_ERROR;
    00201   if (start_date - _date > 15 * DAYS_IN_LEAP_YEAR) return CMD_ERROR;
    00202   if (_date - start_date > DAYS_IN_LEAP_YEAR) return CMD_ERROR;
    00203
    00204   if (flags & DC_EXEC) {
    00205     v->lateness_counter = 0;
    00206     ClrBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
    00207     v->timetable_start = start_date;
    00208
    00209     SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
    00210   }
    00211
    00212   return CommandCost();
    00213 }
    00214
    00215
    00229 CommandCost CmdAutofillTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00230 {
    00231   VehicleID veh = GB(p1, 0, 20);
    00232
    00233   Vehicle *v = Vehicle::GetIfValid(veh);
    00234   if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
    00235
    00236   CommandCost ret = CheckOwnership(v->owner);
    00237   if (ret.Failed()) return ret;
    00238
    00239   if (flags & DC_EXEC) {
    00240     if (HasBit(p2, 0)) {
    00241       /* Start autofilling the timetable, which clears the
    00242        * "timetable has started" bit. Times are not cleared anymore, but are
    00243        * overwritten when the order is reached now. */
    00244       SetBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
    00245       ClrBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
    00246
    00247       /* Overwrite waiting times only if they got longer */
    00248       if (HasBit(p2, 1)) SetBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
    00249
    00250       v->timetable_start = 0;
    00251       v->lateness_counter = 0;
    00252     } else {
    00253       ClrBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
    00254       ClrBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
    00255     }
    00256
    00257     for (Vehicle *v2 = v->FirstShared(); v2 != NULL; v2 = v2->NextShared()) {
    00258       if (v2 != v) {
    00259         /* Stop autofilling; only one vehicle at a time can perform autofill */
    00260         ClrBit(v2->vehicle_flags, VF_AUTOFILL_TIMETABLE);
    00261         ClrBit(v2->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
    00262       }
    00263       SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
    00264     }
    00265   }
    00266
    00267   return CommandCost();
    00268 }
    00269
    00275 void UpdateVehicleTimetable(Vehicle *v, bool travelling)
    00276 {
    00277   uint timetabled = travelling ? v->current_order.travel_time : v->current_order.wait_time;
    00278   uint time_taken = v->current_order_time;
    00279
    00280   v->current_order_time = 0;
    00281
    00282   if (v->current_order.IsType(OT_IMPLICIT)) return; // no timetabling of auto orders
    00283
    00284   VehicleOrderID first_manual_order = 0;
    00285   for (Order *o = v->GetFirstOrder(); o != NULL && o->IsType(OT_IMPLICIT); o = o->next) {
    00286     ++first_manual_order;
    00287   }
    00288
    00289   bool just_started = false;
    00290
    00291   /* This vehicle is arriving at the first destination in the timetable. */
    00292   if (v->cur_real_order_index == first_manual_order && travelling) {
    00293     /* If the start date hasn't been set, or it was set automatically when
    00294      * the vehicle last arrived at the first destination, update it to the
    00295      * current time. Otherwise set the late counter appropriately to when
    00296      * the vehicle should have arrived. */
    00297     just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
    00298
    00299     if (v->timetable_start != 0) {
    00300       v->lateness_counter = (_date - v->timetable_start) * DAY_TICKS + _date_fract;
    00301       v->timetable_start = 0;
    00302     }
    00303
    00304     SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
    00305     SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
    00306   }
    00307
    00308   if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) return;
    00309
    00310   if (HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)) {
    00311     if (travelling && !HasBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME)) {
    00312       /* Need to clear that now as otherwise we are not able to reduce the wait time */
    00313       v->current_order.wait_time = 0;
    00314     }
    00315
    00316     if (just_started) return;
    00317
    00318     /* Modify station waiting time only if our new value is larger (this is
    00319      * always the case when we cleared the timetable). */
    00320     if (!v->current_order.IsType(OT_CONDITIONAL) && (travelling || time_taken > v->current_order.wait_time)) {
    00321       /* Round the time taken up to the nearest day, as this will avoid
    00322        * confusion for people who are timetabling in days, and can be
    00323        * adjusted later by people who aren't.
    00324        * For trains/aircraft multiple movement cycles are done in one
    00325        * tick. This makes it possible to leave the station and process
    00326        * e.g. a depot order in the same tick, causing it to not fill
    00327        * the timetable entry like is done for road vehicles/ships.
    00328        * Thus always make sure at least one tick is used between the
    00329        * processing of different orders when filling the timetable. */
    00330       time_taken = CeilDiv(max(time_taken, 1U), DAY_TICKS) * DAY_TICKS;
    00331
    00332       ChangeTimetable(v, v->cur_real_order_index, time_taken, travelling ? MTF_TRAVEL_TIME : MTF_WAIT_TIME);
    00333     }
    00334
    00335     if (v->cur_real_order_index == first_manual_order && travelling) {
    00336       /* If we just started we would have returned earlier and have not reached
    00337        * this code. So obviously, we have completed our round: So turn autofill
    00338        * off again. */
    00339       ClrBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
    00340       ClrBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
    00341     }
    00342     return;
    00343   }
    00344
    00345   if (just_started) return;
    00346
    00347   /* Vehicles will wait at stations if they arrive early even if they are not
    00348    * timetabled to wait there, so make sure the lateness counter is updated
    00349    * when this happens. */
    00350   if (timetabled == 0 && (travelling || v->lateness_counter >= 0)) return;
    00351
    00352   v->lateness_counter -= (timetabled - time_taken);
    00353
    00354   /* When we are more late than this timetabled bit takes we (somewhat expensively)
    00355    * check how many ticks the (fully filled) timetable has. If a timetable cycle is
    00356    * shorter than the amount of ticks we are late we reduce the lateness by the
    00357    * length of a full cycle till lateness is less than the length of a timetable
    00358    * cycle. When the timetable isn't fully filled the cycle will be INVALID_TICKS. */
    00359   if (v->lateness_counter > (int)timetabled) {
    00360     Ticks cycle = v->orders.list->GetTimetableTotalDuration();
    00361     if (cycle != INVALID_TICKS && v->lateness_counter > cycle) {
    00362       v->lateness_counter %= cycle;
    00363     }
    00364   }
    00365
    00366   for (v = v->FirstShared(); v != NULL; v = v->NextShared()) {
    00367     SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
    00368   }
    00369 }