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 "tile_cmd.h"
00019 #include "settings_type.h"
00020
00021 #include "table/strings.h"
00022
00030 static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 time, bool is_journey)
00031 {
00032 Order *order = v->GetOrder(order_number);
00033 int delta;
00034
00035 if (is_journey) {
00036 delta = time - order->travel_time;
00037 order->travel_time = time;
00038 } else {
00039 delta = time - order->wait_time;
00040 order->wait_time = time;
00041 }
00042 v->orders.list->UpdateOrderTimetable(delta);
00043
00044 for (v = v->FirstShared(); v != NULL; v = v->NextShared()) {
00045 if (v->cur_real_order_index == order_number && v->current_order.Equals(*order)) {
00046 if (is_journey) {
00047 v->current_order.travel_time = time;
00048 } else {
00049 v->current_order.wait_time = time;
00050 }
00051 }
00052 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
00053 }
00054 }
00055
00070 CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00071 {
00072 if (!_settings_game.order.timetabling) return CMD_ERROR;
00073
00074 VehicleID veh = GB(p1, 0, 20);
00075
00076 Vehicle *v = Vehicle::GetIfValid(veh);
00077 if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
00078
00079 CommandCost ret = CheckOwnership(v->owner);
00080 if (ret.Failed()) return ret;
00081
00082 VehicleOrderID order_number = GB(p1, 20, 8);
00083 Order *order = v->GetOrder(order_number);
00084 if (order == NULL || order->IsType(OT_IMPLICIT)) return CMD_ERROR;
00085
00086 bool is_journey = HasBit(p1, 28);
00087
00088 int wait_time = order->wait_time;
00089 int travel_time = order->travel_time;
00090 if (is_journey) {
00091 travel_time = GB(p2, 0, 16);
00092 } else {
00093 wait_time = GB(p2, 0, 16);
00094 }
00095
00096 if (wait_time != order->wait_time) {
00097 switch (order->GetType()) {
00098 case OT_GOTO_STATION:
00099 if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) return_cmd_error(STR_ERROR_TIMETABLE_NOT_STOPPING_HERE);
00100 break;
00101
00102 case OT_CONDITIONAL:
00103 break;
00104
00105 default: return_cmd_error(STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS);
00106 }
00107 }
00108
00109 if (travel_time != order->travel_time && order->IsType(OT_CONDITIONAL)) return CMD_ERROR;
00110
00111 if (flags & DC_EXEC) {
00112 if (wait_time != order->wait_time) ChangeTimetable(v, order_number, wait_time, false);
00113 if (travel_time != order->travel_time) ChangeTimetable(v, order_number, travel_time, true);
00114 }
00115
00116 return CommandCost();
00117 }
00118
00129 CommandCost CmdSetVehicleOnTime(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00130 {
00131 if (!_settings_game.order.timetabling) return CMD_ERROR;
00132
00133 VehicleID veh = GB(p1, 0, 20);
00134
00135 Vehicle *v = Vehicle::GetIfValid(veh);
00136 if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
00137
00138 CommandCost ret = CheckOwnership(v->owner);
00139 if (ret.Failed()) return ret;
00140
00141 if (flags & DC_EXEC) {
00142 v->lateness_counter = 0;
00143 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
00144 }
00145
00146 return CommandCost();
00147 }
00148
00158 CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00159 {
00160 if (!_settings_game.order.timetabling) return CMD_ERROR;
00161
00162 Vehicle *v = Vehicle::GetIfValid(GB(p1, 0, 20));
00163 if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
00164
00165 CommandCost ret = CheckOwnership(v->owner);
00166 if (ret.Failed()) return ret;
00167
00168 DateTicks start_date = (Date)p2 / DAY_TICKS;
00169
00170 #if WALLCLOCK_NETWORK_COMPATIBLE
00171
00172 if (start_date < 0 || start_date > MAX_DAY) return CMD_ERROR;
00173 if (start_date - _date > 15 * DAYS_IN_LEAP_YEAR) return CMD_ERROR;
00174 if (_date - start_date > DAYS_IN_LEAP_YEAR) return CMD_ERROR;
00175 #else
00176 start_date = ((DateTicks)_date * DAY_TICKS) + _date_fract + (DateTicks)(int32)p2;
00177 #endif
00178
00179 if (flags & DC_EXEC) {
00180 v->lateness_counter = 0;
00181 ClrBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
00182 v->timetable_start = start_date;
00183
00184 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
00185 }
00186
00187 return CommandCost();
00188 }
00189
00190
00204 CommandCost CmdAutofillTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00205 {
00206 if (!_settings_game.order.timetabling) return CMD_ERROR;
00207
00208 VehicleID veh = GB(p1, 0, 20);
00209
00210 Vehicle *v = Vehicle::GetIfValid(veh);
00211 if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
00212
00213 CommandCost ret = CheckOwnership(v->owner);
00214 if (ret.Failed()) return ret;
00215
00216 if (flags & DC_EXEC) {
00217 if (HasBit(p2, 0)) {
00218
00219
00220
00221 SetBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
00222 ClrBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
00223
00224
00225 if (HasBit(p2, 1)) SetBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
00226
00227 v->timetable_start = 0;
00228 v->lateness_counter = 0;
00229 } else {
00230 ClrBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
00231 ClrBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
00232 }
00233
00234 for (Vehicle *v2 = v->FirstShared(); v2 != NULL; v2 = v2->NextShared()) {
00235 if (v2 != v) {
00236
00237 ClrBit(v2->vehicle_flags, VF_AUTOFILL_TIMETABLE);
00238 ClrBit(v2->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
00239 }
00240 SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
00241 }
00242 }
00243
00244 return CommandCost();
00245 }
00246
00258 CommandCost CmdAutomateTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00259 {
00260 if (!_settings_game.order.timetable_automated) return CMD_ERROR;
00261
00262 VehicleID veh = GB(p1, 0, 16);
00263
00264 Vehicle *v = Vehicle::GetIfValid(veh);
00265 if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
00266
00267 CommandCost ret = CheckOwnership(v->owner);
00268 if (ret.Failed()) return ret;
00269
00270 if (flags & DC_EXEC) {
00271 for (Vehicle *v2 = v->FirstShared(); v2 != NULL; v2 = v2->NextShared()) {
00272 if (HasBit(p2, 0)) {
00273
00274 SetBit(v2->vehicle_flags, VF_AUTOMATE_TIMETABLE);
00275 ClrBit(v2->vehicle_flags, VF_AUTOFILL_TIMETABLE);
00276 ClrBit(v2->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
00277 ClrBit(v2->vehicle_flags, VF_TIMETABLE_STARTED);
00278 v2->timetable_start = 0;
00279 v2->lateness_counter = 0;
00280 v2->current_loading_time = 0;
00281 v2->ClearSeparation();
00282 } else {
00283
00284 ClrBit(v2->vehicle_flags, VF_AUTOMATE_TIMETABLE);
00285 ClrBit(v2->vehicle_flags, VF_AUTOFILL_TIMETABLE);
00286 ClrBit(v2->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
00287 v2->ClearSeparation();
00288 if (!HasBit(p2, 1)) {
00289
00290 SetBit(v2->vehicle_flags, VF_TIMETABLE_STARTED);
00291 v2->timetable_start = 0;
00292 v2->lateness_counter = 0;
00293 v2->current_loading_time = 0;
00294 OrderList *orders = v2->orders.list;
00295 if (orders != NULL) {
00296 for (int i = 0; i < orders->GetNumOrders(); i++) {
00297 ChangeTimetable(v2, i, 0, false);
00298 ChangeTimetable(v2, i, 0, true);
00299 }
00300 }
00301 }
00302 }
00303 SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
00304 }
00305 }
00306
00307 return CommandCost();
00308 }
00309
00310 int TimeToFinishOrder(Vehicle *v, int n)
00311 {
00312 int left;
00313 Order *order = v->GetOrder(n);
00314 assert(order != NULL);
00315 if ((v->cur_real_order_index == n) && (v->last_station_visited == order->GetDestination())) {
00316 if (order->wait_time == 0) return -1;
00317 if (v->current_loading_time > 0) {
00318 left = order->wait_time - v->current_order_time;
00319 } else {
00320 left = order->wait_time;
00321 }
00322 if (left < 0) left = 0;
00323 } else {
00324 left = order->travel_time;
00325 if (v->cur_real_order_index == n) left -= v->current_order_time;
00326 if (order->travel_time == 0 || order->wait_time == 0) return -1;
00327 if (left < 0) left = 0;
00328 left += order->wait_time;
00329 }
00330 return left;
00331 }
00332
00333 int SeparationBetween(Vehicle *v1, Vehicle *v2)
00334 {
00335 if(v1 == v2) return -1;
00336 int separation = 0;
00337 int time;
00338 int n = v1->cur_real_order_index;
00339 while (n != v2->cur_real_order_index) {
00340 time = TimeToFinishOrder(v1, n);
00341 if (time == -1) return -1;
00342 separation += time;
00343 n++;
00344 if (n >= v1->GetNumOrders()) n = 0;
00345 }
00346 int time1 = TimeToFinishOrder(v1, n);
00347 int time2 = TimeToFinishOrder(v2, n);
00348 if (time1 == -1 || time2 == -1) return -1;
00349 time = time1 - time2;
00350 if (time < 0) {
00351 for (n = 0; n < v1->GetNumOrders(); n++) {
00352 Order *order = v1->GetOrder(n);
00353 if (order->travel_time == 0 || order->wait_time == 0) return -1;
00354 time += order->travel_time + order->wait_time;
00355 }
00356 }
00357 separation += time;
00358 assert(separation >= 0);
00359 if (separation == 0) return -1;
00360 return separation;
00361 }
00362
00363 void UpdateSeparationOrder(Vehicle *v_start)
00364 {
00365
00366 if (v_start->AheadSeparation() == NULL) {
00367 v_start->InitSeparation();
00368 }
00369 if (v_start->AheadSeparation() == NULL) {
00370 return;
00371 }
00372
00373 int swaps = 0;
00374 bool done = false;
00375 while (!done) {
00376 done = true;
00377 int min_sep = SeparationBetween(v_start, v_start->AheadSeparation());
00378 Vehicle *v = v_start;
00379 do {
00380 if (v != v_start) {
00381 int tmp_sep = SeparationBetween(v_start, v);
00382 if (tmp_sep < min_sep && tmp_sep != -1) {
00383 swaps++;
00384 if (swaps >= 50) {
00385 return;
00386 }
00387 done = false;
00388 v_start->ClearSeparation();
00389 v_start->AddToSeparationBehind(v);
00390 break;
00391 }
00392 }
00393 int separation_ahead = SeparationBetween(v, v->AheadSeparation());
00394 int separation_behind = SeparationBetween(v->BehindSeparation(), v);
00395 v->lateness_counter = (separation_ahead - separation_behind) / 2;
00396 if (separation_ahead == -1 || separation_behind == -1) v->lateness_counter = 0;
00397 v = v->AheadSeparation();
00398 } while (v != v_start);
00399 }
00400 }
00401
00407 void UpdateVehicleTimetable(Vehicle *v, bool travelling)
00408 {
00409 uint timetabled = travelling ? v->current_order.travel_time : v->current_order.wait_time;
00410 if (!travelling) v->current_loading_time++;
00411 uint time_taken = v->current_order_time;
00412 uint time_loading = v->current_loading_time;
00413
00414 v->current_order_time = 0;
00415 v->current_loading_time = 0;
00416
00417 if (!_settings_game.order.timetabling) return;
00418 if (v->current_order.IsType(OT_IMPLICIT)) return;
00419
00420 VehicleOrderID first_manual_order = 0;
00421 for (Order *o = v->GetFirstOrder(); o != NULL && o->IsType(OT_IMPLICIT); o = o->next) {
00422 ++first_manual_order;
00423 }
00424
00425 bool just_started = false;
00426
00427
00428 if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED) && HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE)) {
00429 if (_settings_game.order.timetable_separation) v->ClearSeparation();
00430 SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
00431 v->lateness_counter = 0;
00432 if (_settings_game.order.timetable_separation) UpdateSeparationOrder(v);
00433 for (v = v->FirstShared(); v != NULL; v = v->NextShared()) {
00434 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
00435 }
00436 return;
00437 }
00438
00439
00440 if (v->cur_real_order_index == first_manual_order && travelling) {
00441
00442
00443
00444
00445 just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
00446
00447 if (v->timetable_start != 0) {
00448 #if WALLCLOCK_NETWORK_COMPATIBLE
00449 v->lateness_counter = (_date - v->timetable_start) * DAY_TICKS + _date_fract;
00450 #else
00451 v->lateness_counter = (_date * DAY_TICKS) + _date_fract - v->timetable_start;
00452 #endif
00453 v->timetable_start = 0;
00454 }
00455
00456 SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
00457 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
00458 }
00459
00460 if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) return;
00461
00462 if (HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)) {
00463 if (travelling && !HasBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME)) {
00464
00465 v->current_order.wait_time = 0;
00466 }
00467
00468 if (just_started) return;
00469
00470
00471
00472 if (!v->current_order.IsType(OT_CONDITIONAL) && (travelling || time_taken > v->current_order.wait_time)) {
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482 time_taken = CeilDiv(max(time_taken, 1U), DATE_UNIT_SIZE) * DATE_UNIT_SIZE;
00483
00484 ChangeTimetable(v, v->cur_real_order_index, time_taken, travelling);
00485 }
00486
00487 if (v->cur_real_order_index == first_manual_order && travelling) {
00488
00489
00490
00491 ClrBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE);
00492 ClrBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
00493 }
00494 return;
00495 }
00496
00497
00498
00499 if (v->orders.list->IsCompleteTimetable()) {
00500 Ticks duration = v->orders.list->GetTimetableTotalDuration();
00501 while (v->lateness_counter > duration) {
00502 v->lateness_counter -= duration;
00503 }
00504 }
00505
00506 if (just_started) return;
00507
00508
00509 if (timetabled != 0 && HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE)) {
00510 int32 new_time;
00511 if (travelling) {
00512 new_time = time_taken;
00513 } else {
00514 new_time = time_loading;
00515 }
00516
00517
00518 if (!(new_time > (int32)timetabled * 2 || new_time < (int32)timetabled / 2)) {
00519 int arrival_error = timetabled - new_time;
00520
00521 new_time = ((int32)timetabled * 4 + new_time + 2) / 5;
00522
00523 if (arrival_error < 0) new_time++;
00524 if (arrival_error > 0) new_time--;
00525 } else if (new_time > (int32)timetabled * 10 && travelling) {
00526
00527
00528 ChangeTimetable(v, v->cur_real_order_index, 0, travelling);
00529 for (Vehicle *v2 = v->FirstShared(); v2 != NULL; v2 = v2->NextShared()) {
00530 if (_settings_game.order.timetable_separation) v2->ClearSeparation();
00531 ClrBit(v2->vehicle_flags, VF_TIMETABLE_STARTED);
00532 SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
00533 }
00534 return;
00535 }
00536
00537 if (new_time < 1) new_time = 1;
00538 if (new_time != (int32)timetabled)
00539 ChangeTimetable(v, v->cur_real_order_index, new_time, travelling);
00540 } else if (timetabled == 0 && HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE)) {
00541
00542 if (travelling)
00543 ChangeTimetable(v, v->cur_real_order_index, time_taken, travelling);
00544 else
00545 ChangeTimetable(v, v->cur_real_order_index, time_loading, travelling);
00546 }
00547
00548
00549
00550
00551 if (timetabled == 0 && (travelling || v->lateness_counter >= 0)) return;
00552
00553 if (_settings_game.order.timetable_separation && HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) {
00554 v->current_order_time = time_taken;
00555 v->current_loading_time = time_loading;
00556 UpdateSeparationOrder(v);
00557 v->current_order_time = 0;
00558 v->current_loading_time = 0;
00559 } else {
00560 v->lateness_counter -= (timetabled - time_taken);
00561 }
00562
00563
00564
00565
00566
00567
00568 if (v->lateness_counter > (int)timetabled) {
00569 Ticks cycle = v->orders.list->GetTimetableTotalDuration();
00570 if (cycle != INVALID_TICKS && v->lateness_counter > cycle) {
00571 v->lateness_counter %= cycle;
00572 }
00573 }
00574
00575 for (v = v->FirstShared(); v != NULL; v = v->NextShared()) {
00576 SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
00577 }
00578 }