00001
00002
00003
00004
00005
00006
00007
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 #include "core/sort_func.hpp"
00020
00021 #include "table/strings.h"
00022
00030 static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, ModifyTimetableFlags mtf)
00031 {
00032 Order *order = v->GetOrder(order_number);
00033 int delta = 0;
00034
00035 switch (mtf) {
00036 case MTF_WAIT_TIME:
00037 delta = val - order->wait_time;
00038 order->wait_time = val;
00039 break;
00040
00041 case MTF_TRAVEL_TIME:
00042 delta = val - order->travel_time;
00043 order->travel_time = val;
00044 break;
00045
00046 case MTF_TRAVEL_SPEED:
00047 order->max_speed = val;
00048 break;
00049
00050 default:
00051 NOT_REACHED();
00052 }
00053 v->orders.list->UpdateOrderTimetable(delta);
00054
00055 for (v = v->FirstShared(); v != NULL; v = v->NextShared()) {
00056 if (v->cur_real_order_index == order_number && v->current_order.Equals(*order)) {
00057 switch (mtf) {
00058 case MTF_WAIT_TIME:
00059 v->current_order.wait_time = val;
00060 break;
00061
00062 case MTF_TRAVEL_TIME:
00063 v->current_order.travel_time = val;
00064 break;
00065
00066 case MTF_TRAVEL_SPEED:
00067 v->current_order.max_speed = val;
00068 break;
00069
00070 default:
00071 NOT_REACHED();
00072 }
00073 }
00074 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
00075 }
00076 }
00077
00091 CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00092 {
00093 VehicleID veh = GB(p1, 0, 20);
00094
00095 Vehicle *v = Vehicle::GetIfValid(veh);
00096 if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
00097
00098 CommandCost ret = CheckOwnership(v->owner);
00099 if (ret.Failed()) return ret;
00100
00101 VehicleOrderID order_number = GB(p1, 20, 8);
00102 Order *order = v->GetOrder(order_number);
00103 if (order == NULL || order->IsType(OT_IMPLICIT)) return CMD_ERROR;
00104
00105 ModifyTimetableFlags mtf = Extract<ModifyTimetableFlags, 28, 2>(p1);
00106 if (mtf >= MTF_END) return CMD_ERROR;
00107
00108 int wait_time = order->wait_time;
00109 int travel_time = order->travel_time;
00110 int max_speed = order->max_speed;
00111 switch (mtf) {
00112 case MTF_WAIT_TIME:
00113 wait_time = GB(p2, 0, 16);
00114 break;
00115
00116 case MTF_TRAVEL_TIME:
00117 travel_time = GB(p2, 0, 16);
00118 break;
00119
00120 case MTF_TRAVEL_SPEED:
00121 max_speed = GB(p2, 0, 16);
00122 if (max_speed == 0) max_speed = UINT16_MAX;
00123 break;
00124
00125 default:
00126 NOT_REACHED();
00127 }
00128
00129 if (wait_time != order->wait_time) {
00130 switch (order->GetType()) {
00131 case OT_GOTO_STATION:
00132 if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) return_cmd_error(STR_ERROR_TIMETABLE_NOT_STOPPING_HERE);
00133 break;
00134
00135 case OT_CONDITIONAL:
00136 break;
00137
00138 default: return_cmd_error(STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS);
00139 }
00140 }
00141
00142 if (travel_time != order->travel_time && order->IsType(OT_CONDITIONAL)) return CMD_ERROR;
00143 if (max_speed != order->max_speed && (order->IsType(OT_CONDITIONAL) || v->type == VEH_AIRCRAFT)) return CMD_ERROR;
00144
00145 if (flags & DC_EXEC) {
00146 if (wait_time != order->wait_time) ChangeTimetable(v, order_number, wait_time, MTF_WAIT_TIME);
00147 if (travel_time != order->travel_time) ChangeTimetable(v, order_number, travel_time, MTF_TRAVEL_TIME);
00148 if (max_speed != order->max_speed) ChangeTimetable(v, order_number, max_speed, MTF_TRAVEL_SPEED);
00149 }
00150
00151 return CommandCost();
00152 }
00153
00164 CommandCost CmdSetVehicleOnTime(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00165 {
00166 VehicleID veh = GB(p1, 0, 20);
00167
00168 Vehicle *v = Vehicle::GetIfValid(veh);
00169 if (v == NULL || !v->IsPrimaryVehicle() || v->orders.list == NULL) return CMD_ERROR;
00170
00171 CommandCost ret = CheckOwnership(v->owner);
00172 if (ret.Failed()) return ret;
00173
00174 if (flags & DC_EXEC) {
00175 v->lateness_counter = 0;
00176 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
00177 }
00178
00179 return CommandCost();
00180 }
00181
00190 static int CDECL VehicleTimetableSorter(Vehicle * const *ap, Vehicle * const *bp)
00191 {
00192 const Vehicle *a = *ap;
00193 const Vehicle *b = *bp;
00194
00195 VehicleOrderID a_order = a->cur_real_order_index;
00196 VehicleOrderID b_order = b->cur_real_order_index;
00197 int j = (int)b_order - (int)a_order;
00198
00199
00200 bool a_load = a->current_order.IsType(OT_LOADING) && a->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
00201 bool b_load = b->current_order.IsType(OT_LOADING) && b->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
00202
00203
00204
00205
00206
00207 if (!a_load) a_order--;
00208 if (!b_load) b_order--;
00209
00210
00211 int i = (int)b_order - (int)a_order;
00212 if (i != 0) return i;
00213 if (j != 0) return j;
00214
00215
00216 i = b->current_order_time - a->current_order_time;
00217 if (i != 0) return i;
00218
00219
00220 return b->unitnumber - a->unitnumber;
00221 }
00222
00234 CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00235 {
00236 bool timetable_all = HasBit(p1, 20);
00237 Vehicle *v = Vehicle::GetIfValid(GB(p1, 0, 20));
00238 if (v == NULL || !v->IsPrimaryVehicle() || v->orders.list == NULL) return CMD_ERROR;
00239
00240 CommandCost ret = CheckOwnership(v->owner);
00241 if (ret.Failed()) return ret;
00242
00243
00244 Date start_date = (Date)p2;
00245 if (start_date < 0 || start_date > MAX_DAY) return CMD_ERROR;
00246 if (start_date - _date > 15 * DAYS_IN_LEAP_YEAR) return CMD_ERROR;
00247 if (_date - start_date > DAYS_IN_LEAP_YEAR) return CMD_ERROR;
00248 if (timetable_all && !v->orders.list->IsCompleteTimetable()) return CMD_ERROR;
00249
00250 if (flags & DC_EXEC) {
00251 SmallVector<Vehicle *, 8> vehs;
00252
00253 if (timetable_all) {
00254 for (Vehicle *w = v->orders.list->GetFirstSharedVehicle(); w != NULL; w = w->NextShared()) {
00255 *vehs.Append() = w;
00256 }
00257 } else {
00258 *vehs.Append() = v;
00259 }
00260
00261 int total_duration = v->orders.list->GetTimetableTotalDuration();
00262 int num_vehs = vehs.Length();
00263
00264 if (num_vehs >= 2) {
00265 QSortT(vehs.Begin(), vehs.Length(), &VehicleTimetableSorter);
00266 }
00267
00268 int base = vehs.FindIndex(v);
00269
00270 for (Vehicle **viter = vehs.Begin(); viter != vehs.End(); viter++) {
00271 int idx = (viter - vehs.Begin()) - base;
00272 Vehicle *w = *viter;
00273
00274 w->lateness_counter = 0;
00275 ClrBit(w->vehicle_flags, VF_TIMETABLE_STARTED);
00276
00277 w->timetable_start = start_date + idx * total_duration / num_vehs / DAY_TICKS;
00278 SetWindowDirty(WC_VEHICLE_TIMETABLE, w->index);
00279 }
00280
00281 }
00282
00283 return CommandCost();
00284 }
00285
00286
00300 CommandCost CmdAutofillTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00301 {
00302 VehicleID veh = GB(p1, 0, 20);
00303
00304 Vehicle *v = Vehicle::GetIfValid(veh);
00305 if (v == NULL || !v->IsPrimaryVehicle() || v->orders.list == NULL) return CMD_ERROR;
00306
00307 CommandCost ret = CheckOwnership(v->owner);
00308 if (ret.Failed()) return ret;
00309
00310 if (flags & DC_EXEC) {
00311 if (HasBit(p2, 0)) {
00312
00313
00314
00315 SetBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
00316 ClrBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
00317
00318
00319 if (HasBit(p2, 1)) SetBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
00320
00321 v->timetable_start = 0;
00322 v->lateness_counter = 0;
00323 } else {
00324 ClrBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
00325 ClrBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
00326 }
00327
00328 for (Vehicle *v2 = v->FirstShared(); v2 != NULL; v2 = v2->NextShared()) {
00329 if (v2 != v) {
00330
00331 ClrBit(v2->vehicle_flags, VF_AUTOFILL_TIMETABLE);
00332 ClrBit(v2->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
00333 }
00334 SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
00335 }
00336 }
00337
00338 return CommandCost();
00339 }
00340
00346 void UpdateVehicleTimetable(Vehicle *v, bool travelling)
00347 {
00348 uint timetabled = travelling ? v->current_order.travel_time : v->current_order.wait_time;
00349 uint time_taken = v->current_order_time;
00350
00351 v->current_order_time = 0;
00352
00353 if (v->current_order.IsType(OT_IMPLICIT)) return;
00354
00355 VehicleOrderID first_manual_order = 0;
00356 for (Order *o = v->GetFirstOrder(); o != NULL && o->IsType(OT_IMPLICIT); o = o->next) {
00357 ++first_manual_order;
00358 }
00359
00360 bool just_started = false;
00361
00362
00363 if (v->cur_real_order_index == first_manual_order && travelling) {
00364
00365
00366
00367
00368 just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
00369
00370 if (v->timetable_start != 0) {
00371 v->lateness_counter = (_date - v->timetable_start) * DAY_TICKS + _date_fract;
00372 v->timetable_start = 0;
00373 }
00374
00375 SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
00376 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
00377 }
00378
00379 if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) return;
00380
00381 if (HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)) {
00382 if (travelling && !HasBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME)) {
00383
00384 v->current_order.wait_time = 0;
00385 }
00386
00387 if (just_started) return;
00388
00389
00390
00391 if (!v->current_order.IsType(OT_CONDITIONAL) && (travelling || time_taken > v->current_order.wait_time)) {
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401 time_taken = CeilDiv(max(time_taken, 1U), DAY_TICKS) * DAY_TICKS;
00402
00403 ChangeTimetable(v, v->cur_real_order_index, time_taken, travelling ? MTF_TRAVEL_TIME : MTF_WAIT_TIME);
00404 }
00405
00406 if (v->cur_real_order_index == first_manual_order && travelling) {
00407
00408
00409
00410 ClrBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
00411 ClrBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
00412 }
00413 return;
00414 }
00415
00416 if (just_started) return;
00417
00418
00419
00420
00421 if (timetabled == 0 && (travelling || v->lateness_counter >= 0)) return;
00422
00423 v->lateness_counter -= (timetabled - time_taken);
00424
00425
00426
00427
00428
00429
00430 if (v->lateness_counter > (int)timetabled) {
00431 Ticks cycle = v->orders.list->GetTimetableTotalDuration();
00432 if (cycle != INVALID_TICKS && v->lateness_counter > cycle) {
00433 v->lateness_counter %= cycle;
00434 }
00435 }
00436
00437 for (v = v->FirstShared(); v != NULL; v = v->NextShared()) {
00438 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
00439 }
00440 }