OpenTTD Source 20250612-master-gb012d9e3dc
vehicle_cmd.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
6 */
7
10#include "stdafx.h"
11#include "roadveh.h"
12#include "news_func.h"
13#include "airport.h"
14#include "command_func.h"
15#include "company_func.h"
16#include "train.h"
17#include "aircraft.h"
18#include "newgrf_text.h"
19#include "vehicle_func.h"
20#include "string_func.h"
21#include "depot_map.h"
22#include "vehiclelist.h"
23#include "engine_func.h"
25#include "autoreplace_gui.h"
26#include "group.h"
27#include "order_backup.h"
28#include "ship.h"
29#include "newgrf.h"
30#include "company_base.h"
31#include "core/random_func.hpp"
32#include "vehicle_cmd.h"
33#include "aircraft_cmd.h"
34#include "autoreplace_cmd.h"
35#include "group_cmd.h"
36#include "order_cmd.h"
37#include "roadveh_cmd.h"
38#include "train_cmd.h"
39#include "ship_cmd.h"
40#include <charconv>
41
42#include "table/strings.h"
43
44#include "safeguards.h"
45
46/* Tables used in vehicle_func.h to find the right error message for a certain vehicle type */
47const StringID _veh_build_msg_table[] = {
48 STR_ERROR_CAN_T_BUY_TRAIN,
49 STR_ERROR_CAN_T_BUY_ROAD_VEHICLE,
50 STR_ERROR_CAN_T_BUY_SHIP,
51 STR_ERROR_CAN_T_BUY_AIRCRAFT,
52};
53
54const StringID _veh_sell_msg_table[] = {
55 STR_ERROR_CAN_T_SELL_TRAIN,
56 STR_ERROR_CAN_T_SELL_ROAD_VEHICLE,
57 STR_ERROR_CAN_T_SELL_SHIP,
58 STR_ERROR_CAN_T_SELL_AIRCRAFT,
59};
60
61const StringID _veh_sell_all_msg_table[] = {
62 STR_ERROR_CAN_T_SELL_ALL_TRAIN,
63 STR_ERROR_CAN_T_SELL_ALL_ROAD_VEHICLE,
64 STR_ERROR_CAN_T_SELL_ALL_SHIP,
65 STR_ERROR_CAN_T_SELL_ALL_AIRCRAFT,
66};
67
68const StringID _veh_autoreplace_msg_table[] = {
69 STR_ERROR_CAN_T_AUTOREPLACE_TRAIN,
70 STR_ERROR_CAN_T_AUTOREPLACE_ROAD_VEHICLE,
71 STR_ERROR_CAN_T_AUTOREPLACE_SHIP,
72 STR_ERROR_CAN_T_AUTOREPLACE_AIRCRAFT,
73};
74
75const StringID _veh_refit_msg_table[] = {
76 STR_ERROR_CAN_T_REFIT_TRAIN,
77 STR_ERROR_CAN_T_REFIT_ROAD_VEHICLE,
78 STR_ERROR_CAN_T_REFIT_SHIP,
79 STR_ERROR_CAN_T_REFIT_AIRCRAFT,
80};
81
82const StringID _send_to_depot_msg_table[] = {
83 STR_ERROR_CAN_T_SEND_TRAIN_TO_DEPOT,
84 STR_ERROR_CAN_T_SEND_ROAD_VEHICLE_TO_DEPOT,
85 STR_ERROR_CAN_T_SEND_SHIP_TO_DEPOT,
86 STR_ERROR_CAN_T_SEND_AIRCRAFT_TO_HANGAR,
87};
88
89
100std::tuple<CommandCost, VehicleID, uint, uint16_t, CargoArray> CmdBuildVehicle(DoCommandFlags flags, TileIndex tile, EngineID eid, bool use_free_vehicles, CargoType cargo, ClientID client_id)
101{
102 /* Elementary check for valid location. */
103 if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return { CMD_ERROR, VehicleID::Invalid(), 0, 0, {} };
104
105 VehicleType type = GetDepotVehicleType(tile);
106
107 /* Validate the engine type. */
108 if (!IsEngineBuildable(eid, type, _current_company)) return { CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + type), VehicleID::Invalid(), 0, 0, {} };
109
110 /* Validate the cargo type. */
111 if (cargo >= NUM_CARGO && IsValidCargoType(cargo)) return { CMD_ERROR, VehicleID::Invalid(), 0, 0, {} };
112
113 const Engine *e = Engine::Get(eid);
115
116 /* Engines without valid cargo should not be available */
117 CargoType default_cargo = e->GetDefaultCargoType();
118 if (!IsValidCargoType(default_cargo)) return { CMD_ERROR, VehicleID::Invalid(), 0, 0, {} };
119
120 bool refitting = IsValidCargoType(cargo) && cargo != default_cargo;
121
122 /* Check whether the number of vehicles we need to build can be built according to pool space. */
123 uint num_vehicles;
124 switch (type) {
125 case VEH_TRAIN: num_vehicles = (e->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + CountArticulatedParts(eid, false); break;
126 case VEH_ROAD: num_vehicles = 1 + CountArticulatedParts(eid, false); break;
127 case VEH_SHIP: num_vehicles = 1; break;
128 case VEH_AIRCRAFT: num_vehicles = e->u.air.subtype & AIR_CTOL ? 2 : 3; break;
129 default: NOT_REACHED(); // Safe due to IsDepotTile()
130 }
131 if (!Vehicle::CanAllocateItem(num_vehicles)) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), VehicleID::Invalid(), 0, 0, {} };
132
133 /* Check whether we can allocate a unit number. Autoreplace does not allocate
134 * an unit number as it will (always) reuse the one of the replaced vehicle
135 * and (train) wagons don't have an unit number in any scenario. */
136 UnitID unit_num = (flags.Test(DoCommandFlag::QueryCost) || flags.Test(DoCommandFlag::AutoReplace) || (type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON)) ? 0 : GetFreeUnitNumber(type);
137 if (unit_num == UINT16_MAX) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), VehicleID::Invalid(), 0, 0, {} };
138
139 /* If we are refitting we need to temporarily purchase the vehicle to be able to
140 * test it. */
141 DoCommandFlags subflags = flags;
143
144 /* Vehicle construction needs random bits, so we have to save the random
145 * seeds to prevent desyncs. */
146 SavedRandomSeeds saved_seeds;
147 SaveRandomSeeds(&saved_seeds);
148
149 Vehicle *v = nullptr;
150 switch (type) {
151 case VEH_TRAIN: value.AddCost(CmdBuildRailVehicle(subflags, tile, e, &v)); break;
152 case VEH_ROAD: value.AddCost(CmdBuildRoadVehicle(subflags, tile, e, &v)); break;
153 case VEH_SHIP: value.AddCost(CmdBuildShip (subflags, tile, e, &v)); break;
154 case VEH_AIRCRAFT: value.AddCost(CmdBuildAircraft (subflags, tile, e, &v)); break;
155 default: NOT_REACHED(); // Safe due to IsDepotTile()
156 }
157
158 VehicleID veh_id = VehicleID::Invalid();
159 uint refitted_capacity = 0;
160 uint16_t refitted_mail_capacity = 0;
161 CargoArray cargo_capacities{};
162 if (value.Succeeded()) {
163 if (subflags.Test(DoCommandFlag::Execute)) {
164 v->unitnumber = unit_num;
165 v->value = value.GetCost();
166 veh_id = v->index;
167 }
168
169 if (refitting) {
170 /* Refit only one vehicle. If we purchased an engine, it may have gained free wagons. */
171 CommandCost cc;
172 std::tie(cc, refitted_capacity, refitted_mail_capacity, cargo_capacities) = CmdRefitVehicle(flags, v->index, cargo, 0, false, false, 1);
173 value.AddCost(std::move(cc));
174 } else {
175 /* Fill in non-refitted capacities */
176 if (e->type == VEH_TRAIN || e->type == VEH_ROAD) {
177 cargo_capacities = GetCapacityOfArticulatedParts(eid);
178 refitted_capacity = cargo_capacities[default_cargo];
179 refitted_mail_capacity = 0;
180 } else {
181 refitted_capacity = e->GetDisplayDefaultCapacity(&refitted_mail_capacity);
182 cargo_capacities[default_cargo] = refitted_capacity;
183 CargoType mail = GetCargoTypeByLabel(CT_MAIL);
184 if (IsValidCargoType(mail)) cargo_capacities[mail] = refitted_mail_capacity;
185 }
186 }
187
188 if (flags.Test(DoCommandFlag::Execute)) {
189 if (type == VEH_TRAIN && use_free_vehicles && !flags.Test(DoCommandFlag::AutoReplace) && Train::From(v)->IsEngine()) {
190 /* Move any free wagons to the new vehicle. */
192 }
193
197 if (IsLocalCompany()) {
198 InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the auto replace window (must be called before incrementing num_engines)
199 }
200 }
201
202 if (subflags.Test(DoCommandFlag::Execute)) {
205
206 if (v->IsPrimaryVehicle()) {
208 if (!subflags.Test(DoCommandFlag::AutoReplace)) OrderBackup::Restore(v, client_id);
209 }
210
211 Company::Get(v->owner)->freeunits[v->type].UseID(v->unitnumber);
212 }
213
214
215 /* If we are not in DoCommandFlag::Execute undo everything */
216 if (flags != subflags) {
218 }
219 }
220
221 /* Only restore if we actually did some refitting */
222 if (flags != subflags) RestoreRandomSeeds(saved_seeds);
223
224 return { value, veh_id, refitted_capacity, refitted_mail_capacity, cargo_capacities };
225}
226
236CommandCost CmdSellVehicle(DoCommandFlags flags, VehicleID v_id, bool sell_chain, bool backup_order, ClientID client_id)
237{
238 Vehicle *v = Vehicle::GetIfValid(v_id);
239 if (v == nullptr) return CMD_ERROR;
240
241 Vehicle *front = v->First();
242
243 CommandCost ret = CheckOwnership(front->owner);
244 if (ret.Failed()) return ret;
245
246 if (front->vehstatus.Test(VehState::Crashed)) return CommandCost(STR_ERROR_VEHICLE_IS_DESTROYED);
247
248 if (!front->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type);
249
250 if (v->type == VEH_TRAIN) {
251 ret = CmdSellRailWagon(flags, v, sell_chain, backup_order, client_id);
252 } else {
254
255 if (flags.Test(DoCommandFlag::Execute)) {
256 if (front->IsPrimaryVehicle() && backup_order) OrderBackup::Backup(front, client_id);
257 delete front;
258 }
259 }
260
261 return ret;
262}
263
273static int GetRefitCostFactor(const Vehicle *v, EngineID engine_type, CargoType new_cargo_type, uint8_t new_subtype, bool *auto_refit_allowed)
274{
275 /* Prepare callback param with info about the new cargo type. */
276 const Engine *e = Engine::Get(engine_type);
277
278 /* Is this vehicle a NewGRF vehicle? */
279 if (e->GetGRF() != nullptr) {
280 const CargoSpec *cs = CargoSpec::Get(new_cargo_type);
281 uint32_t param1 = (cs->classes.base() << 16) | (new_subtype << 8) | e->GetGRF()->cargo_map[new_cargo_type];
282
283 uint16_t cb_res = GetVehicleCallback(CBID_VEHICLE_REFIT_COST, param1, 0, engine_type, v);
284 if (cb_res != CALLBACK_FAILED) {
285 *auto_refit_allowed = HasBit(cb_res, 14);
286 int factor = GB(cb_res, 0, 14);
287 if (factor >= 0x2000) factor -= 0x4000; // Treat as signed integer.
288 return factor;
289 }
290 }
291
292 *auto_refit_allowed = e->info.refit_cost == 0;
293 return (v == nullptr || v->cargo_type != new_cargo_type) ? e->info.refit_cost : 0;
294}
295
305static CommandCost GetRefitCost(const Vehicle *v, EngineID engine_type, CargoType new_cargo_type, uint8_t new_subtype, bool *auto_refit_allowed)
306{
307 ExpensesType expense_type;
308 const Engine *e = Engine::Get(engine_type);
309 Price base_price;
310 int cost_factor = GetRefitCostFactor(v, engine_type, new_cargo_type, new_subtype, auto_refit_allowed);
311 switch (e->type) {
312 case VEH_SHIP:
313 base_price = PR_BUILD_VEHICLE_SHIP;
314 expense_type = EXPENSES_SHIP_RUN;
315 break;
316
317 case VEH_ROAD:
318 base_price = PR_BUILD_VEHICLE_ROAD;
319 expense_type = EXPENSES_ROADVEH_RUN;
320 break;
321
322 case VEH_AIRCRAFT:
323 base_price = PR_BUILD_VEHICLE_AIRCRAFT;
324 expense_type = EXPENSES_AIRCRAFT_RUN;
325 break;
326
327 case VEH_TRAIN:
328 base_price = (e->u.rail.railveh_type == RAILVEH_WAGON) ? PR_BUILD_VEHICLE_WAGON : PR_BUILD_VEHICLE_TRAIN;
329 cost_factor <<= 1;
330 expense_type = EXPENSES_TRAIN_RUN;
331 break;
332
333 default: NOT_REACHED();
334 }
335 if (cost_factor < 0) {
336 return CommandCost(expense_type, -GetPrice(base_price, -cost_factor, e->GetGRF(), -10));
337 } else {
338 return CommandCost(expense_type, GetPrice(base_price, cost_factor, e->GetGRF(), -10));
339 }
340}
341
349
362static std::tuple<CommandCost, uint, uint16_t, CargoArray> RefitVehicle(Vehicle *v, bool only_this, uint8_t num_vehicles, CargoType new_cargo_type, uint8_t new_subtype, DoCommandFlags flags, bool auto_refit)
363{
364 CommandCost cost(v->GetExpenseType(false));
365 uint total_capacity = 0;
366 uint total_mail_capacity = 0;
367 num_vehicles = num_vehicles == 0 ? UINT8_MAX : num_vehicles;
368 CargoArray cargo_capacities{};
369
370 VehicleSet vehicles_to_refit;
371 if (!only_this) {
372 GetVehicleSet(vehicles_to_refit, v, num_vehicles);
373 /* In this case, we need to check the whole chain. */
374 v = v->First();
375 }
376
377 std::vector<RefitResult> refit_result;
378
380 uint8_t actual_subtype = new_subtype;
381 for (; v != nullptr; v = (only_this ? nullptr : v->Next())) {
382 /* Reset actual_subtype for every new vehicle */
383 if (!v->IsArticulatedPart()) actual_subtype = new_subtype;
384
385 if (v->type == VEH_TRAIN && std::ranges::find(vehicles_to_refit, v->index) == vehicles_to_refit.end() && !only_this) continue;
386
387 const Engine *e = v->GetEngine();
388 if (!e->CanCarryCargo()) continue;
389
390 /* If the vehicle is not refittable, or does not allow automatic refitting,
391 * count its capacity nevertheless if the cargo matches */
392 bool refittable = HasBit(e->info.refit_mask, new_cargo_type) && (!auto_refit || e->info.misc_flags.Test(EngineMiscFlag::AutoRefit));
393 if (!refittable && v->cargo_type != new_cargo_type) {
394 uint amount = e->DetermineCapacity(v, nullptr);
395 if (amount > 0) cargo_capacities[v->cargo_type] += amount;
396 continue;
397 }
398
399 /* Determine best fitting subtype if requested */
400 if (actual_subtype == 0xFF) {
401 actual_subtype = GetBestFittingSubType(v, v, new_cargo_type);
402 }
403
404 /* Back up the vehicle's cargo type */
405 CargoType temp_cargo_type = v->cargo_type;
406 uint8_t temp_subtype = v->cargo_subtype;
407 if (refittable) {
408 v->cargo_type = new_cargo_type;
409 v->cargo_subtype = actual_subtype;
410 }
411
412 uint16_t mail_capacity = 0;
413 uint amount = e->DetermineCapacity(v, &mail_capacity);
414 total_capacity += amount;
415 /* mail_capacity will always be zero if the vehicle is not an aircraft. */
416 total_mail_capacity += mail_capacity;
417
418 cargo_capacities[new_cargo_type] += amount;
419 CargoType mail = GetCargoTypeByLabel(CT_MAIL);
420 if (IsValidCargoType(mail)) cargo_capacities[mail] += mail_capacity;
421
422 if (!refittable) continue;
423
424 /* Restore the original cargo type */
425 v->cargo_type = temp_cargo_type;
426 v->cargo_subtype = temp_subtype;
427
428 bool auto_refit_allowed;
429 CommandCost refit_cost = GetRefitCost(v, v->engine_type, new_cargo_type, actual_subtype, &auto_refit_allowed);
430 if (auto_refit && !flags.Test(DoCommandFlag::QueryCost) && !auto_refit_allowed) {
431 /* Sorry, auto-refitting not allowed, subtract the cargo amount again from the total.
432 * When querrying cost/capacity (for example in order refit GUI), we always assume 'allowed'.
433 * It is not predictable. */
434 total_capacity -= amount;
435 total_mail_capacity -= mail_capacity;
436
437 if (v->cargo_type == new_cargo_type) {
438 /* Add the old capacity nevertheless, if the cargo matches */
439 total_capacity += v->cargo_cap;
440 if (v->type == VEH_AIRCRAFT) total_mail_capacity += v->Next()->cargo_cap;
441 }
442 continue;
443 }
444 cost.AddCost(std::move(refit_cost));
445
446 /* Record the refitting.
447 * Do not execute the refitting immediately, so DetermineCapacity and GetRefitCost do the same in test and exec run.
448 * (weird NewGRFs)
449 * Note:
450 * - If the capacity of vehicles depends on other vehicles in the chain, the actual capacity is
451 * set after RefitVehicle() via ConsistChanged() and friends. The estimation via _returned_refit_capacity will be wrong.
452 * - We have to call the refit cost callback with the pre-refit configuration of the chain because we want refit and
453 * autorefit to behave the same, and we need its result for auto_refit_allowed.
454 */
455 refit_result.emplace_back(v, amount, mail_capacity, actual_subtype);
456 }
457
458 if (flags.Test(DoCommandFlag::Execute)) {
459 /* Store the result */
460 for (RefitResult &result : refit_result) {
461 Vehicle *u = result.v;
462 u->refit_cap = (u->cargo_type == new_cargo_type) ? std::min<uint16_t>(result.capacity, u->refit_cap) : 0;
463 if (u->cargo.TotalCount() > u->refit_cap) u->cargo.Truncate(u->cargo.TotalCount() - u->refit_cap);
464 u->cargo_type = new_cargo_type;
465 u->cargo_cap = result.capacity;
466 u->cargo_subtype = result.subtype;
467 if (u->type == VEH_AIRCRAFT) {
468 Vehicle *w = u->Next();
469 assert(w != nullptr);
470 w->refit_cap = std::min<uint16_t>(w->refit_cap, result.mail_capacity);
471 w->cargo_cap = result.mail_capacity;
472 if (w->cargo.TotalCount() > w->refit_cap) w->cargo.Truncate(w->cargo.TotalCount() - w->refit_cap);
473 }
474 }
475 }
476
477 refit_result.clear();
478 return { cost, total_capacity, total_mail_capacity, cargo_capacities };
479}
480
493std::tuple<CommandCost, uint, uint16_t, CargoArray> CmdRefitVehicle(DoCommandFlags flags, VehicleID veh_id, CargoType new_cargo_type, uint8_t new_subtype, bool auto_refit, bool only_this, uint8_t num_vehicles)
494{
495 Vehicle *v = Vehicle::GetIfValid(veh_id);
496 if (v == nullptr) return { CMD_ERROR, 0, 0, {} };
497
498 /* Don't allow disasters and sparks and such to be refitted.
499 * We cannot check for IsPrimaryVehicle as autoreplace also refits in free wagon chains. */
500 if (!IsCompanyBuildableVehicleType(v->type)) return { CMD_ERROR, 0, 0, {} };
501
502 Vehicle *front = v->First();
503
504 CommandCost ret = CheckOwnership(front->owner);
505 if (ret.Failed()) return { ret, 0, 0, {} };
506
507 bool free_wagon = v->type == VEH_TRAIN && Train::From(front)->IsFreeWagon(); // used by autoreplace/renew
508
509 /* Don't allow shadows and such to be refitted. */
510 if (v != front && (v->type == VEH_SHIP || v->type == VEH_AIRCRAFT)) return { CMD_ERROR, 0, 0, {} };
511
512 /* Allow auto-refitting only during loading and normal refitting only in a depot. */
513 if (!flags.Test(DoCommandFlag::QueryCost) && // used by the refit GUI, including the order refit GUI.
514 !free_wagon && // used by autoreplace/renew
515 (!auto_refit || !front->current_order.IsType(OT_LOADING)) && // refit inside stations
516 !front->IsStoppedInDepot()) { // refit inside depots
517 return { CommandCost(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type), 0, 0, {} };
518 }
519
520 if (front->vehstatus.Test(VehState::Crashed)) return { CommandCost(STR_ERROR_VEHICLE_IS_DESTROYED), 0, 0, {} };
521
522 /* Check cargo */
523 if (new_cargo_type >= NUM_CARGO) return { CMD_ERROR, 0, 0, {} };
524
525 /* For ships and aircraft there is always only one. */
526 only_this |= front->type == VEH_SHIP || front->type == VEH_AIRCRAFT;
527
528 auto [cost, refit_capacity, mail_capacity, cargo_capacities] = RefitVehicle(v, only_this, num_vehicles, new_cargo_type, new_subtype, flags, auto_refit);
529
530 if (flags.Test(DoCommandFlag::Execute)) {
531 /* Update the cached variables */
532 switch (v->type) {
533 case VEH_TRAIN:
534 Train::From(front)->ConsistChanged(auto_refit ? CCF_AUTOREFIT : CCF_REFIT);
535 break;
536 case VEH_ROAD:
537 RoadVehUpdateCache(RoadVehicle::From(front), auto_refit);
538 if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) RoadVehicle::From(front)->CargoChanged();
539 break;
540
541 case VEH_SHIP:
544 break;
545
546 case VEH_AIRCRAFT:
549 break;
550
551 default: NOT_REACHED();
552 }
553 front->MarkDirty();
554
555 if (!free_wagon) {
558 }
560 } else {
561 /* Always invalidate the cache; querycost might have filled it. */
563 }
564
565 return { cost, refit_capacity, mail_capacity, cargo_capacities };
566}
567
575CommandCost CmdStartStopVehicle(DoCommandFlags flags, VehicleID veh_id, bool evaluate_startstop_cb)
576{
577 /* Disable the effect of evaluate_startstop_cb, when DoCommandFlag::AutoReplace is not set */
578 if (!flags.Test(DoCommandFlag::AutoReplace)) evaluate_startstop_cb = true;
579
580 Vehicle *v = Vehicle::GetIfValid(veh_id);
581 if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
582
584 if (ret.Failed()) return ret;
585
586 if (v->vehstatus.Test(VehState::Crashed)) return CommandCost(STR_ERROR_VEHICLE_IS_DESTROYED);
587
588 switch (v->type) {
589 case VEH_TRAIN:
590 if (v->vehstatus.Test(VehState::Stopped) && Train::From(v)->gcache.cached_power == 0) return CommandCost(STR_ERROR_TRAIN_START_NO_POWER);
591 break;
592
593 case VEH_SHIP:
594 case VEH_ROAD:
595 break;
596
597 case VEH_AIRCRAFT: {
598 Aircraft *a = Aircraft::From(v);
599 /* cannot stop airplane when in flight, or when taking off / landing */
600 if (a->state >= STARTTAKEOFF && a->state < TERM7) return CommandCost(STR_ERROR_AIRCRAFT_IS_IN_FLIGHT);
601 if (HasBit(a->flags, VAF_HELI_DIRECT_DESCENT)) return CommandCost(STR_ERROR_AIRCRAFT_IS_IN_FLIGHT);
602 break;
603 }
604
605 default: return CMD_ERROR;
606 }
607
608 if (evaluate_startstop_cb) {
609 /* Check if this vehicle can be started/stopped. Failure means 'allow'. */
610 std::array<int32_t, 1> regs100;
611 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v, regs100);
612 StringID error = STR_NULL;
613 if (callback != CALLBACK_FAILED) {
614 if (v->GetGRF()->grf_version < 8) {
615 /* 8 bit result 0xFF means 'allow' */
616 if (callback < 0x400 && GB(callback, 0, 8) != 0xFF) error = GetGRFStringID(v->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
617 } else {
618 if (callback < 0x400) {
619 error = GetGRFStringID(v->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
620 } else {
621 switch (callback) {
622 case 0x400: // allow
623 break;
624
625 case 0x40F:
626 error = GetGRFStringID(v->GetGRFID(), static_cast<GRFStringID>(regs100[0]));
627 break;
628
629 default: // unknown reason -> disallow
630 error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
631 break;
632 }
633 }
634 }
635 }
636 if (error != STR_NULL) return CommandCost(error);
637 }
638
639 if (flags.Test(DoCommandFlag::Execute)) {
641
643 if (v->type != VEH_TRAIN) v->cur_speed = 0; // trains can stop 'slowly'
644
645 /* Unbunching data is no longer valid. */
647
648 v->MarkDirty();
653 }
654 return CommandCost();
655}
656
666CommandCost CmdMassStartStopVehicle(DoCommandFlags flags, TileIndex tile, bool do_start, bool vehicle_list_window, const VehicleListIdentifier &vli)
667{
668 VehicleList list;
669
670 if (!vli.Valid()) return CMD_ERROR;
672
673 if (vehicle_list_window) {
674 if (!GenerateVehicleSortList(&list, vli)) return CMD_ERROR;
675 } else {
676 if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
677 /* Get the list of vehicles in the depot */
678 BuildDepotVehicleList(vli.vtype, tile, &list, nullptr);
679 }
680
681 for (const Vehicle *v : list) {
682 if (v->vehstatus.Test(VehState::Stopped) != do_start) continue;
683
684 if (!vehicle_list_window && !v->IsChainInDepot()) continue;
685
686 /* Just try and don't care if some vehicle's can't be stopped. */
687 Command<CMD_START_STOP_VEHICLE>::Do(flags, v->index, false);
688 }
689
690 return CommandCost();
691}
692
701{
702 VehicleList list;
703
705
706 if (!IsCompanyBuildableVehicleType(vehicle_type)) return CMD_ERROR;
707 if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
708
709 /* Get the list of vehicles in the depot */
710 BuildDepotVehicleList(vehicle_type, tile, &list, &list);
711
712 CommandCost last_error = CMD_ERROR;
713 bool had_success = false;
714 for (const Vehicle *v : list) {
715 CommandCost ret = Command<CMD_SELL_VEHICLE>::Do(flags, v->index, true, false, INVALID_CLIENT_ID);
716 if (ret.Succeeded()) {
717 cost.AddCost(ret.GetCost());
718 had_success = true;
719 } else {
720 last_error = std::move(ret);
721 }
722 }
723
724 return had_success ? cost : last_error;
725}
726
735{
736 VehicleList list;
738
739 if (!IsCompanyBuildableVehicleType(vehicle_type)) return CMD_ERROR;
740 if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
741
742 /* Get the list of vehicles in the depot */
743 BuildDepotVehicleList(vehicle_type, tile, &list, &list, true);
744
745 for (const Vehicle *v : list) {
746 /* Ensure that the vehicle completely in the depot */
747 if (!v->IsChainInDepot()) continue;
748
750
751 if (ret.Succeeded()) cost.AddCost(ret.GetCost());
752 }
753 return cost;
754}
755
761bool IsUniqueVehicleName(const std::string &name)
762{
763 for (const Vehicle *v : Vehicle::Iterate()) {
764 if (!v->name.empty() && v->name == name) return false;
765 }
766
767 return true;
768}
769
775static void CloneVehicleName(const Vehicle *src, Vehicle *dst)
776{
777 std::string buf;
778
779 /* Find the position of the first digit in the last group of digits. */
780 size_t number_position;
781 for (number_position = src->name.length(); number_position > 0; number_position--) {
782 /* The design of UTF-8 lets this work simply without having to check
783 * for UTF-8 sequences. */
784 if (src->name[number_position - 1] < '0' || src->name[number_position - 1] > '9') break;
785 }
786
787 /* Format buffer and determine starting number. */
788 long num;
789 uint8_t padding = 0;
790 if (number_position == src->name.length()) {
791 /* No digit at the end, so start at number 2. */
792 buf = src->name;
793 buf += " ";
794 number_position = buf.length();
795 num = 2;
796 } else {
797 /* Found digits, parse them and start at the next number. */
798 buf = src->name.substr(0, number_position);
799
800 auto num_str = std::string_view(src->name).substr(number_position);
801 padding = (uint8_t)num_str.length();
802
803 [[maybe_unused]] auto err = std::from_chars(num_str.data(), num_str.data() + num_str.size(), num, 10).ec;
804 assert(err == std::errc());
805 num++;
806 }
807
808 /* Check if this name is already taken. */
809 for (int max_iterations = 1000; max_iterations > 0; max_iterations--, num++) {
810 std::string new_name = fmt::format("{}{:0{}}", buf, num, padding);
811
812 /* Check the name is unique. */
813 if (IsUniqueVehicleName(new_name)) {
814 dst->name = std::move(new_name);
815 break;
816 }
817 }
818
819 /* All done. If we didn't find a name, it'll just use its default. */
820}
821
830std::tuple<CommandCost, VehicleID> CmdCloneVehicle(DoCommandFlags flags, TileIndex tile, VehicleID veh_id, bool share_orders)
831{
833
834 Vehicle *v = Vehicle::GetIfValid(veh_id);
835 if (v == nullptr || !v->IsPrimaryVehicle()) return { CMD_ERROR, VehicleID::Invalid() };
836 Vehicle *v_front = v;
837 Vehicle *w = nullptr;
838 Vehicle *w_front = nullptr;
839 Vehicle *w_rear = nullptr;
840
841 /*
842 * v_front is the front engine in the original vehicle
843 * v is the car/vehicle of the original vehicle that is currently being copied
844 * w_front is the front engine of the cloned vehicle
845 * w is the car/vehicle currently being cloned
846 * w_rear is the rear end of the cloned train. It's used to add more cars and is only used by trains
847 */
848
850 if (ret.Failed()) return { ret, VehicleID::Invalid() };
851
852 if (v->type == VEH_TRAIN && (!v->IsFrontEngine() || Train::From(v)->crash_anim_pos >= 4400)) return { CMD_ERROR, VehicleID::Invalid() };
853
854 /* check that we can allocate enough vehicles */
855 if (!flags.Test(DoCommandFlag::Execute)) {
856 int veh_counter = 0;
857 do {
858 veh_counter++;
859 } while ((v = v->Next()) != nullptr);
860
861 if (!Vehicle::CanAllocateItem(veh_counter)) {
862 return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), VehicleID::Invalid() };
863 }
864 }
865
866 v = v_front;
867
868 VehicleID new_veh_id = VehicleID::Invalid();
869 do {
870 if (v->type == VEH_TRAIN && Train::From(v)->IsRearDualheaded()) {
871 /* we build the rear ends of multiheaded trains with the front ones */
872 continue;
873 }
874
875 /* In case we're building a multi headed vehicle and the maximum number of
876 * vehicles is almost reached (e.g. max trains - 1) not all vehicles would
877 * be cloned. When the non-primary engines were build they were seen as
878 * 'new' vehicles whereas they would immediately be joined with a primary
879 * engine. This caused the vehicle to be not build as 'the limit' had been
880 * reached, resulting in partially build vehicles and such. */
881 DoCommandFlags build_flags = flags;
883
884 CommandCost cost;
885 std::tie(cost, new_veh_id, std::ignore, std::ignore, std::ignore) = Command<CMD_BUILD_VEHICLE>::Do(build_flags, tile, v->engine_type, false, INVALID_CARGO, INVALID_CLIENT_ID);
886
887 if (cost.Failed()) {
888 /* Can't build a part, then sell the stuff we already made; clear up the mess */
889 if (w_front != nullptr) Command<CMD_SELL_VEHICLE>::Do(flags, w_front->index, true, false, INVALID_CLIENT_ID);
890 return { cost, VehicleID::Invalid() };
891 }
892
893 total_cost.AddCost(cost.GetCost());
894
895 if (flags.Test(DoCommandFlag::Execute)) {
896 w = Vehicle::Get(new_veh_id);
897
898 if (v->type == VEH_TRAIN && Train::From(v)->flags.Test(VehicleRailFlag::Flipped)) {
899 /* Only copy the reverse state if neither old or new vehicle implements reverse-on-build probability callback. */
900 if (!TestVehicleBuildProbability(v, v->engine_type, BuildProbabilityType::Reversed).has_value() &&
901 !TestVehicleBuildProbability(w, w->engine_type, BuildProbabilityType::Reversed).has_value()) {
903 }
904 }
905
906 if (v->type == VEH_TRAIN && !v->IsFrontEngine()) {
907 /* this s a train car
908 * add this unit to the end of the train */
909 CommandCost result = Command<CMD_MOVE_RAIL_VEHICLE>::Do(flags, w->index, w_rear->index, true);
910 if (result.Failed()) {
911 /* The train can't be joined to make the same consist as the original.
912 * Sell what we already made (clean up) and return an error. */
913 Command<CMD_SELL_VEHICLE>::Do(flags, w_front->index, true, false, INVALID_CLIENT_ID);
915 return { result, VehicleID::Invalid() }; // return error and the message returned from CMD_MOVE_RAIL_VEHICLE
916 }
917 } else {
918 /* this is a front engine or not a train. */
919 w_front = w;
921 w->SetServiceIntervalIsCustom(v->ServiceIntervalIsCustom());
922 w->SetServiceIntervalIsPercent(v->ServiceIntervalIsPercent());
923 }
924 w_rear = w; // trains needs to know the last car in the train, so they can add more in next loop
925 }
926 } while (v->type == VEH_TRAIN && (v = v->GetNextVehicle()) != nullptr);
927
928 if (flags.Test(DoCommandFlag::Execute) && v_front->type == VEH_TRAIN) {
929 /* for trains this needs to be the front engine due to the callback function */
930 new_veh_id = w_front->index;
931 }
932
933 if (flags.Test(DoCommandFlag::Execute)) {
934 /* Cloned vehicles belong to the same group */
935 Command<CMD_ADD_VEHICLE_GROUP>::Do(flags, v_front->group_id, w_front->index, false, VehicleListIdentifier{});
936 }
937
938
939 /* Take care of refitting. */
940 w = w_front;
941 v = v_front;
942
943 /* Both building and refitting are influenced by newgrf callbacks, which
944 * makes it impossible to accurately estimate the cloning costs. In
945 * particular, it is possible for engines of the same type to be built with
946 * different numbers of articulated parts, so when refitting we have to
947 * loop over real vehicles first, and then the articulated parts of those
948 * vehicles in a different loop. */
949 do {
950 do {
951 if (flags.Test(DoCommandFlag::Execute)) {
952 assert(w != nullptr);
953
954 /* Find out what's the best sub type */
955 uint8_t subtype = GetBestFittingSubType(v, w, v->cargo_type);
956 if (w->cargo_type != v->cargo_type || w->cargo_subtype != subtype) {
957 CommandCost cost = std::get<0>(Command<CMD_REFIT_VEHICLE>::Do(flags, w->index, v->cargo_type, subtype, false, true, 0));
958 if (cost.Succeeded()) total_cost.AddCost(cost.GetCost());
959 }
960
961 if (w->IsGroundVehicle() && w->HasArticulatedPart()) {
962 w = w->GetNextArticulatedPart();
963 } else {
964 break;
965 }
966 } else {
967 const Engine *e = v->GetEngine();
968 CargoType initial_cargo = (e->CanCarryCargo() ? e->GetDefaultCargoType() : INVALID_CARGO);
969
970 if (v->cargo_type != initial_cargo && IsValidCargoType(initial_cargo)) {
971 bool dummy;
972 total_cost.AddCost(GetRefitCost(nullptr, v->engine_type, v->cargo_type, v->cargo_subtype, &dummy));
973 }
974 }
975
976 if (v->IsGroundVehicle() && v->HasArticulatedPart()) {
977 v = v->GetNextArticulatedPart();
978 } else {
979 break;
980 }
981 } while (v != nullptr);
982
983 if (flags.Test(DoCommandFlag::Execute) && v->type == VEH_TRAIN) w = w->GetNextVehicle();
984 } while (v->type == VEH_TRAIN && (v = v->GetNextVehicle()) != nullptr);
985
986 if (flags.Test(DoCommandFlag::Execute)) {
987 /*
988 * Set the orders of the vehicle. Cannot do it earlier as we need
989 * the vehicle refitted before doing this, otherwise the moved
990 * cargo types might not match (passenger vs non-passenger)
991 */
992 CommandCost result = Command<CMD_CLONE_ORDER>::Do(flags, (share_orders ? CO_SHARE : CO_COPY), w_front->index, v_front->index);
993 if (result.Failed()) {
994 /* The vehicle has already been bought, so now it must be sold again. */
995 Command<CMD_SELL_VEHICLE>::Do(flags, w_front->index, true, false, INVALID_CLIENT_ID);
996 return { result, VehicleID::Invalid() };
997 }
998
999 /* Now clone the vehicle's name, if it has one. */
1000 if (!v_front->name.empty()) CloneVehicleName(v_front, w_front);
1001
1002 /* Since we can't estimate the cost of cloning a vehicle accurately we must
1003 * check whether the company has enough money manually. */
1004 if (!CheckCompanyHasMoney(total_cost)) {
1005 /* The vehicle has already been bought, so now it must be sold again. */
1006 Command<CMD_SELL_VEHICLE>::Do(flags, w_front->index, true, false, INVALID_CLIENT_ID);
1007 return { total_cost, VehicleID::Invalid() };
1008 }
1009 }
1010
1011 return { total_cost, new_veh_id };
1012}
1013
1022{
1023 VehicleList list;
1024
1025 if (!GenerateVehicleSortList(&list, vli)) return CMD_ERROR;
1026
1027 /* Send all the vehicles to a depot */
1028 bool had_success = false;
1029 for (uint i = 0; i < list.size(); i++) {
1030 const Vehicle *v = list[i];
1032
1033 if (ret.Succeeded()) {
1034 had_success = true;
1035
1036 /* Return 0 if DoCommandFlag::Execute is not set this is a valid goto depot command)
1037 * In this case we know that at least one vehicle can be sent to a depot
1038 * and we will issue the command. We can now safely quit the loop, knowing
1039 * it will succeed at least once. With DoCommandFlag::Execute we really need to send them to the depot */
1040 if (!flags.Test(DoCommandFlag::Execute)) break;
1041 }
1042 }
1043
1044 return had_success ? CommandCost() : CMD_ERROR;
1045}
1046
1056{
1057 if (depot_cmd.Test(DepotCommandFlag::MassSend)) {
1058 /* Mass goto depot requested */
1059 if (!vli.Valid()) return CMD_ERROR;
1060 return SendAllVehiclesToDepot(flags, depot_cmd.Test(DepotCommandFlag::Service), vli);
1061 }
1062
1063 Vehicle *v = Vehicle::GetIfValid(veh_id);
1064 if (v == nullptr) return CMD_ERROR;
1065 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
1066
1067 return v->SendToDepot(flags, depot_cmd);
1068}
1069
1077CommandCost CmdRenameVehicle(DoCommandFlags flags, VehicleID veh_id, const std::string &text)
1078{
1079 Vehicle *v = Vehicle::GetIfValid(veh_id);
1080 if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
1081
1083 if (ret.Failed()) return ret;
1084
1085 bool reset = text.empty();
1086
1087 if (!reset) {
1089 if (!flags.Test(DoCommandFlag::AutoReplace) && !IsUniqueVehicleName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
1090 }
1091
1092 if (flags.Test(DoCommandFlag::Execute)) {
1093 if (reset) {
1094 v->name.clear();
1095 } else {
1096 v->name = text;
1097 }
1100 }
1101
1102 return CommandCost();
1103}
1104
1105
1115CommandCost CmdChangeServiceInt(DoCommandFlags flags, VehicleID veh_id, uint16_t serv_int, bool is_custom, bool is_percent)
1116{
1117 Vehicle *v = Vehicle::GetIfValid(veh_id);
1118 if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
1119
1121 if (ret.Failed()) return ret;
1122
1123 const Company *company = Company::Get(v->owner);
1124 is_percent = is_custom ? is_percent : company->settings.vehicle.servint_ispercent;
1125
1126 if (is_custom) {
1127 if (serv_int != GetServiceIntervalClamped(serv_int, is_percent)) return CMD_ERROR;
1128 } else {
1129 serv_int = CompanyServiceInterval(company, v->type);
1130 }
1131
1132 if (flags.Test(DoCommandFlag::Execute)) {
1133 v->SetServiceInterval(serv_int);
1134 v->SetServiceIntervalIsCustom(is_custom);
1135 v->SetServiceIntervalIsPercent(is_percent);
1137 }
1138
1139 return CommandCost();
1140}
Base for aircraft.
@ VAF_HELI_DIRECT_DESCENT
The helicopter is descending directly at its destination (helipad or in front of hangar)
Definition aircraft.h:45
void UpdateAircraftCache(Aircraft *v, bool update_range=false)
Update cached values of an aircraft.
CommandCost CmdBuildAircraft(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build an aircraft.
Command definitions related to aircraft.
Various declarations for airports.
@ STARTTAKEOFF
Airplane has arrived at a runway for take-off.
Definition airport.h:74
@ TERM7
Heading for terminal 7.
Definition airport.h:82
CargoArray GetCapacityOfArticulatedParts(EngineID engine)
Get the capacity of the parts of a given engine.
uint CountArticulatedParts(EngineID engine_type, bool purchase_window)
Count the number of articulated parts of an engine.
Functions related to articulated vehicles.
Command definitions related to autoreplace.
void InvalidateAutoreplaceWindow(EngineID e, GroupID id_g)
Rebuild the left autoreplace list if an engine is removed or added.
Functions related to the autoreplace GUIs.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:23
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:106
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:75
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
constexpr Timpl & Flip(Tvalue_type value)
Flip the value-th bit.
constexpr Timpl & Set()
Set all bits.
Common return value for all commands.
bool Succeeded() const
Did this command succeed?
void AddCost(const Money &cost)
Adds the given cost to the cost of the command.
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
Enum-as-bit-set wrapper.
uint Truncate(uint max_move=UINT_MAX)
Truncates the cargo in this list to the given amount.
uint TotalCount() const
Returns sum of cargo, including reserved cargo.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
@ QueryCost
query cost only, don't build.
@ AutoReplace
autoreplace/autorenew is in progress, this shall disable vehicle limits when building,...
Definition of stuff that is very close to a company, like the company struct itself.
int CompanyServiceInterval(const Company *c, VehicleType type)
Get the service interval for the given company and vehicle type.
bool CheckCompanyHasMoney(CommandCost &cost)
Verify whether the company can pay the bill.
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
bool IsLocalCompany()
Is the current company the local company?
Map related accessors for depots.
bool IsDepotTile(Tile tile)
Is the given tile a tile with a depot on it?
Definition depot_map.h:42
VehicleType GetDepotVehicleType(Tile t)
Get the type of vehicles that can use a depot.
Definition depot_map.h:78
bool do_start
flag for starting playback of next_file at next opportunity
Definition dmusic.cpp:122
Money GetPrice(Price index, uint cost_factor, const GRFFile *grf_file, int shift)
Determine a certain price.
Definition economy.cpp:952
ExpensesType
Types of expenses.
@ EXPENSES_ROADVEH_RUN
Running costs road vehicles.
@ EXPENSES_TRAIN_RUN
Running costs trains.
@ EXPENSES_AIRCRAFT_RUN
Running costs aircraft.
@ EXPENSES_SHIP_RUN
Running costs ships.
@ EXPENSES_NEW_VEHICLES
New vehicles.
Price
Enumeration of all base prices for use with Prices.
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
Check if an engine is buildable.
Definition engine.cpp:1246
Functions related to engines.
@ AIR_CTOL
Conventional Take Off and Landing, i.e. planes.
@ AutoRefit
Automatic refitting is allowed.
@ RAILVEH_WAGON
simple wagon, not motorized
Definition engine_type.h:34
@ RAILVEH_MULTIHEAD
indicates a combination of two locomotives
Definition engine_type.h:33
Base class for groups and group functions.
Command definitions related to engine groups.
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1535
virtual void MarkDirty()
Marks the vehicles to be redrawn and updates cached variables.
ClientID
'Unique' identifier to be given to clients
@ INVALID_CLIENT_ID
Client is not part of anything.
Base for the NewGRF implementation.
@ CBID_VEHICLE_REFIT_COST
Called to determine the cost factor for refitting a vehicle.
@ CBID_VEHICLE_START_STOP_CHECK
Called when the company (or AI) tries to start or stop a vehicle.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
std::optional< bool > TestVehicleBuildProbability(Vehicle *v, EngineID engine, BuildProbabilityType type)
Test for vehicle build probablity type.
uint16_t GetVehicleCallback(CallbackID callback, uint32_t param1, uint32_t param2, EngineID engine, const Vehicle *v, std::span< int32_t > regs100)
Evaluate a newgrf callback for vehicles.
StringID GetGRFStringID(uint32_t grfid, GRFStringID stringid)
Returns the index for this stringid associated with its grfID.
Header of Action 04 "universal holder" structure and functions.
static constexpr GRFStringID GRFSTR_MISC_GRF_TEXT
Miscellaneous GRF text range.
Functions related to news.
void DeleteVehicleNews(VehicleID vid, AdviceType advice_type=AdviceType::Invalid)
Delete news with a given advice type about a vehicle.
@ VehicleWaiting
The vehicle is waiting in the depot.
Functions related to order backups.
uint16_t GetServiceIntervalClamped(int interval, bool ispercent)
Clamp the service interval to the correct min/max.
Command definitions related to orders.
Pseudo random number generator.
void SaveRandomSeeds(SavedRandomSeeds *storage)
Saves the current seeds.
void RestoreRandomSeeds(const SavedRandomSeeds &storage)
Restores previously saved seeds.
Road vehicle states.
void RoadVehUpdateCache(RoadVehicle *v, bool same_length=false)
Update the cache of a road vehicle.
CommandCost CmdBuildRoadVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build a road vehicle.
Command definitions related to road vehicles.
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
Base for ships.
CommandCost CmdBuildShip(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build a ship.
Definition ship_cmd.cpp:888
Command definitions related to ships.
Definition of base types and functions in a cross-platform compatible way.
size_t Utf8StringLength(std::string_view str)
Get the length of an UTF-8 encoded string in number of characters and thus not the number of bytes th...
Definition string.cpp:347
Functions related to low-level strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
uint8_t subtype
Type of aircraft.
Aircraft, helicopters, rotors and their shadows belong to this class.
Definition aircraft.h:72
uint8_t state
State of the airport.
Definition aircraft.h:77
uint8_t flags
Aircraft flags.
Definition aircraft.h:81
std::string name
Name of vehicle.
uint16_t service_interval
The interval for (automatic) servicing; either in days or %.
void ResetDepotUnbunching()
Resets all the data used for depot unbunching.
VehicleType type
Type of vehicle.
Class for storing amounts of cargo.
Definition cargo_type.h:113
Specification of a cargo type.
Definition cargotype.h:74
CargoClasses classes
Classes of this cargo type.
Definition cargotype.h:81
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
CompanySettings settings
settings specific for each company
VehicleDefaultSettings vehicle
default settings for vehicles
EngineMiscFlags misc_flags
Miscellaneous flags.
Money GetCost() const
Return how much a new engine costs.
Definition engine.cpp:318
const GRFFile * GetGRF() const
Retrieve the NewGRF the engine is tied to.
uint DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity=nullptr) const
Determines capacity of a given vehicle from scratch.
Definition engine.cpp:201
VehicleType type
Vehicle type, ie VEH_ROAD, VEH_TRAIN, etc.
Definition engine_base.h:61
uint GetDisplayDefaultCapacity(uint16_t *mail_capacity=nullptr) const
Determines the default cargo capacity of an engine for display purposes.
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:96
bool CanCarryCargo() const
Determines whether an engine can carry something.
Definition engine.cpp:168
std::array< uint8_t, NUM_CARGO > cargo_map
Inverse cargo translation table (CargoType -> local ID)
Definition newgrf.h:137
VehicleSettings vehicle
options for vehicles
static void CountVehicle(const Vehicle *v, int delta)
Update num_vehicle when adding or removing a vehicle.
static void CountEngine(const Vehicle *v, int delta)
Update num_engines when adding/removing an engine.
static void UpdateAutoreplace(CompanyID company)
Update autoreplace_defined and autoreplace_finished of all statistics of a company.
static void Restore(Vehicle *v, uint32_t user)
Restore the data of this order to the given vehicle.
static void Backup(const Vehicle *v, uint32_t user)
Create an order backup for the given vehicle.
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:66
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static Titem * GetIfValid(auto index)
Returns Titem with given index.
Helper structure for RefitVehicle()
uint capacity
New capacity of vehicle.
Vehicle * v
Vehicle to refit.
uint mail_capacity
New mail capacity of aircraft.
uint8_t subtype
cargo subtype to refit to
Stores the state of all random number generators.
void UpdateCache()
Update the caches of this ship.
Definition ship_cmd.cpp:232
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
bool servint_ispercent
service intervals are in percents
The information about a vehicle list.
Definition vehiclelist.h:32
VehicleType vtype
The vehicle type associated with this list.
Definition vehiclelist.h:34
uint8_t roadveh_acceleration_model
realistic acceleration for road vehicles
Vehicle data structure.
EngineID engine_type
The type of engine used for this vehicle.
const Engine * GetEngine() const
Retrieves the engine of the vehicle.
Definition vehicle.cpp:718
bool IsStoppedInDepot() const
Check whether the vehicle is in the depot and stopped.
virtual ExpensesType GetExpenseType(bool income) const
Sets the expense type associated to this vehicle type.
Vehicle * GetNextArticulatedPart() const
Get the next part of an articulated engine.
VehicleCargoList cargo
The cargo this vehicle is carrying.
uint16_t cargo_cap
total capacity
CommandCost SendToDepot(DoCommandFlags flags, DepotCommandFlags command)
Send this vehicle to the depot using the given command(s).
Definition vehicle.cpp:2544
bool HasArticulatedPart() const
Check if an engine has an articulated part.
Vehicle * GetNextVehicle() const
Get the next real (non-articulated part) vehicle in the consist.
GroupID group_id
Index of group Pool array.
VehStates vehstatus
Status.
bool IsArticulatedPart() const
Check if the vehicle is an articulated part of an engine.
CargoType cargo_type
type of cargo this vehicle is carrying
debug_inline bool IsFrontEngine() const
Check if the vehicle is a front engine.
Vehicle * First() const
Get the first vehicle of this vehicle chain.
Order current_order
The current order (+ status, like: loading)
Vehicle * Next() const
Get the next vehicle of this vehicle.
const GRFFile * GetGRF() const
Retrieve the NewGRF the vehicle is tied to.
Definition vehicle.cpp:728
Money value
Value of the vehicle.
uint16_t refit_cap
Capacity left over from before last refit.
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the vehicle is tied to.
Definition vehicle.cpp:738
virtual bool IsPrimaryVehicle() const
Whether this is the primary vehicle in the chain.
uint16_t cur_speed
current speed
uint8_t cargo_subtype
Used for livery refits (NewGRF variations)
debug_inline bool IsGroundVehicle() const
Check if the vehicle is a ground vehicle.
TileIndex tile
Current tile index.
void InvalidateNewGRFCacheOfChain()
Invalidates cached NewGRF variables of all vehicles in the chain (after the current vehicle)
Owner owner
Which company owns the vehicle?
UnitID unitnumber
unit number, for display purposes only
bool IsTileOwner(Tile tile, Owner owner)
Checks if a tile belongs to the given owner.
Definition tile_map.h:214
Base for the train class.
void NormalizeTrainVehInDepot(const Train *u)
Move all free vehicles in the depot to the train.
@ Flipped
Reverse the visible direction of the vehicle.
Definition train.h:28
static constexpr ConsistChangeFlags CCF_REFIT
Valid changes for refitting in a depot.
Definition train.h:55
static constexpr ConsistChangeFlags CCF_AUTOREFIT
Valid changes for autorefitting in stations.
Definition train.h:54
CommandCost CmdBuildRailVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build a railroad vehicle.
CommandCost CmdSellRailWagon(DoCommandFlags flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
Sell a (single) train wagon/engine.
Command definitions related to trains.
uint16_t UnitID
Type for the company global vehicle unit number.
void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8_t num_vehicles)
Calculates the set of vehicles that will be affected by a given selection.
Definition vehicle.cpp:3179
UnitID GetFreeUnitNumber(VehicleType type)
Get an unused unit number for a vehicle (if allowed).
Definition vehicle.cpp:1865
@ Crashed
Vehicle is crashed.
@ Stopped
Vehicle is stopped by the player.
CommandCost CmdMassStartStopVehicle(DoCommandFlags flags, TileIndex tile, bool do_start, bool vehicle_list_window, const VehicleListIdentifier &vli)
Starts or stops a lot of vehicles.
static void CloneVehicleName(const Vehicle *src, Vehicle *dst)
Clone the custom name of a vehicle, adding or incrementing a number.
bool IsUniqueVehicleName(const std::string &name)
Test if a name is unique among vehicle names.
CommandCost CmdDepotSellAllVehicles(DoCommandFlags flags, TileIndex tile, VehicleType vehicle_type)
Sells all vehicles in a depot.
std::tuple< CommandCost, VehicleID > CmdCloneVehicle(DoCommandFlags flags, TileIndex tile, VehicleID veh_id, bool share_orders)
Clone a vehicle.
CommandCost CmdChangeServiceInt(DoCommandFlags flags, VehicleID veh_id, uint16_t serv_int, bool is_custom, bool is_percent)
Change the service interval of a vehicle.
static CommandCost GetRefitCost(const Vehicle *v, EngineID engine_type, CargoType new_cargo_type, uint8_t new_subtype, bool *auto_refit_allowed)
Learn the price of refitting a certain engine.
static int GetRefitCostFactor(const Vehicle *v, EngineID engine_type, CargoType new_cargo_type, uint8_t new_subtype, bool *auto_refit_allowed)
Helper to run the refit cost callback.
CommandCost CmdSellVehicle(DoCommandFlags flags, VehicleID v_id, bool sell_chain, bool backup_order, ClientID client_id)
Sell a vehicle.
static std::tuple< CommandCost, uint, uint16_t, CargoArray > RefitVehicle(Vehicle *v, bool only_this, uint8_t num_vehicles, CargoType new_cargo_type, uint8_t new_subtype, DoCommandFlags flags, bool auto_refit)
Refits a vehicle (chain).
CommandCost CmdSendVehicleToDepot(DoCommandFlags flags, VehicleID veh_id, DepotCommandFlags depot_cmd, const VehicleListIdentifier &vli)
Send a vehicle to the depot.
static CommandCost SendAllVehiclesToDepot(DoCommandFlags flags, bool service, const VehicleListIdentifier &vli)
Send all vehicles of type to depots.
std::tuple< CommandCost, uint, uint16_t, CargoArray > CmdRefitVehicle(DoCommandFlags flags, VehicleID veh_id, CargoType new_cargo_type, uint8_t new_subtype, bool auto_refit, bool only_this, uint8_t num_vehicles)
Refits a vehicle to the specified cargo type.
CommandCost CmdRenameVehicle(DoCommandFlags flags, VehicleID veh_id, const std::string &text)
Give a custom name to your vehicle.
CommandCost CmdStartStopVehicle(DoCommandFlags flags, VehicleID veh_id, bool evaluate_startstop_cb)
Start/Stop a vehicle.
CommandCost CmdDepotMassAutoReplace(DoCommandFlags flags, TileIndex tile, VehicleType vehicle_type)
Autoreplace all vehicles in the depot.
std::tuple< CommandCost, VehicleID, uint, uint16_t, CargoArray > CmdBuildVehicle(DoCommandFlags flags, TileIndex tile, EngineID eid, bool use_free_vehicles, CargoType cargo, ClientID client_id)
Build a vehicle.
Command definitions for vehicles.
Functions related to vehicles.
uint8_t GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoType dest_cargo_type)
Get the best fitting subtype when 'cloning'/'replacing' v_from with v_for.
bool IsCompanyBuildableVehicleType(VehicleType type)
Is the given vehicle type buildable by a company?
WindowClass GetWindowClassForVehicleType(VehicleType vt)
Get WindowClass for vehicle list of given vehicle type.
Definition vehicle_gui.h:97
VehicleType
Available vehicle types.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
@ MassSend
Tells that it's a mass send to depot command (type in VLW flag)
@ DontCancel
Don't cancel current goto depot command if any.
@ Service
The vehicle will leave the depot right after arrival (service only)
static const uint MAX_LENGTH_VEHICLE_NAME_CHARS
The maximum length of a vehicle name in characters including '\0'.
@ WID_VV_START_STOP
Start or stop this vehicle, and show information about the current state.
bool GenerateVehicleSortList(VehicleList *list, const VehicleListIdentifier &vli)
Generate a list of vehicles based on window type.
void BuildDepotVehicleList(VehicleType type, TileIndex tile, VehicleList *engines, VehicleList *wagons, bool individual_wagons)
Generate a list of vehicles inside a depot.
Functions and type for generating vehicle lists.
std::vector< const Vehicle * > VehicleList
A list of vehicles.
Definition vehiclelist.h:68
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3173
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3265
void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
Mark a particular widget in a particular window as dirty (in need of repainting)
Definition window.cpp:3160
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3147
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition window.cpp:3282
@ WC_VEHICLE_DEPOT
Depot view; Window numbers:
@ WC_VEHICLE_DETAILS
Vehicle details; Window numbers:
@ WC_COMPANY
Company view; Window numbers:
@ WC_VEHICLE_VIEW
Vehicle view; Window numbers: