00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "roadveh.h"
00008 #include "ship.h"
00009 #include "spritecache.h"
00010 #include "landscape.h"
00011 #include "timetable.h"
00012 #include "viewport_func.h"
00013 #include "gfx_func.h"
00014 #include "news_func.h"
00015 #include "command_func.h"
00016 #include "company_func.h"
00017 #include "vehicle_gui.h"
00018 #include "train.h"
00019 #include "aircraft.h"
00020 #include "newgrf_engine.h"
00021 #include "newgrf_sound.h"
00022 #include "newgrf_station.h"
00023 #include "newgrf_text.h"
00024 #include "group.h"
00025 #include "group_gui.h"
00026 #include "strings_func.h"
00027 #include "zoom_func.h"
00028 #include "functions.h"
00029 #include "date_func.h"
00030 #include "window_func.h"
00031 #include "vehicle_func.h"
00032 #include "autoreplace_func.h"
00033 #include "autoreplace_gui.h"
00034 #include "string_func.h"
00035 #include "oldpool_func.h"
00036 #include "depot_map.h"
00037 #include "ai/ai.hpp"
00038 #include "core/smallmap_type.hpp"
00039 #include "vehiclelist.h"
00040 #include "core/mem_func.hpp"
00041 #include "depot_func.h"
00042 #include "settings_type.h"
00043
00044 #include "table/sprites.h"
00045 #include "table/strings.h"
00046
00047 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00048
00049 VehicleID _vehicle_id_ctr_day;
00050 const Vehicle *_place_clicked_vehicle;
00051 VehicleID _new_vehicle_id;
00052 uint16 _returned_refit_capacity;
00053
00054
00055
00056 const uint32 _veh_build_proc_table[] = {
00057 CMD_BUILD_RAIL_VEHICLE,
00058 CMD_BUILD_ROAD_VEH,
00059 CMD_BUILD_SHIP,
00060 CMD_BUILD_AIRCRAFT,
00061 };
00062 const uint32 _veh_sell_proc_table[] = {
00063 CMD_SELL_RAIL_WAGON,
00064 CMD_SELL_ROAD_VEH,
00065 CMD_SELL_SHIP,
00066 CMD_SELL_AIRCRAFT,
00067 };
00068
00069 const uint32 _veh_refit_proc_table[] = {
00070 CMD_REFIT_RAIL_VEHICLE,
00071 CMD_REFIT_ROAD_VEH,
00072 CMD_REFIT_SHIP,
00073 CMD_REFIT_AIRCRAFT,
00074 };
00075
00076 const uint32 _send_to_depot_proc_table[] = {
00077 CMD_SEND_TRAIN_TO_DEPOT,
00078 CMD_SEND_ROADVEH_TO_DEPOT,
00079 CMD_SEND_SHIP_TO_DEPOT,
00080 CMD_SEND_AIRCRAFT_TO_HANGAR,
00081 };
00082
00083
00084
00085 DEFINE_OLD_POOL_GENERIC(Vehicle, Vehicle)
00086
00087
00091 bool Vehicle::NeedsAutorenewing(const Company *c) const
00092 {
00093
00094
00095
00096
00097 assert(c == GetCompany(this->owner));
00098
00099 if (!c->engine_renew) return false;
00100 if (this->age - this->max_age < (c->engine_renew_months * 30)) return false;
00101 if (this->age == 0) return false;
00102
00103 return true;
00104 }
00105
00106 void VehicleServiceInDepot(Vehicle *v)
00107 {
00108 v->date_of_last_service = _date;
00109 v->breakdowns_since_last_service = 0;
00110 v->reliability = GetEngine(v->engine_type)->reliability;
00111 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00112 }
00113
00114 bool Vehicle::NeedsServicing() const
00115 {
00116 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00117
00118 if (_settings_game.order.no_servicing_if_no_breakdowns && _settings_game.difficulty.vehicle_breakdowns == 0) {
00119
00120
00121 return EngineHasReplacementForCompany(GetCompany(this->owner), this->engine_type, this->group_id);
00122 }
00123
00124 return _settings_game.vehicle.servint_ispercent ?
00125 (this->reliability < GetEngine(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00126 (this->date_of_last_service + this->service_interval < _date);
00127 }
00128
00129 bool Vehicle::NeedsAutomaticServicing() const
00130 {
00131 if (_settings_game.order.gotodepot && VehicleHasDepotOrders(this)) return false;
00132 if (this->current_order.IsType(OT_LOADING)) return false;
00133 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00134 return NeedsServicing();
00135 }
00136
00137 StringID VehicleInTheWayErrMsg(const Vehicle *v)
00138 {
00139 switch (v->type) {
00140 case VEH_TRAIN: return STR_8803_TRAIN_IN_THE_WAY;
00141 case VEH_ROAD: return STR_9000_ROAD_VEHICLE_IN_THE_WAY;
00142 case VEH_AIRCRAFT: return STR_A015_AIRCRAFT_IN_THE_WAY;
00143 default: return STR_980E_SHIP_IN_THE_WAY;
00144 }
00145 }
00146
00147 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00148 {
00149 byte z = *(byte*)data;
00150
00151 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00152 if (v->z_pos > z) return NULL;
00153
00154 _error_message = VehicleInTheWayErrMsg(v);
00155 return v;
00156 }
00157
00158 bool EnsureNoVehicleOnGround(TileIndex tile)
00159 {
00160 byte z = GetTileMaxZ(tile);
00161 return !HasVehicleOnPos(tile, &z, &EnsureNoVehicleProcZ);
00162 }
00163
00165 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00166 {
00167 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00168 if (v == (const Vehicle *)data) return NULL;
00169
00170 _error_message = VehicleInTheWayErrMsg(v);
00171 return v;
00172 }
00173
00181 bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00182 {
00183 return HasVehicleOnPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc) ||
00184 HasVehicleOnPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc);
00185 }
00186
00187
00188 static void UpdateVehiclePosHash(Vehicle *v, int x, int y);
00189
00190 void VehiclePositionChanged(Vehicle *v)
00191 {
00192 int img = v->cur_image;
00193 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
00194 const Sprite *spr = GetSprite(img, ST_NORMAL);
00195
00196 pt.x += spr->x_offs;
00197 pt.y += spr->y_offs;
00198
00199 UpdateVehiclePosHash(v, pt.x, pt.y);
00200
00201 v->left_coord = pt.x;
00202 v->top_coord = pt.y;
00203 v->right_coord = pt.x + spr->width + 2;
00204 v->bottom_coord = pt.y + spr->height + 2;
00205 }
00206
00207 Vehicle::Vehicle()
00208 {
00209 this->type = VEH_INVALID;
00210 this->left_coord = INVALID_COORD;
00211 this->group_id = DEFAULT_GROUP;
00212 this->fill_percent_te_id = INVALID_TE_ID;
00213 this->first = this;
00214 this->colourmap = PAL_NONE;
00215 }
00216
00221 byte VehicleRandomBits()
00222 {
00223 return GB(Random(), 0, 8);
00224 }
00225
00226
00227 bool Vehicle::AllocateList(Vehicle **vl, int num)
00228 {
00229 if (!Vehicle::CanAllocateItem(num)) return false;
00230 if (vl == NULL) return true;
00231
00232 uint counter = _Vehicle_pool.first_free_index;
00233
00234 for (int i = 0; i != num; i++) {
00235 vl[i] = new (AllocateRaw(counter)) InvalidVehicle();
00236 counter++;
00237 }
00238
00239 return true;
00240 }
00241
00242
00243
00244 const int HASH_BITS = 7;
00245 const int HASH_SIZE = 1 << HASH_BITS;
00246 const int HASH_MASK = HASH_SIZE - 1;
00247 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00248 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00249
00250
00251
00252 const int HASH_RES = 0;
00253
00254 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00255
00256 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00257 {
00258 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00259 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00260 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00261 for (; v != NULL; v = v->next_new_hash) {
00262 Vehicle *a = proc(v, data);
00263 if (find_first && a != NULL) return a;
00264 }
00265 if (x == xu) break;
00266 }
00267 if (y == yu) break;
00268 }
00269
00270 return NULL;
00271 }
00272
00273
00285 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00286 {
00287 const int COLL_DIST = 6;
00288
00289
00290 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00291 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00292 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00293 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00294
00295 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00296 }
00297
00312 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00313 {
00314 VehicleFromPosXY(x, y, data, proc, false);
00315 }
00316
00328 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00329 {
00330 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00331 }
00332
00343 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00344 {
00345 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00346 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00347
00348 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00349 for (; v != NULL; v = v->next_new_hash) {
00350 if (v->tile != tile) continue;
00351
00352 Vehicle *a = proc(v, data);
00353 if (find_first && a != NULL) return a;
00354 }
00355
00356 return NULL;
00357 }
00358
00372 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00373 {
00374 VehicleFromPos(tile, data, proc, false);
00375 }
00376
00387 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00388 {
00389 return VehicleFromPos(tile, data, proc, true) != NULL;
00390 }
00391
00392
00393 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00394 {
00395 Vehicle **old_hash = v->old_new_hash;
00396 Vehicle **new_hash;
00397
00398 if (remove) {
00399 new_hash = NULL;
00400 } else {
00401 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00402 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00403 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00404 }
00405
00406 if (old_hash == new_hash) return;
00407
00408
00409 if (old_hash != NULL) {
00410 Vehicle *last = NULL;
00411 Vehicle *u = *old_hash;
00412 while (u != v) {
00413 last = u;
00414 u = u->next_new_hash;
00415 assert(u != NULL);
00416 }
00417
00418 if (last == NULL) {
00419 *old_hash = v->next_new_hash;
00420 } else {
00421 last->next_new_hash = v->next_new_hash;
00422 }
00423 }
00424
00425
00426 if (new_hash != NULL) {
00427 v->next_new_hash = *new_hash;
00428 *new_hash = v;
00429 assert(v != v->next_new_hash);
00430 }
00431
00432
00433 v->old_new_hash = new_hash;
00434 }
00435
00436 static Vehicle *_vehicle_position_hash[0x1000];
00437
00438 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00439 {
00440 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00441
00442 Vehicle **old_hash, **new_hash;
00443 int old_x = v->left_coord;
00444 int old_y = v->top_coord;
00445
00446 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00447 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00448
00449 if (old_hash == new_hash) return;
00450
00451
00452 if (old_hash != NULL) {
00453 Vehicle *last = NULL;
00454 Vehicle *u = *old_hash;
00455 while (u != v) {
00456 last = u;
00457 u = u->next_hash;
00458 assert(u != NULL);
00459 }
00460
00461 if (last == NULL) {
00462 *old_hash = v->next_hash;
00463 } else {
00464 last->next_hash = v->next_hash;
00465 }
00466 }
00467
00468
00469 if (new_hash != NULL) {
00470 v->next_hash = *new_hash;
00471 *new_hash = v;
00472 }
00473 }
00474
00475 void ResetVehiclePosHash()
00476 {
00477 Vehicle *v;
00478 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00479 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00480 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00481 }
00482
00483 void ResetVehicleColourMap()
00484 {
00485 Vehicle *v;
00486 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00487 }
00488
00493 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00494 static AutoreplaceMap _vehicles_to_autoreplace;
00495
00496 void InitializeVehicles()
00497 {
00498 _Vehicle_pool.CleanPool();
00499 _Vehicle_pool.AddBlockToPool();
00500
00501 _vehicles_to_autoreplace.Reset();
00502 ResetVehiclePosHash();
00503 }
00504
00505 Vehicle *GetLastVehicleInChain(Vehicle *v)
00506 {
00507 while (v->Next() != NULL) v = v->Next();
00508 return v;
00509 }
00510
00511 const Vehicle *GetLastVehicleInChain(const Vehicle *v)
00512 {
00513 while (v->Next() != NULL) v = v->Next();
00514 return v;
00515 }
00516
00517 uint CountVehiclesInChain(const Vehicle *v)
00518 {
00519 uint count = 0;
00520 do count++; while ((v = v->Next()) != NULL);
00521 return count;
00522 }
00523
00528 bool IsEngineCountable(const Vehicle *v)
00529 {
00530 switch (v->type) {
00531 case VEH_AIRCRAFT: return IsNormalAircraft(v);
00532 case VEH_TRAIN:
00533 return !IsArticulatedPart(v) &&
00534 !IsRearDualheaded(v);
00535 case VEH_ROAD: return IsRoadVehFront(v);
00536 case VEH_SHIP: return true;
00537 default: return false;
00538 }
00539 }
00540
00541 void Vehicle::PreDestructor()
00542 {
00543 if (CleaningPool()) return;
00544
00545 if (IsValidStationID(this->last_station_visited)) {
00546 GetStation(this->last_station_visited)->loading_vehicles.remove(this);
00547
00548 HideFillingPercent(&this->fill_percent_te_id);
00549 }
00550
00551 if (IsEngineCountable(this)) {
00552 GetCompany(this->owner)->num_engines[this->engine_type]--;
00553 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00554
00555 DeleteGroupHighlightOfVehicle(this);
00556 if (IsValidGroupID(this->group_id)) GetGroup(this->group_id)->num_engines[this->engine_type]--;
00557 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00558 }
00559
00560 if (this->type == VEH_ROAD) ClearSlot(this);
00561 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00562 Station *st = GetTargetAirportIfValid(this);
00563 if (st != NULL) {
00564 const AirportFTA *layout = st->Airport()->layout;
00565 CLRBITS(st->airport_flags, layout[this->u.air.previous_pos].block | layout[this->u.air.pos].block);
00566 }
00567 }
00568
00569 if (this->type != VEH_TRAIN || (this->type == VEH_TRAIN && (IsFrontEngine(this) || IsFreeWagon(this)))) {
00570 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00571 }
00572
00573 if (this->IsPrimaryVehicle()) {
00574 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00575 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00576 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00577 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00578 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00579 InvalidateWindow(WC_COMPANY, this->owner);
00580 }
00581 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00582
00583 this->cargo.Truncate(0);
00584 DeleteVehicleOrders(this);
00585 DeleteDepotHighlightOfVehicle(this);
00586
00587 extern void StopGlobalFollowVehicle(const Vehicle *v);
00588 StopGlobalFollowVehicle(this);
00589 }
00590
00591 Vehicle::~Vehicle()
00592 {
00593 free(this->name);
00594
00595 if (CleaningPool()) return;
00596
00597
00598
00599 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00600
00601 Vehicle *v = this->Next();
00602 this->SetNext(NULL);
00603
00604 delete v;
00605
00606 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00607 this->next_hash = NULL;
00608 this->next_new_hash = NULL;
00609
00610 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00611
00612 new (this) InvalidVehicle();
00613 }
00614
00618 void VehicleEnteredDepotThisTick(Vehicle *v)
00619 {
00620
00621
00622 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED) &&
00623 (!v->current_order.IsType(OT_GOTO_DEPOT) ||
00624 !(v->current_order.GetDepotActionType() & ODATFB_HALT));
00625
00626
00627
00628
00629
00630
00631 v->vehstatus |= VS_STOPPED;
00632 }
00633
00634 void CallVehicleTicks()
00635 {
00636 _vehicles_to_autoreplace.Clear();
00637
00638 Station *st;
00639 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00640
00641 Vehicle *v;
00642 FOR_ALL_VEHICLES(v) {
00643 v->Tick();
00644
00645 switch (v->type) {
00646 default: break;
00647
00648 case VEH_TRAIN:
00649 case VEH_ROAD:
00650 case VEH_AIRCRAFT:
00651 case VEH_SHIP:
00652 if (v->type == VEH_TRAIN && IsTrainWagon(v)) continue;
00653 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00654 if (v->type == VEH_ROAD && !IsRoadVehFront(v)) continue;
00655
00656 v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
00657
00658 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00659
00660
00661 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00662 }
00663 }
00664
00665 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00666 v = it->first;
00667
00668 _current_company = v->owner;
00669
00670
00671
00672
00673 if (it->second) v->vehstatus &= ~VS_STOPPED;
00674
00675
00676 int x = v->x_pos;
00677 int y = v->y_pos;
00678 int z = v->z_pos;
00679
00680 const Company *c = GetCompany(_current_company);
00681 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->engine_renew_money));
00682 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00683 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->engine_renew_money));
00684
00685 if (!IsLocalCompany()) continue;
00686
00687 if (res.Succeeded()) {
00688 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00689 continue;
00690 }
00691
00692 StringID error_message = res.GetErrorMessage();
00693 if (error_message == STR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00694
00695 if (error_message == STR_0003_NOT_ENOUGH_CASH_REQUIRES) error_message = STR_AUTOREPLACE_MONEY_LIMIT;
00696
00697 StringID message;
00698 if (error_message == STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00699 message = error_message;
00700 } else {
00701 message = STR_VEHICLE_AUTORENEW_FAILED;
00702 }
00703
00704 SetDParam(0, v->index);
00705 SetDParam(1, error_message);
00706 AddNewsItem(message, NS_ADVICE, v->index, 0);
00707 }
00708
00709 _current_company = OWNER_NONE;
00710 }
00711
00717 bool CanRefitTo(EngineID engine_type, CargoID cid_to)
00718 {
00719 return HasBit(EngInfo(engine_type)->refit_mask, cid_to);
00720 }
00721
00726 CargoID FindFirstRefittableCargo(EngineID engine_type)
00727 {
00728 uint32 refit_mask = EngInfo(engine_type)->refit_mask;
00729
00730 if (refit_mask != 0) {
00731 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00732 if (HasBit(refit_mask, cid)) return cid;
00733 }
00734 }
00735
00736 return CT_INVALID;
00737 }
00738
00743 CommandCost GetRefitCost(EngineID engine_type)
00744 {
00745 Money base_cost;
00746 ExpensesType expense_type;
00747 switch (GetEngine(engine_type)->type) {
00748 case VEH_SHIP:
00749 base_cost = _price.ship_base;
00750 expense_type = EXPENSES_SHIP_RUN;
00751 break;
00752
00753 case VEH_ROAD:
00754 base_cost = _price.roadveh_base;
00755 expense_type = EXPENSES_ROADVEH_RUN;
00756 break;
00757
00758 case VEH_AIRCRAFT:
00759 base_cost = _price.aircraft_base;
00760 expense_type = EXPENSES_AIRCRAFT_RUN;
00761 break;
00762
00763 case VEH_TRAIN:
00764 base_cost = 2 * ((RailVehInfo(engine_type)->railveh_type == RAILVEH_WAGON) ?
00765 _price.build_railwagon : _price.build_railvehicle);
00766 expense_type = EXPENSES_TRAIN_RUN;
00767 break;
00768
00769 default: NOT_REACHED();
00770 }
00771 return CommandCost(expense_type, (EngInfo(engine_type)->refit_cost * base_cost) >> 10);
00772 }
00773
00774 static void DoDrawVehicle(const Vehicle *v)
00775 {
00776 SpriteID image = v->cur_image;
00777 SpriteID pal = PAL_NONE;
00778
00779 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00780
00781 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00782 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00783 }
00784
00785 void ViewportAddVehicles(DrawPixelInfo *dpi)
00786 {
00787
00788 const int l = dpi->left;
00789 const int r = dpi->left + dpi->width;
00790 const int t = dpi->top;
00791 const int b = dpi->top + dpi->height;
00792
00793
00794 int xl, xu, yl, yu;
00795
00796 if (dpi->width + 70 < (1 << (7 + 6))) {
00797 xl = GB(l - 70, 7, 6);
00798 xu = GB(r, 7, 6);
00799 } else {
00800
00801 xl = 0;
00802 xu = 0x3F;
00803 }
00804
00805 if (dpi->height + 70 < (1 << (6 + 6))) {
00806 yl = GB(t - 70, 6, 6) << 6;
00807 yu = GB(b, 6, 6) << 6;
00808 } else {
00809
00810 yl = 0;
00811 yu = 0x3F << 6;
00812 }
00813
00814 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00815 for (int x = xl;; x = (x + 1) & 0x3F) {
00816 const Vehicle *v = _vehicle_position_hash[x + y];
00817
00818 while (v != NULL) {
00819 if (!(v->vehstatus & VS_HIDDEN) &&
00820 l <= v->right_coord &&
00821 t <= v->bottom_coord &&
00822 r >= v->left_coord &&
00823 b >= v->top_coord) {
00824 DoDrawVehicle(v);
00825 }
00826 v = v->next_hash;
00827 }
00828
00829 if (x == xu) break;
00830 }
00831
00832 if (y == yu) break;
00833 }
00834 }
00835
00836 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00837 {
00838 Vehicle *found = NULL, *v;
00839 uint dist, best_dist = UINT_MAX;
00840
00841 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00842
00843 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00844 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00845
00846 FOR_ALL_VEHICLES(v) {
00847 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00848 x >= v->left_coord && x <= v->right_coord &&
00849 y >= v->top_coord && y <= v->bottom_coord) {
00850
00851 dist = max(
00852 abs(((v->left_coord + v->right_coord) >> 1) - x),
00853 abs(((v->top_coord + v->bottom_coord) >> 1) - y)
00854 );
00855
00856 if (dist < best_dist) {
00857 found = v;
00858 best_dist = dist;
00859 }
00860 }
00861 }
00862
00863 return found;
00864 }
00865
00866 void CheckVehicle32Day(Vehicle *v)
00867 {
00868 if ((v->day_counter & 0x1F) != 0) return;
00869
00870 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00871 if (callback == CALLBACK_FAILED) return;
00872 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00873 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00874 }
00875
00876 void DecreaseVehicleValue(Vehicle *v)
00877 {
00878 v->value -= v->value >> 8;
00879 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00880 }
00881
00882 static const byte _breakdown_chance[64] = {
00883 3, 3, 3, 3, 3, 3, 3, 3,
00884 4, 4, 5, 5, 6, 6, 7, 7,
00885 8, 8, 9, 9, 10, 10, 11, 11,
00886 12, 13, 13, 13, 13, 14, 15, 16,
00887 17, 19, 21, 25, 28, 31, 34, 37,
00888 40, 44, 48, 52, 56, 60, 64, 68,
00889 72, 80, 90, 100, 110, 120, 130, 140,
00890 150, 170, 190, 210, 230, 250, 250, 250,
00891 };
00892
00893 void CheckVehicleBreakdown(Vehicle *v)
00894 {
00895 int rel, rel_old;
00896
00897
00898 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
00899 if ((rel_old >> 8) != (rel >> 8)) InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00900
00901 if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED ||
00902 _settings_game.difficulty.vehicle_breakdowns < 1 ||
00903 v->cur_speed < 5 || _game_mode == GM_MENU) {
00904 return;
00905 }
00906
00907 uint32 r = Random();
00908
00909
00910 int chance = v->breakdown_chance + 1;
00911 if (Chance16I(1, 25, r)) chance += 25;
00912 v->breakdown_chance = min(255, chance);
00913
00914
00915 rel = v->reliability;
00916 if (v->type == VEH_SHIP) rel += 0x6666;
00917
00918
00919 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
00920
00921
00922 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
00923 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
00924 v->breakdown_delay = GB(r, 24, 7) + 0x80;
00925 v->breakdown_chance = 0;
00926 }
00927 }
00928
00929 void AgeVehicle(Vehicle *v)
00930 {
00931 if (v->age < 65535) v->age++;
00932
00933 int age = v->age - v->max_age;
00934 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
00935 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
00936 v->reliability_spd_dec <<= 1;
00937 }
00938
00939 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00940
00941
00942 if (v->Previous() != NULL || v->owner != _local_company) return;
00943
00944
00945 if (GetCompany(v->owner)->engine_renew && GetEngine(v->engine_type)->company_avail != 0) return;
00946
00947 StringID str;
00948 if (age == -DAYS_IN_LEAP_YEAR) {
00949 str = STR_01A0_IS_GETTING_OLD;
00950 } else if (age == 0) {
00951 str = STR_01A1_IS_GETTING_VERY_OLD;
00952 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
00953 str = STR_01A2_IS_GETTING_VERY_OLD_AND;
00954 } else {
00955 return;
00956 }
00957
00958 SetDParam(0, v->index);
00959 AddNewsItem(str, NS_ADVICE, v->index, 0);
00960 }
00961
00969 CommandCost CmdStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00970 {
00971
00972 if ((flags & DC_AUTOREPLACE) == 0) SetBit(p2, 0);
00973
00974 if (!IsValidVehicleID(p1)) return CMD_ERROR;
00975
00976 Vehicle *v = GetVehicle(p1);
00977
00978 if (!CheckOwnership(v->owner)) return CMD_ERROR;
00979 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
00980
00981 switch (v->type) {
00982 case VEH_TRAIN:
00983 if (v->vehstatus & VS_STOPPED && v->u.rail.cached_power == 0) return_cmd_error(STR_TRAIN_START_NO_CATENARY);
00984 break;
00985
00986 case VEH_SHIP:
00987 case VEH_ROAD:
00988 break;
00989
00990 case VEH_AIRCRAFT:
00991
00992 if (v->u.air.state >= STARTTAKEOFF && v->u.air.state < TERM7) return_cmd_error(STR_A017_AIRCRAFT_IS_IN_FLIGHT);
00993 break;
00994
00995 default: return CMD_ERROR;
00996 }
00997
00998
00999
01000 uint16 callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
01001 if (callback != CALLBACK_FAILED && GB(callback, 0, 8) != 0xFF && HasBit(p2, 0)) {
01002 StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
01003 return_cmd_error(error);
01004 }
01005
01006 if (flags & DC_EXEC) {
01007 static const StringID vehicle_waiting_in_depot[] = {
01008 STR_8814_TRAIN_IS_WAITING_IN_DEPOT,
01009 STR_9016_ROAD_VEHICLE_IS_WAITING,
01010 STR_981C_SHIP_IS_WAITING_IN_DEPOT,
01011 STR_A014_AIRCRAFT_IS_WAITING_IN,
01012 };
01013
01014 static const WindowClass vehicle_list[] = {
01015 WC_TRAINS_LIST,
01016 WC_ROADVEH_LIST,
01017 WC_SHIPS_LIST,
01018 WC_AIRCRAFT_LIST,
01019 };
01020
01021 if (v->IsStoppedInDepot() && (flags & DC_AUTOREPLACE) == 0) DeleteVehicleNews(p1, vehicle_waiting_in_depot[v->type]);
01022
01023 v->vehstatus ^= VS_STOPPED;
01024 if (v->type != VEH_TRAIN) v->cur_speed = 0;
01025 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01026 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01027 InvalidateWindowClasses(vehicle_list[v->type]);
01028 }
01029 return CommandCost();
01030 }
01031
01042 CommandCost CmdMassStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01043 {
01044 VehicleList list;
01045 CommandCost return_value = CMD_ERROR;
01046 VehicleType vehicle_type = (VehicleType)GB(p2, 0, 5);
01047 bool start_stop = HasBit(p2, 5);
01048 bool vehicle_list_window = HasBit(p2, 6);
01049
01050 if (vehicle_list_window) {
01051 uint32 id = p1;
01052 uint16 window_type = p2 & VLW_MASK;
01053
01054 GenerateVehicleSortList(&list, vehicle_type, _current_company, id, window_type);
01055 } else {
01056
01057 BuildDepotVehicleList(vehicle_type, tile, &list, NULL);
01058 }
01059
01060 for (uint i = 0; i < list.Length(); i++) {
01061 const Vehicle *v = list[i];
01062
01063 if (!!(v->vehstatus & VS_STOPPED) != start_stop) continue;
01064
01065 if (!vehicle_list_window) {
01066 if (vehicle_type == VEH_TRAIN) {
01067 if (CheckTrainInDepot(v, false) == -1) continue;
01068 } else {
01069 if (!(v->vehstatus & VS_HIDDEN)) continue;
01070 }
01071 }
01072
01073 CommandCost ret = DoCommand(tile, v->index, 0, flags, CMD_START_STOP_VEHICLE);
01074
01075 if (CmdSucceeded(ret)) {
01076 return_value = CommandCost();
01077
01078
01079 if (!(flags & DC_EXEC)) break;
01080 }
01081 }
01082
01083 return return_value;
01084 }
01085
01092 CommandCost CmdDepotSellAllVehicles(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01093 {
01094 VehicleList list;
01095
01096 CommandCost cost(EXPENSES_NEW_VEHICLES);
01097 uint sell_command;
01098 VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
01099
01100 switch (vehicle_type) {
01101 case VEH_TRAIN: sell_command = CMD_SELL_RAIL_WAGON; break;
01102 case VEH_ROAD: sell_command = CMD_SELL_ROAD_VEH; break;
01103 case VEH_SHIP: sell_command = CMD_SELL_SHIP; break;
01104 case VEH_AIRCRAFT: sell_command = CMD_SELL_AIRCRAFT; break;
01105 default: return CMD_ERROR;
01106 }
01107
01108
01109 BuildDepotVehicleList(vehicle_type, tile, &list, &list);
01110
01111 for (uint i = 0; i < list.Length(); i++) {
01112 CommandCost ret = DoCommand(tile, list[i]->index, 1, flags, sell_command);
01113 if (CmdSucceeded(ret)) cost.AddCost(ret);
01114 }
01115
01116 if (cost.GetCost() == 0) return CMD_ERROR;
01117 return cost;
01118 }
01119
01129 CommandCost CmdDepotMassAutoReplace(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01130 {
01131 VehicleList list;
01132 CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES);
01133 VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
01134 bool all_or_nothing = HasBit(p2, 0);
01135
01136 if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
01137
01138
01139 BuildDepotVehicleList(vehicle_type, tile, &list, &list, true);
01140
01141 bool did_something = false;
01142
01143 for (uint i = 0; i < list.Length(); i++) {
01144 Vehicle *v = (Vehicle*)list[i];
01145
01146
01147 if (!v->IsInDepot()) continue;
01148
01149 CommandCost ret = DoCommand(0, v->index, 0, flags, CMD_AUTOREPLACE_VEHICLE);
01150
01151 if (CmdSucceeded(ret)) {
01152 did_something = true;
01153 cost.AddCost(ret);
01154 } else {
01155 if (ret.GetErrorMessage() != STR_AUTOREPLACE_NOTHING_TO_DO && all_or_nothing) {
01156
01157
01158
01159 assert(!(flags & DC_EXEC));
01160
01161 return CMD_ERROR;
01162 }
01163 }
01164 }
01165
01166 if (!did_something) {
01167
01168
01169 cost = CMD_ERROR;
01170 }
01171
01172 return cost;
01173 }
01174
01181 CommandCost CmdCloneVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01182 {
01183 CommandCost total_cost(EXPENSES_NEW_VEHICLES);
01184 uint32 build_argument = 2;
01185
01186 if (!IsValidVehicleID(p1)) return CMD_ERROR;
01187
01188 Vehicle *v = GetVehicle(p1);
01189 Vehicle *v_front = v;
01190 Vehicle *w = NULL;
01191 Vehicle *w_front = NULL;
01192 Vehicle *w_rear = NULL;
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202 if (!CheckOwnership(v->owner)) return CMD_ERROR;
01203
01204 if (v->type == VEH_TRAIN && (!IsFrontEngine(v) || v->u.rail.crash_anim_pos >= 4400)) return CMD_ERROR;
01205
01206
01207 if (!(flags & DC_EXEC)) {
01208 int veh_counter = 0;
01209 do {
01210 veh_counter++;
01211 } while ((v = v->Next()) != NULL);
01212
01213 if (!Vehicle::AllocateList(NULL, veh_counter)) {
01214 return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
01215 }
01216 }
01217
01218 v = v_front;
01219
01220 do {
01221 if (v->type == VEH_TRAIN && IsRearDualheaded(v)) {
01222
01223 continue;
01224 }
01225
01226 CommandCost cost = DoCommand(tile, v->engine_type, build_argument, flags, GetCmdBuildVeh(v));
01227 build_argument = 3;
01228
01229 if (CmdFailed(cost)) return cost;
01230
01231 total_cost.AddCost(cost);
01232
01233 if (flags & DC_EXEC) {
01234 w = GetVehicle(_new_vehicle_id);
01235
01236 if (v->type == VEH_TRAIN && HasBit(v->u.rail.flags, VRF_REVERSE_DIRECTION)) {
01237 SetBit(w->u.rail.flags, VRF_REVERSE_DIRECTION);
01238 }
01239
01240 if (v->type == VEH_TRAIN && !IsFrontEngine(v)) {
01241
01242
01243 CommandCost result = DoCommand(0, (w_rear->index << 16) | w->index, 1, flags, CMD_MOVE_RAIL_VEHICLE);
01244 if (CmdFailed(result)) {
01245
01246
01247 DoCommand(w_front->tile, w_front->index, 1, flags, GetCmdSellVeh(w_front));
01248 DoCommand(w_front->tile, w->index, 1, flags, GetCmdSellVeh(w));
01249 return result;
01250 }
01251 } else {
01252
01253 w_front = w;
01254 w->service_interval = v->service_interval;
01255 }
01256 w_rear = w;
01257 }
01258 } while (v->type == VEH_TRAIN && (v = GetNextVehicle(v)) != NULL);
01259
01260 if (flags & DC_EXEC && v_front->type == VEH_TRAIN) {
01261
01262 _new_vehicle_id = w_front->index;
01263 }
01264
01265 if (flags & DC_EXEC) {
01266
01267 DoCommand(0, v_front->group_id, w_front->index, flags, CMD_ADD_VEHICLE_GROUP);
01268 }
01269
01270
01271
01272 w = w_front;
01273 v = v_front;
01274
01275
01276
01277
01278
01279
01280
01281 do {
01282 do {
01283 if (flags & DC_EXEC) {
01284 assert(w != NULL);
01285
01286 if (w->cargo_type != v->cargo_type || w->cargo_subtype != v->cargo_subtype) {
01287 CommandCost cost = DoCommand(0, w->index, v->cargo_type | (v->cargo_subtype << 8) | 1U << 16 , flags, GetCmdRefitVeh(v));
01288 if (CmdSucceeded(cost)) total_cost.AddCost(cost);
01289 }
01290
01291 if (w->type == VEH_TRAIN && EngineHasArticPart(w)) {
01292 w = GetNextArticPart(w);
01293 } else if (w->type == VEH_ROAD && RoadVehHasArticPart(w)) {
01294 w = w->Next();
01295 } else {
01296 break;
01297 }
01298 } else {
01299 CargoID initial_cargo = GetEngineCargoType(v->engine_type);
01300
01301 if (v->cargo_type != initial_cargo && initial_cargo != CT_INVALID) {
01302 total_cost.AddCost(GetRefitCost(v->engine_type));
01303 }
01304 }
01305
01306 if (v->type == VEH_TRAIN && EngineHasArticPart(v)) {
01307 v = GetNextArticPart(v);
01308 } else if (v->type == VEH_ROAD && RoadVehHasArticPart(v)) {
01309 v = v->Next();
01310 } else {
01311 break;
01312 }
01313 } while (v != NULL);
01314
01315 if ((flags & DC_EXEC) && v->type == VEH_TRAIN) w = GetNextVehicle(w);
01316 } while (v->type == VEH_TRAIN && (v = GetNextVehicle(v)) != NULL);
01317
01318 if (flags & DC_EXEC) {
01319
01320
01321
01322
01323
01324 DoCommand(0, (v_front->index << 16) | w_front->index, p2 & 1 ? CO_SHARE : CO_COPY, flags, CMD_CLONE_ORDER);
01325 }
01326
01327
01328
01329 if (!CheckCompanyHasMoney(total_cost)) {
01330 if (flags & DC_EXEC) {
01331
01332 DoCommand(w_front->tile, w_front->index, 1, flags, GetCmdSellVeh(w_front));
01333 }
01334 return CMD_ERROR;
01335 }
01336
01337 return total_cost;
01338 }
01339
01349 CommandCost SendAllVehiclesToDepot(VehicleType type, DoCommandFlag flags, bool service, Owner owner, uint16 vlw_flag, uint32 id)
01350 {
01351 VehicleList list;
01352
01353 GenerateVehicleSortList(&list, type, owner, id, vlw_flag);
01354
01355
01356 for (uint i = 0; i < list.Length(); i++) {
01357 const Vehicle *v = list[i];
01358 CommandCost ret = DoCommand(v->tile, v->index, (service ? 1 : 0) | DEPOT_DONT_CANCEL, flags, GetCmdSendToDepot(type));
01359
01360
01361
01362
01363
01364 if (CmdSucceeded(ret) && !(flags & DC_EXEC)) {
01365 return CommandCost();
01366 }
01367 }
01368
01369 return (flags & DC_EXEC) ? CommandCost() : CMD_ERROR;
01370 }
01371
01378 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01379 {
01380 int count = 0;
01381 int max = 0;
01382 int cars = 0;
01383 int unloading = 0;
01384 bool loading = false;
01385
01386 const Vehicle *u = v;
01387 const Station *st = v->last_station_visited != INVALID_STATION ? GetStation(v->last_station_visited) : NULL;
01388
01389
01390 for (; v != NULL; v = v->Next()) {
01391 count += v->cargo.Count();
01392 max += v->cargo_cap;
01393 if (v->cargo_cap != 0 && colour != NULL) {
01394 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01395 loading |= !(u->current_order.GetUnloadType() & OUFB_UNLOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01396 cars++;
01397 }
01398 }
01399
01400 if (colour != NULL) {
01401 if (unloading == 0 && loading) {
01402 *colour = STR_PERCENT_UP;
01403 } else if (cars == unloading || !loading) {
01404 *colour = STR_PERCENT_DOWN;
01405 } else {
01406 *colour = STR_PERCENT_UP_DOWN;
01407 }
01408 }
01409
01410
01411 if (max == 0) return 100;
01412
01413
01414 return (count * 100) / max;
01415 }
01416
01417 void VehicleEnterDepot(Vehicle *v)
01418 {
01419 switch (v->type) {
01420 case VEH_TRAIN:
01421 InvalidateWindowClasses(WC_TRAINS_LIST);
01422
01423 SetDepotWaypointReservation(v->tile, false);
01424 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
01425
01426 if (!IsFrontEngine(v)) v = v->First();
01427 UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
01428 v->load_unload_time_rem = 0;
01429 ClrBit(v->u.rail.flags, VRF_TOGGLE_REVERSE);
01430 TrainConsistChanged(v, true);
01431 break;
01432
01433 case VEH_ROAD:
01434 InvalidateWindowClasses(WC_ROADVEH_LIST);
01435 if (!IsRoadVehFront(v)) v = v->First();
01436 break;
01437
01438 case VEH_SHIP:
01439 InvalidateWindowClasses(WC_SHIPS_LIST);
01440 v->u.ship.state = TRACK_BIT_DEPOT;
01441 RecalcShipStuff(v);
01442 break;
01443
01444 case VEH_AIRCRAFT:
01445 InvalidateWindowClasses(WC_AIRCRAFT_LIST);
01446 HandleAircraftEnterHangar(v);
01447 break;
01448 default: NOT_REACHED();
01449 }
01450
01451 if (v->type != VEH_TRAIN) {
01452
01453
01454 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01455 }
01456 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01457
01458 v->vehstatus |= VS_HIDDEN;
01459 v->cur_speed = 0;
01460
01461 VehicleServiceInDepot(v);
01462
01463 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01464
01465 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01466 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01467
01468 Order t = v->current_order;
01469 v->current_order.MakeDummy();
01470
01471 if (t.IsRefit()) {
01472 _current_company = v->owner;
01473 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01474
01475 if (CmdFailed(cost)) {
01476 _vehicles_to_autoreplace[v] = false;
01477 if (v->owner == _local_company) {
01478
01479 SetDParam(0, v->index);
01480 AddNewsItem(STR_ORDER_REFIT_FAILED, NS_ADVICE, v->index, 0);
01481 }
01482 } else if (v->owner == _local_company && cost.GetCost() != 0) {
01483 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01484 }
01485 }
01486
01487 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01488
01489 UpdateVehicleTimetable(v, true);
01490 v->cur_order_index++;
01491 }
01492 if (t.GetDepotActionType() & ODATFB_HALT) {
01493
01494 v->vehstatus |= VS_STOPPED;
01495 if (v->owner == _local_company) {
01496 StringID string;
01497
01498 switch (v->type) {
01499 case VEH_TRAIN: string = STR_8814_TRAIN_IS_WAITING_IN_DEPOT; break;
01500 case VEH_ROAD: string = STR_9016_ROAD_VEHICLE_IS_WAITING; break;
01501 case VEH_SHIP: string = STR_981C_SHIP_IS_WAITING_IN_DEPOT; break;
01502 case VEH_AIRCRAFT: string = STR_A014_AIRCRAFT_IS_WAITING_IN; break;
01503 default: NOT_REACHED(); string = STR_EMPTY;
01504 }
01505
01506 SetDParam(0, v->index);
01507 AddNewsItem(string, NS_ADVICE, v->index, 0);
01508 }
01509 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01510 }
01511 }
01512 }
01513
01514 static bool IsUniqueVehicleName(const char *name)
01515 {
01516 const Vehicle *v;
01517
01518 FOR_ALL_VEHICLES(v) {
01519 if (v->name != NULL && strcmp(v->name, name) == 0) return false;
01520 }
01521
01522 return true;
01523 }
01524
01531 CommandCost CmdRenameVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01532 {
01533 if (!IsValidVehicleID(p1)) return CMD_ERROR;
01534
01535 Vehicle *v = GetVehicle(p1);
01536 if (!CheckOwnership(v->owner)) return CMD_ERROR;
01537
01538 bool reset = StrEmpty(text);
01539
01540 if (!reset) {
01541 if (strlen(text) >= MAX_LENGTH_VEHICLE_NAME_BYTES) return CMD_ERROR;
01542 if (!(flags & DC_AUTOREPLACE) && !IsUniqueVehicleName(text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
01543 }
01544
01545 if (flags & DC_EXEC) {
01546 free(v->name);
01547 v->name = reset ? NULL : strdup(text);
01548 InvalidateWindowClassesData(WC_TRAINS_LIST, 1);
01549 MarkWholeScreenDirty();
01550 }
01551
01552 return CommandCost();
01553 }
01554
01555
01562 CommandCost CmdChangeServiceInt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01563 {
01564 uint16 serv_int = GetServiceIntervalClamped(p2);
01565
01566 if (serv_int != p2 || !IsValidVehicleID(p1)) return CMD_ERROR;
01567
01568 Vehicle *v = GetVehicle(p1);
01569
01570 if (!CheckOwnership(v->owner)) return CMD_ERROR;
01571
01572 if (flags & DC_EXEC) {
01573 v->service_interval = serv_int;
01574 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01575 }
01576
01577 return CommandCost();
01578 }
01579
01580
01581 static Rect _old_vehicle_coords;
01582
01589 void BeginVehicleMove(const Vehicle *v)
01590 {
01591 _old_vehicle_coords.left = v->left_coord;
01592 _old_vehicle_coords.top = v->top_coord;
01593 _old_vehicle_coords.right = v->right_coord;
01594 _old_vehicle_coords.bottom = v->bottom_coord;
01595 }
01596
01603 void EndVehicleMove(const Vehicle *v)
01604 {
01605 MarkAllViewportsDirty(
01606 min(_old_vehicle_coords.left, v->left_coord),
01607 min(_old_vehicle_coords.top, v->top_coord),
01608 max(_old_vehicle_coords.right, v->right_coord) + 1,
01609 max(_old_vehicle_coords.bottom, v->bottom_coord) + 1
01610 );
01611 }
01612
01621 void MarkSingleVehicleDirty(const Vehicle *v)
01622 {
01623 MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
01624 }
01625
01630 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01631 {
01632 static const int8 _delta_coord[16] = {
01633 -1,-1,-1, 0, 1, 1, 1, 0,
01634 -1, 0, 1, 1, 1, 0,-1,-1,
01635 };
01636
01637 int x = v->x_pos + _delta_coord[v->direction];
01638 int y = v->y_pos + _delta_coord[v->direction + 8];
01639
01640 GetNewVehiclePosResult gp;
01641 gp.x = x;
01642 gp.y = y;
01643 gp.old_tile = v->tile;
01644 gp.new_tile = TileVirtXY(x, y);
01645 return gp;
01646 }
01647
01648 static const Direction _new_direction_table[] = {
01649 DIR_N , DIR_NW, DIR_W ,
01650 DIR_NE, DIR_SE, DIR_SW,
01651 DIR_E , DIR_SE, DIR_S
01652 };
01653
01654 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01655 {
01656 int i = 0;
01657
01658 if (y >= v->y_pos) {
01659 if (y != v->y_pos) i += 3;
01660 i += 3;
01661 }
01662
01663 if (x >= v->x_pos) {
01664 if (x != v->x_pos) i++;
01665 i++;
01666 }
01667
01668 Direction dir = v->direction;
01669
01670 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01671 if (dirdiff == DIRDIFF_SAME) return dir;
01672 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01673 }
01674
01675 Trackdir GetVehicleTrackdir(const Vehicle *v)
01676 {
01677 if (v->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
01678
01679 switch (v->type) {
01680 case VEH_TRAIN:
01681 if (v->u.rail.track == TRACK_BIT_DEPOT)
01682 return DiagDirToDiagTrackdir(GetRailDepotDirection(v->tile));
01683
01684 if (v->u.rail.track == TRACK_BIT_WORMHOLE)
01685 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01686
01687 return TrackDirectionToTrackdir(FindFirstTrack(v->u.rail.track), v->direction);
01688
01689 case VEH_SHIP:
01690 if (v->IsInDepot())
01691
01692 return DiagDirToDiagTrackdir(GetShipDepotDirection(v->tile));
01693
01694 if (v->u.ship.state == TRACK_BIT_WORMHOLE)
01695 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01696
01697 return TrackDirectionToTrackdir(FindFirstTrack(v->u.ship.state), v->direction);
01698
01699 case VEH_ROAD:
01700 if (v->IsInDepot())
01701 return DiagDirToDiagTrackdir(GetRoadDepotDirection(v->tile));
01702
01703 if (IsStandardRoadStopTile(v->tile))
01704 return DiagDirToDiagTrackdir(GetRoadStopDir(v->tile));
01705
01706 if (IsDriveThroughStopTile(v->tile)) return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01707
01708
01709 if (!IsReversingRoadTrackdir((Trackdir)v->u.road.state)) return (Trackdir)v->u.road.state;
01710
01711
01712 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01713
01714
01715 default: return INVALID_TRACKDIR;
01716 }
01717 }
01718
01728 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01729 {
01730 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01731 }
01732
01733 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01734 {
01735
01736 const Vehicle *v;
01737 FOR_ALL_VEHICLES(v) {
01738 if (v->type == type && v->owner == owner) {
01739 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01740 }
01741 }
01742
01743 if (this->maxid == 0) return;
01744
01745 this->maxid++;
01746 this->maxid++;
01747
01748 this->cache = MallocT<bool>(this->maxid);
01749
01750 MemSetT(this->cache, 0, this->maxid);
01751
01752
01753 FOR_ALL_VEHICLES(v) {
01754 if (v->type == type && v->owner == owner) {
01755 this->cache[v->unitnumber] = true;
01756 }
01757 }
01758 }
01759
01760 UnitID FreeUnitIDGenerator::NextID()
01761 {
01762 if (this->maxid <= this->curid) return ++this->curid;
01763
01764 while (this->cache[++this->curid]) { }
01765
01766 return this->curid;
01767 }
01768
01769 UnitID GetFreeUnitNumber(VehicleType type)
01770 {
01771 FreeUnitIDGenerator gen(type, _current_company);
01772
01773 return gen.NextID();
01774 }
01775
01776
01785 bool CanBuildVehicleInfrastructure(VehicleType type)
01786 {
01787 assert(IsCompanyBuildableVehicleType(type));
01788
01789 if (!IsValidCompanyID(_local_company)) return false;
01790 if (_settings_client.gui.always_build_infrastructure) return true;
01791
01792 UnitID max;
01793 switch (type) {
01794 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01795 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01796 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01797 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01798 default: NOT_REACHED();
01799 }
01800
01801
01802 if (max > 0) {
01803
01804 const Engine *e;
01805 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01806 if (HasBit(e->company_avail, _local_company)) return true;
01807 }
01808 return false;
01809 }
01810
01811
01812 const Vehicle *v;
01813 FOR_ALL_VEHICLES(v) {
01814 if (v->owner == _local_company && v->type == type) return true;
01815 }
01816
01817 return false;
01818 }
01819
01820
01821 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01822 {
01823 const Company *c = GetCompany(company);
01824 LiveryScheme scheme = LS_DEFAULT;
01825 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01826
01827
01828
01829 if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
01830
01831 switch (GetEngine(engine_type)->type) {
01832 default: NOT_REACHED();
01833 case VEH_TRAIN: {
01834 const RailVehicleInfo *rvi = RailVehInfo(engine_type);
01835
01836 if (cargo_type == CT_INVALID) cargo_type = rvi->cargo_type;
01837 if (rvi->railveh_type == RAILVEH_WAGON) {
01838 if (!GetCargo(cargo_type)->is_freight) {
01839 if (parent_engine_type == INVALID_ENGINE) {
01840 scheme = LS_PASSENGER_WAGON_STEAM;
01841 } else {
01842 switch (RailVehInfo(parent_engine_type)->engclass) {
01843 default: NOT_REACHED();
01844 case EC_STEAM: scheme = LS_PASSENGER_WAGON_STEAM; break;
01845 case EC_DIESEL: scheme = LS_PASSENGER_WAGON_DIESEL; break;
01846 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
01847 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
01848 case EC_MAGLEV: scheme = LS_PASSENGER_WAGON_MAGLEV; break;
01849 }
01850 }
01851 } else {
01852 scheme = LS_FREIGHT_WAGON;
01853 }
01854 } else {
01855 bool is_mu = HasBit(EngInfo(engine_type)->misc_flags, EF_RAIL_IS_MU);
01856
01857 switch (rvi->engclass) {
01858 default: NOT_REACHED();
01859 case EC_STEAM: scheme = LS_STEAM; break;
01860 case EC_DIESEL: scheme = is_mu ? LS_DMU : LS_DIESEL; break;
01861 case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
01862 case EC_MONORAIL: scheme = LS_MONORAIL; break;
01863 case EC_MAGLEV: scheme = LS_MAGLEV; break;
01864 }
01865 }
01866 break;
01867 }
01868
01869 case VEH_ROAD: {
01870 const RoadVehicleInfo *rvi = RoadVehInfo(engine_type);
01871 if (cargo_type == CT_INVALID) cargo_type = rvi->cargo_type;
01872 if (HasBit(EngInfo(engine_type)->misc_flags, EF_ROAD_TRAM)) {
01873
01874 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01875 } else {
01876
01877 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01878 }
01879 break;
01880 }
01881
01882 case VEH_SHIP: {
01883 const ShipVehicleInfo *svi = ShipVehInfo(engine_type);
01884 if (cargo_type == CT_INVALID) cargo_type = svi->cargo_type;
01885 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01886 break;
01887 }
01888
01889 case VEH_AIRCRAFT: {
01890 const AircraftVehicleInfo *avi = AircraftVehInfo(engine_type);
01891 if (cargo_type == CT_INVALID) cargo_type = CT_PASSENGERS;
01892 switch (avi->subtype) {
01893 case AIR_HELI: scheme = LS_HELICOPTER; break;
01894 case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
01895 case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
01896 }
01897 break;
01898 }
01899 }
01900
01901
01902 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01903 }
01904
01905 return &c->livery[scheme];
01906 }
01907
01908
01909 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01910 {
01911 SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01912
01913
01914 if (map != PAL_NONE) return map;
01915
01916
01917 if (HasBit(EngInfo(engine_type)->callbackmask, CBM_VEHICLE_COLOUR_REMAP)) {
01918 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01919
01920
01921 if (callback != CALLBACK_FAILED && callback != 0xC000) {
01922 map = GB(callback, 0, 14);
01923
01924
01925 if (!HasBit(callback, 14)) {
01926
01927 if (v != NULL) ((Vehicle*)v)->colourmap = map;
01928 return map;
01929 }
01930 }
01931 }
01932
01933 bool twocc = HasBit(EngInfo(engine_type)->misc_flags, EF_USES_2CC);
01934
01935 if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
01936
01937 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
01938
01939 map += livery->colour1;
01940 if (twocc) map += livery->colour2 * 16;
01941
01942
01943 if (v != NULL) ((Vehicle*)v)->colourmap = map;
01944 return map;
01945 }
01946
01947 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
01948 {
01949 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01950 }
01951
01952 SpriteID GetVehiclePalette(const Vehicle *v)
01953 {
01954 if (v->type == VEH_TRAIN) {
01955 return GetEngineColourMap(
01956 (v->u.rail.first_engine != INVALID_ENGINE && (UsesWagonOverride(v) || (IsArticulatedPart(v) && RailVehInfo(v->engine_type)->railveh_type != RAILVEH_WAGON))) ?
01957 v->u.rail.first_engine : v->engine_type,
01958 v->owner, v->u.rail.first_engine, v);
01959 }
01960
01961 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01962 }
01963
01964
01965 void Vehicle::BeginLoading()
01966 {
01967 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01968
01969 if (this->current_order.IsType(OT_GOTO_STATION) &&
01970 this->current_order.GetDestination() == this->last_station_visited) {
01971 current_order.MakeLoading(true);
01972 UpdateVehicleTimetable(this, true);
01973
01974
01975
01976
01977
01978
01979 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01980
01981 } else {
01982 current_order.MakeLoading(false);
01983 }
01984
01985 GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
01986
01987 VehiclePayment(this);
01988
01989 InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner);
01990 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01991 InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
01992 InvalidateWindow(WC_STATION_VIEW, this->last_station_visited);
01993
01994 GetStation(this->last_station_visited)->MarkTilesDirty(true);
01995 this->MarkDirty();
01996 }
01997
01998 void Vehicle::LeaveStation()
01999 {
02000 assert(current_order.IsType(OT_LOADING));
02001
02002
02003 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
02004
02005 current_order.MakeLeaveStation();
02006 Station *st = GetStation(this->last_station_visited);
02007 st->loading_vehicles.remove(this);
02008
02009 HideFillingPercent(&this->fill_percent_te_id);
02010
02011 if (this->type == VEH_TRAIN) {
02012
02013 if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
02014
02015
02016
02017
02018 if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(GetVehicleTrackdir(this)), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
02019 TryPathReserve(this, true, true);
02020 }
02021 }
02022 }
02023
02024
02025 void Vehicle::HandleLoading(bool mode)
02026 {
02027 switch (this->current_order.GetType()) {
02028 case OT_LOADING: {
02029 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02030
02031
02032 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
02033 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
02034
02035 this->PlayLeaveStationSound();
02036
02037 bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
02038 this->LeaveStation();
02039
02040
02041 if (!at_destination_station) return;
02042 break;
02043 }
02044
02045 case OT_DUMMY: break;
02046
02047 default: return;
02048 }
02049
02050 this->cur_order_index++;
02051 InvalidateVehicleOrder(this, 0);
02052 }
02053
02054 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02055 {
02056 if (!CheckOwnership(this->owner)) return CMD_ERROR;
02057 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02058 if (this->IsStoppedInDepot()) return CMD_ERROR;
02059
02060 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02061 bool halt_in_depot = this->current_order.GetDepotActionType() & ODATFB_HALT;
02062 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02063
02064
02065
02066 if (flags & DC_EXEC) {
02067 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02068 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02069 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02070 }
02071 return CommandCost();
02072 }
02073
02074 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02075 if (flags & DC_EXEC) {
02076
02077
02078 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->cur_order_index++;
02079
02080 this->current_order.MakeDummy();
02081 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02082 }
02083 return CommandCost();
02084 }
02085
02086 TileIndex location;
02087 DestinationID destination;
02088 bool reverse;
02089 static const StringID no_depot[] = {STR_883A_UNABLE_TO_FIND_ROUTE_TO, STR_9019_UNABLE_TO_FIND_LOCAL_DEPOT, STR_981A_UNABLE_TO_FIND_LOCAL_DEPOT, STR_A012_CAN_T_SEND_AIRCRAFT_TO};
02090 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02091
02092 if (flags & DC_EXEC) {
02093 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02094
02095 this->dest_tile = location;
02096 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02097 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02098 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02099
02100
02101 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02102
02103 if (this->type == VEH_AIRCRAFT && this->u.air.state == FLYING && this->u.air.targetairport != destination) {
02104
02105 extern void AircraftNextAirportPos_and_Order(Vehicle *v);
02106 AircraftNextAirportPos_and_Order(this);
02107 }
02108 }
02109
02110 return CommandCost();
02111
02112 }
02113
02114 void Vehicle::SetNext(Vehicle *next)
02115 {
02116 if (this->next != NULL) {
02117
02118 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02119 v->first = this->next;
02120 }
02121 this->next->previous = NULL;
02122 }
02123
02124 this->next = next;
02125
02126 if (this->next != NULL) {
02127
02128 if (this->next->previous != NULL) this->next->previous->next = NULL;
02129 this->next->previous = this;
02130 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02131 v->first = this->first;
02132 }
02133 }
02134 }
02135
02136 void Vehicle::AddToShared(Vehicle *shared_chain)
02137 {
02138 assert(this->previous_shared == NULL && this->next_shared == NULL);
02139
02140 if (!shared_chain->orders.list) {
02141 assert(shared_chain->previous_shared == NULL);
02142 assert(shared_chain->next_shared == NULL);
02143 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02144 }
02145
02146 this->next_shared = shared_chain->next_shared;
02147 this->previous_shared = shared_chain;
02148
02149 shared_chain->next_shared = this;
02150
02151 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02152
02153 shared_chain->orders.list->AddVehicle(this);
02154 }
02155
02156 void Vehicle::RemoveFromShared()
02157 {
02158
02159
02160 bool were_first = (this->FirstShared() == this);
02161 uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
02162
02163 this->orders.list->RemoveVehicle(this);
02164
02165 if (!were_first) {
02166
02167 this->previous_shared->next_shared = this->NextShared();
02168 }
02169
02170 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02171
02172
02173 if (this->orders.list->GetNumVehicles() == 1) {
02174
02175 DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
02176 InvalidateVehicleOrder(this->FirstShared(), 0);
02177 } else if (were_first) {
02178
02179
02180 InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
02181 }
02182
02183 this->next_shared = NULL;
02184 this->previous_shared = NULL;
02185 }
02186
02187 void StopAllVehicles()
02188 {
02189 Vehicle *v;
02190 FOR_ALL_VEHICLES(v) {
02191
02192
02193 v->vehstatus |= VS_STOPPED;
02194 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
02195 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
02196 }
02197 }
02198
02199 void VehiclesYearlyLoop()
02200 {
02201 Vehicle *v;
02202 FOR_ALL_VEHICLES(v) {
02203 if (v->IsPrimaryVehicle()) {
02204
02205 Money profit = v->GetDisplayProfitThisYear();
02206 if (v->age >= 730 && profit < 0) {
02207 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02208 SetDParam(0, v->index);
02209 SetDParam(1, profit);
02210 AddNewsItem(
02211 STR_VEHICLE_IS_UNPROFITABLE,
02212 NS_ADVICE,
02213 v->index,
02214 0);
02215 }
02216 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02217 }
02218
02219 v->profit_last_year = v->profit_this_year;
02220 v->profit_this_year = 0;
02221 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
02222 }
02223 }
02224 }
02225
02226
02236 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02237 {
02238 assert(IsEngineIndex(engine_type));
02239 const Engine *e = GetEngine(engine_type);
02240
02241 switch (e->type) {
02242 case VEH_TRAIN:
02243 return (st->facilities & FACIL_TRAIN) != 0;
02244
02245 case VEH_ROAD:
02246
02247
02248
02249 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02250
02251 case VEH_SHIP:
02252 return (st->facilities & FACIL_DOCK) != 0;
02253
02254 case VEH_AIRCRAFT:
02255 return (st->facilities & FACIL_AIRPORT) != 0 &&
02256 (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02257
02258 default:
02259 return false;
02260 }
02261 }
02262
02269 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02270 {
02271 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(v) != NULL;
02272
02273 return CanVehicleUseStation(v->engine_type, st);
02274 }