* Set the start date of the timetable. * @param flags Operation to perform. * @param veh_id Vehicle ID. * @param timetable_all Set to set timetable start for all vehicles sharing this order * @param start_tick The TimerGameTick::counter tick when the timetable starts. * @return The error or cost of the operation. */
| 349 | * @return The error or cost of the operation. |
| 350 | */ |
| 351 | CommandCost CmdSetTimetableStart(DoCommandFlags flags, VehicleID veh_id, bool timetable_all, TimerGameTick::TickCounter start_tick) |
| 352 | { |
| 353 | Vehicle *v = Vehicle::GetIfValid(veh_id); |
| 354 | if (v == nullptr || !v->IsPrimaryVehicle() || v->orders == nullptr) return CMD_ERROR; |
| 355 | |
| 356 | CommandCost ret = CheckOwnership(v->owner); |
| 357 | if (ret.Failed()) return ret; |
| 358 | |
| 359 | TimerGameTick::Ticks total_duration = v->orders->GetTimetableTotalDuration(); |
| 360 | |
| 361 | TimerGameEconomy::Date start_date = GetDateFromStartTick(start_tick); |
| 362 | |
| 363 | /* Don't let a timetable start at an invalid date. */ |
| 364 | if (start_date < 0 || start_date > EconomyTime::MAX_DATE) return CMD_ERROR; |
| 365 | |
| 366 | /* Don't let a timetable start more than 15 years into the future... */ |
| 367 | if (start_date - TimerGameEconomy::date > TimerGameEconomy::DateAtStartOfYear(MAX_TIMETABLE_START_YEARS)) return CMD_ERROR; |
| 368 | /* ...or 1 year in the past. */ |
| 369 | if (TimerGameEconomy::date - start_date > EconomyTime::DAYS_IN_LEAP_YEAR) return CMD_ERROR; |
| 370 | |
| 371 | /* If trying to distribute start dates over a shared order group, we need to know the total duration. */ |
| 372 | if (timetable_all && !v->orders->IsCompleteTimetable()) return CommandCost(STR_ERROR_TIMETABLE_INCOMPLETE); |
| 373 | |
| 374 | /* Don't allow invalid start dates for other vehicles in the shared order group. */ |
| 375 | if (timetable_all && start_date + (total_duration / Ticks::DAY_TICKS) > EconomyTime::MAX_DATE) return CMD_ERROR; |
| 376 | |
| 377 | if (flags.Test(DoCommandFlag::Execute)) { |
| 378 | std::vector<Vehicle *> vehs; |
| 379 | |
| 380 | if (timetable_all) { |
| 381 | for (Vehicle *w = v->orders->GetFirstSharedVehicle(); w != nullptr; w = w->NextShared()) { |
| 382 | vehs.push_back(w); |
| 383 | } |
| 384 | } else { |
| 385 | vehs.push_back(v); |
| 386 | } |
| 387 | |
| 388 | int num_vehs = (uint)vehs.size(); |
| 389 | |
| 390 | if (num_vehs >= 2) { |
| 391 | std::sort(vehs.begin(), vehs.end(), &VehicleTimetableSorter); |
| 392 | } |
| 393 | |
| 394 | int idx = 0; |
| 395 | |
| 396 | for (Vehicle *w : vehs) { |
| 397 | w->lateness_counter = 0; |
| 398 | w->vehicle_flags.Reset(VehicleFlag::TimetableStarted); |
| 399 | /* Do multiplication, then division to reduce rounding errors. */ |
| 400 | w->timetable_start = start_tick + (idx * total_duration / num_vehs); |
| 401 | |
| 402 | /* Unbunching data is no longer valid. */ |
| 403 | v->ResetDepotUnbunching(); |
| 404 | |
| 405 | SetWindowDirty(WC_VEHICLE_TIMETABLE, w->index); |
| 406 | ++idx; |
| 407 | } |
| 408 |
nothing calls this directly
no test coverage detected